/**
   * Verify the firmware version for the NetworkSystem
   *
   * @param uri - Device URI
   * @throws ControllerException thrown if firmware version is not supported
   */
  public void verifyVersion(URI uri) throws ControllerException {
    // Retrieve the storage device info from the database.
    NetworkSystem networkDev = getDeviceObject(uri);
    NetworkSystemDevice networkDevice = getDevice();
    if (networkDevice == null) {
      throw NetworkDeviceControllerException.exceptions.verifyVersionFailedNull(uri.toString());
    }
    String version = null;
    try {
      version = networkDevice.getVersion(networkDev);
      networkDev.setVersion(version);
      String minimumSupportedVersion =
          VersionChecker.getMinimumSupportedVersion(Type.valueOf(networkDev.getSystemType()));
      _log.info(
          "Verifying version details : Minimum Supported Version {} - Discovered Firmware Version {}",
          minimumSupportedVersion,
          version);

      if (VersionChecker.verifyVersionDetails(minimumSupportedVersion, version) < 0) {
        networkDev.setCompatibilityStatus(
            DiscoveredDataObject.CompatibilityStatus.INCOMPATIBLE.name());
        throw NetworkDeviceControllerException.exceptions.versionNotSupported(
            version, minimumSupportedVersion);
      } else {
        networkDev.setCompatibilityStatus(
            DiscoveredDataObject.CompatibilityStatus.COMPATIBLE.name());
      }
    } catch (Exception ex) {
      Date date = new Date();
      networkDev.setLastDiscoveryStatusMessage(ex.getMessage());
      throw NetworkDeviceControllerException.exceptions.verifyVersionFailed(
          uri.toString(), date.toString(), ex);
    } finally {
      if (networkDev != null) {
        try {
          dbClient.persistObject(networkDev);
        } catch (DatabaseException ex) {
          _log.error("Error while persisting object to DB", ex);
        }
      }
    }
  }
 private void setCompatibilityByACLXFlag(
     StorageSystem storageSystem, CIMInstance portInstance, StoragePort port) {
   Object portAttributesValue = portInstance.getPropertyValue(EMC_PORT_ATTRIBUTES);
   if (portAttributesValue != null && storageSystem.checkIfVmax3()) {
     boolean foundACLXFlag = false;
     UnsignedInteger16[] portAttributes = (UnsignedInteger16[]) portAttributesValue;
     for (UnsignedInteger16 portAttribute : portAttributes) {
       if (portAttribute.equals(EMC_PORT_ATTRIBUTE_ACLX_FLAG)) {
         foundACLXFlag = true;
         break;
       }
     }
     String compatibilityStatus =
         (foundACLXFlag)
             ? DiscoveredDataObject.CompatibilityStatus.COMPATIBLE.name()
             : DiscoveredDataObject.CompatibilityStatus.INCOMPATIBLE.name();
     _logger.info(
         String.format(
             "setCompatibilityByACLXFlag(%s) = %s", port.getNativeGuid(), compatibilityStatus));
     port.setCompatibilityStatus(compatibilityStatus);
   }
 }
  /**
   * 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;
  }
  /**
   * 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);
      }
    }
  }