/**
   * Called to update the job status when the volume expand job completes.
   *
   * @param jobContext The job context.
   */
  public void updateStatus(JobContext jobContext) throws Exception {
    CloseableIterator<CIMObjectPath> associatorIterator = null;
    CloseableIterator<CIMInstance> instanceIterator = null;
    JobStatus jobStatus = getJobStatus();

    try {
      if (jobStatus == JobStatus.IN_PROGRESS) {
        return;
      }

      DbClient dbClient = jobContext.getDbClient();
      CIMConnectionFactory cimConnectionFactory = jobContext.getCimConnectionFactory();
      WBEMClient client = getWBEMClient(dbClient, cimConnectionFactory);

      // If terminal state update storage pool capacity and remove reservation for volume capacity
      // from pool's reserved capacity map.
      if (jobStatus == JobStatus.SUCCESS
          || jobStatus == JobStatus.FAILED
          || jobStatus == JobStatus.FATAL_ERROR) {
        SmisUtils.updateStoragePoolCapacity(dbClient, client, _storagePoolURI);

        StoragePool pool = dbClient.queryObject(StoragePool.class, _storagePoolURI);
        StringMap reservationMap = pool.getReservedCapacityMap();
        URI volumeId = getTaskCompleter().getId();
        // remove from reservation map
        reservationMap.remove(volumeId.toString());
        dbClient.persistObject(pool);
      }

      String opId = getTaskCompleter().getOpId();
      StringBuilder logMsgBuilder =
          new StringBuilder(
              String.format(
                  "Updating status of job %s to %s, task: %s",
                  this.getJobName(), jobStatus.name(), opId));

      if (jobStatus == JobStatus.SUCCESS) {
        VolumeExpandCompleter taskCompleter = (VolumeExpandCompleter) getTaskCompleter();
        Volume volume = dbClient.queryObject(Volume.class, taskCompleter.getId());
        // set requested capacity
        volume.setCapacity(taskCompleter.getSize());
        // set meta related properties
        volume.setTotalMetaMemberCapacity(taskCompleter.getTotalMetaMembersSize());
        volume.setMetaMemberCount(taskCompleter.getMetaMemberCount());
        volume.setMetaMemberSize(taskCompleter.getMetaMemberSize());
        volume.setIsComposite(taskCompleter.isComposite());
        volume.setCompositionType(taskCompleter.getMetaVolumeType());

        // set provisioned capacity
        associatorIterator =
            client.associatorNames(getCimJob(), null, SmisConstants.CIM_STORAGE_VOLUME, null, null);
        if (associatorIterator.hasNext()) {
          CIMObjectPath volumePath = associatorIterator.next();
          CIMInstance volumeInstance = client.getInstance(volumePath, true, false, null);
          if (volumeInstance != null) {
            CIMProperty consumableBlocks =
                volumeInstance.getProperty(SmisConstants.CP_CONSUMABLE_BLOCKS);
            CIMProperty blockSize = volumeInstance.getProperty(SmisConstants.CP_BLOCK_SIZE);
            // calculate provisionedCapacity = consumableBlocks * block size
            Long provisionedCapacity =
                Long.valueOf(consumableBlocks.getValue().toString())
                    * Long.valueOf(blockSize.getValue().toString());
            volume.setProvisionedCapacity(provisionedCapacity);
          }

          // set allocated capacity
          instanceIterator =
              client.referenceInstances(
                  volumePath,
                  SmisConstants.CIM_ALLOCATED_FROM_STORAGEPOOL,
                  null,
                  false,
                  SmisConstants.PS_SPACE_CONSUMED);
          if (instanceIterator.hasNext()) {
            CIMInstance allocatedFromStoragePoolPath = instanceIterator.next();
            CIMProperty spaceConsumed =
                allocatedFromStoragePoolPath.getProperty(SmisConstants.CP_SPACE_CONSUMED);
            if (null != spaceConsumed) {
              volume.setAllocatedCapacity(Long.valueOf(spaceConsumed.getValue().toString()));
            }
          }
        }
        logMsgBuilder.append(
            String.format(
                "%n   Capacity: %s, Provisioned capacity: %s, Allocated Capacity: %s",
                volume.getCapacity(),
                volume.getProvisionedCapacity(),
                volume.getAllocatedCapacity()));
        if (volume.getIsComposite()) {
          logMsgBuilder.append(
              String.format(
                  "%n   Is Meta: %s, Total meta member capacity: %s, Meta member count %s, Meta member size: %s",
                  volume.getIsComposite(),
                  volume.getTotalMetaMemberCapacity(),
                  volume.getMetaMemberCount(),
                  volume.getMetaMemberSize()));
        }

        _log.info(logMsgBuilder.toString());

        // Reset list of meta member volumes in the volume
        if (volume.getMetaVolumeMembers() != null) {
          volume.getMetaVolumeMembers().clear();
        }

        StorageSystem storageSystem =
            dbClient.queryObject(StorageSystem.class, volume.getStorageController());
        // set the RP tag on the volume if the volume is RP protected
        if (volume.checkForRp()
            && storageSystem.getSystemType() != null
            && storageSystem
                .getSystemType()
                .equalsIgnoreCase(DiscoveredDataObject.Type.vmax.toString())) {
          SmisCommandHelper helper = jobContext.getSmisCommandHelper();
          List<CIMObjectPath> volumePathList = new ArrayList<CIMObjectPath>();
          volumePathList.add(helper.getVolumeMember(storageSystem, volume));
          helper.setRecoverPointTag(storageSystem, volumePathList, true);
        }

        dbClient.persistObject(volume);
        // Reset list of meta members native ids in WF data (when meta is created meta members are
        // removed from array)
        WorkflowService.getInstance().storeStepData(opId, new ArrayList<String>());
      }
    } catch (Exception e) {
      _log.error("Caught an exception while trying to updateStatus for SmisVolumeExpandJob", e);
      setPostProcessingErrorStatus(
          "Encountered an internal error during volume expand job status processing : "
              + e.getMessage());
    } finally {
      _metaVolumeTaskCompleter.setLastStepStatus(jobStatus);
      if (associatorIterator != null) {
        associatorIterator.close();
      }
      if (instanceIterator != null) {
        instanceIterator.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);
      }
    }
  }