/**
   * Find an UnManagedVolume for the given WWN by first checking in the UnManagedVolumesToUpdate
   * collection (in case we've already fetched it and updated it elsewhere), and then check the
   * database.
   *
   * @param wwn the WWN to find an UnManagedVolume for
   * @param dbClient a reference to the database client
   * @param cachedStorageNativeIds see comments, cached list of storage native GUIDs
   * @return an UnManagedVolume object for the given WWN
   */
  private UnManagedVolume findUnManagedVolumeForWwn(
      String wwn, DbClient dbClient, List<String> cachedStorageNativeIds) {
    UnManagedVolume unManagedVolume = unManagedVolumesToUpdateByWwn.get(wwn);

    if (null == unManagedVolume) {
      unManagedVolume = DiscoveryUtils.checkUnManagedVolumeExistsInDBByWwn(dbClient, wwn);
    }

    // Special for RP. XIO unmanaged volumes store a WWN in the "wwn" field that will not match
    // the WWN returned by RP, however the proper 128-but WWN is in two places:
    // 1. The volume information "NATIVE_ID" field. (not indexable, so hard to run a query to find)
    // 2. Locked in the native guid of the volume
    // XTREMIO+APM00144755987+UNMANAGEDVOLUME+616a8770e89749a7908d48a3dd9cf0fd
    // The goal of this section of code is to loop through XIO arrays and search for the native guid
    // based on that XIO native guid and wwn to see if we find the unmanaged volume.
    //
    // Someday RP will return the short WWN in the CG information and this inefficient code can be
    // removed.
    if (null == unManagedVolume && cachedStorageNativeIds != null) {
      for (String storageNativeIdPrefix : cachedStorageNativeIds) {
        // Search for the unmanaged volume based on the native GUID
        String searchCriteria = storageNativeIdPrefix + "+UNMANAGEDVOLUME+" + wwn.toLowerCase();
        List<UnManagedVolume> volumes =
            CustomQueryUtility.getUnManagedVolumeByNativeGuid(dbClient, searchCriteria);
        if (volumes != null && !volumes.isEmpty()) {
          log.info("Found XIO unmanaged volume: " + volumes.get(0).getLabel());
          return volumes.get(0);
        }
      }
    }

    return unManagedVolume;
  }
  private List<CifsShareACL> queryDBShareACLs() {

    try {
      ContainmentConstraint containmentConstraint = null;

      if (this.fs != null) {
        _log.info(
            "Querying DB for Share ACLs of share {} of filesystemId {} ",
            this.shareName,
            fs.getId());
        containmentConstraint =
            ContainmentConstraint.Factory.getFileCifsShareAclsConstraint(this.fs.getId());
      } else {
        // Snapshot
        _log.info(
            "Querying DB for Share ACLs of share {} of snapshotId {} ",
            this.shareName,
            this.snapshot.getId());
        containmentConstraint =
            ContainmentConstraint.Factory.getSnapshotCifsShareAclsConstraint(this.snapshot.getId());
      }

      List<CifsShareACL> shareAclList =
          CustomQueryUtility.queryActiveResourcesByConstraint(
              this.dbClient, CifsShareACL.class, containmentConstraint);

      return shareAclList;

    } catch (Exception e) {
      _log.error("Error while querying DB for ACL of a share {}", e);
    }

    return null;
  }
 /**
  * If any endpoint is used in an active File Export, throws an exception
  *
  * @param endpoints endpoints being added
  * @param varrays endpoints belong to
  *     <p>Assumes endpoint formats have been validated.
  */
 public static void checkNotUsedByActiveFileExport(String endpoint, DbClient dbClient) {
   Network network = NetworkUtil.getEndpointNetwork(endpoint, dbClient);
   if (network != null) {
     Set<String> netVArrayIds = network.getConnectedVirtualArrays();
     if ((netVArrayIds != null) && (!netVArrayIds.isEmpty())) {
       Iterator<String> netVArrayIdsIter = netVArrayIds.iterator();
       while (netVArrayIdsIter.hasNext()) {
         String varrayId = netVArrayIdsIter.next();
         List<FileShare> fileShares =
             CustomQueryUtility.queryActiveResourcesByConstraint(
                 dbClient,
                 FileShare.class,
                 AlternateIdConstraint.Factory.getConstraint(FileShare.class, "varray", varrayId));
         for (FileShare fileShare : fileShares) {
           FSExportMap fsExports = fileShare.getFsExports();
           if (fsExports != null) {
             Iterator<FileExport> it = fsExports.values().iterator();
             while (it.hasNext()) {
               FileExport fileExport = it.next();
               if (fileExport.getClients().contains(endpoint)
                   || fileExport.getStoragePort().contains(endpoint)) {
                 throw APIException.badRequests.endpointsCannotBeUpdatedActiveExport(endpoint);
               }
             }
           }
         }
       }
     }
   }
 }
 /**
  * check Storage Volume exists in DB
  *
  * @param dbClient
  * @param nativeGuid
  * @return
  * @throws IOException
  */
 public static Volume checkStorageVolumeExistsInDB(DbClient dbClient, String nativeGuid)
     throws IOException {
   List<Volume> volumes = CustomQueryUtility.getActiveVolumeByNativeGuid(dbClient, nativeGuid);
   Iterator<Volume> volumesItr = volumes.iterator();
   if (volumesItr.hasNext()) {
     return volumesItr.next();
   }
   return null;
 }
 /**
  * check Storage Volume exists in DB
  *
  * @param dbClient
  * @param nativeGuid
  * @return
  * @throws IOException
  */
 public static BlockSnapshot checkBlockSnapshotExistsInDB(DbClient dbClient, String nativeGuid)
     throws IOException {
   List<BlockSnapshot> snapshots =
       CustomQueryUtility.getActiveBlockSnapshotByNativeGuid(dbClient, nativeGuid);
   Iterator<BlockSnapshot> snapshotItr = snapshots.iterator();
   if (snapshotItr.hasNext()) {
     return snapshotItr.next();
   }
   return null;
 }
  /**
   * Cleans up instances of {@link BlockSnapshot} that failed to be created. Also, any stale entries
   * in {@link BlockSnapshotSession#getLinkedTargets()} will be cleaned up.
   *
   * @param volume Volume URI to process.
   * @param itemsToUpdate Items to be updated.
   * @param itemsToDelete Items to be deleted.
   */
  @Override
  public void process(
      URI volume, Collection<DataObject> itemsToUpdate, Collection<DataObject> itemsToDelete) {
    List<BlockSnapshot> snapshots =
        CustomQueryUtility.queryActiveResourcesByConstraint(
            getDbClient(),
            BlockSnapshot.class,
            ContainmentConstraint.Factory.getVolumeSnapshotConstraint(volume));
    List<BlockSnapshot> failedSnapshots = new ArrayList<>();
    List<BlockSnapshotSession> updateSessions = new ArrayList<>();

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

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

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

    itemsToUpdate.addAll(updateSessions);
    itemsToDelete.addAll(failedSnapshots);
  }
  /**
   * Checks if an port in use by an export masks
   *
   * @param portId the port URI being checked
   * @param dbClient
   * @return true if the port in use by export masks
   */
  public static boolean isBlockStoragePortInUse(URI portId, DbClient dbClient) {

    if (portId != null) {
      List<ExportMask> exportMasks =
          CustomQueryUtility.queryActiveResourcesByConstraint(
              dbClient,
              ExportMask.class,
              AlternateIdConstraint.Factory.getConstraint(
                  ExportMask.class, "storagePorts", portId.toString()));
      return (exportMasks != null && !exportMasks.isEmpty());
    }
    return false;
  }
  /**
   * 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();
  }
  /**
   * Checks if an initiator in use by an export groups
   *
   * @param initId the initiator URI being checked
   * @param dbClient
   * @return true if the initiator in use by export groups
   */
  public static boolean isInitiatorInUse(URI initId, DbClient dbClient) {

    if (initId != null) {

      List<ExportGroup> exportGroups =
          CustomQueryUtility.queryActiveResourcesByConstraint(
              dbClient,
              ExportGroup.class,
              AlternateIdConstraint.Factory.getConstraint(
                  ExportGroup.class, "initiators", initId.toString()));
      return (exportGroups != null && !exportGroups.isEmpty());
    }
    return false;
  }
  private List<QuotaDirectory> queryDBQuotaDirectories(FileShare fs) {
    _log.info("Querying all quota directories Using FsId {}", fs.getId());
    try {
      ContainmentConstraint containmentConstraint =
          ContainmentConstraint.Factory.getQuotaDirectoryConstraint(fs.getId());
      List<QuotaDirectory> fsQuotaDirs =
          CustomQueryUtility.queryActiveResourcesByConstraint(
              _dbClient, QuotaDirectory.class, containmentConstraint);
      return fsQuotaDirs;
    } catch (Exception e) {
      _log.error("Error while querying {}", e);
    }

    return null;
  }
  /**
   * get the volume to be updated after application add and remove operations could be the volume
   * passed in if it's a simple block volume or the vplex virtual volume if it's a backing volume
   *
   * @param voluri uri of volume operated on during add or remove volume from application operation
   * @param dbClient
   * @return the volume to update
   */
  private Volume getVolume(URI voluri, DbClient dbClient) {
    // if this is a vplex volume, update the parent virtual volume
    List<Volume> vplexVolumes =
        CustomQueryUtility.queryActiveResourcesByConstraint(
            dbClient, Volume.class, getVolumesByAssociatedId(voluri.toString()));

    Volume volume = null;

    for (Volume vplexVolume : vplexVolumes) {
      URI storageURI = vplexVolume.getStorageController();
      StorageSystem storage = dbClient.queryObject(StorageSystem.class, storageURI);
      if (DiscoveredDataObject.Type.vplex.name().equals(storage.getSystemType())) {
        volume = vplexVolume;
      }
    }

    if (volume == null) {
      volume = dbClient.queryObject(Volume.class, voluri);
    }
    return volume;
  }
  /**
   * Update the Storage Provider. This is useful when we move arrays to some other provider.
   *
   * @param id the URN of a ViPR Storage Provider
   * @brief Update Storage provider
   * @return Updated Storage Provider information.
   */
  @PUT
  @Produces({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON})
  @Consumes({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON})
  @Path("/{id}")
  @CheckPermission(roles = {Role.SYSTEM_ADMIN, Role.RESTRICTED_SYSTEM_ADMIN})
  public StorageProviderRestRep updateStorageProvider(
      @PathParam("id") URI id, StorageProviderUpdateParam param) {
    StorageProvider storageProvider = _dbClient.queryObject(StorageProvider.class, id);
    if (null == storageProvider || storageProvider.getInactive()) {
      throw APIException.notFound.unableToFindEntityInURL(id);
    } else {
      /* Usecase is not to remove the provider instead we can update the old storage provider with
      new provider details. */
      if (param.getName() != null
          && !param.getName().equals("")
          && !param.getName().equalsIgnoreCase(storageProvider.getLabel())) {
        checkForDuplicateName(param.getName(), StorageProvider.class);
        storageProvider.setLabel(param.getName());
      }

      // if the ip or port passed are different from the existing one
      // check to ensure a provider does not exist with the new ip + port combo
      String existingIPAddress = storageProvider.getIPAddress();
      Integer existingPortNumber = storageProvider.getPortNumber();
      if ((param.getIpAddress() != null && !param.getIpAddress().equals(existingIPAddress))
          || (param.getPortNumber() != null && !param.getPortNumber().equals(existingPortNumber))) {
        String ipAddress =
            (param.getIpAddress() != null) ? param.getIpAddress() : existingIPAddress;
        Integer portNumber =
            (param.getPortNumber() != null) ? param.getPortNumber() : existingPortNumber;
        ArgValidator.checkFieldRange(portNumber, 1, 65535, "port_number");

        String providerKey = ipAddress + "-" + portNumber;
        List<StorageProvider> providers =
            CustomQueryUtility.getActiveStorageProvidersByProviderId(_dbClient, providerKey);
        if (providers != null && !providers.isEmpty()) {
          throw APIException.badRequests.invalidParameterStorageProviderAlreadyRegistered(
              providerKey);
        }

        // User can update IP address for an existing Provider object
        // if and only if the connection with old IP is not alive.
        if (!existingIPAddress.equals(param.getIpAddress())
            && isOldConnectionAlive(
                existingIPAddress, existingPortNumber, storageProvider.getInterfaceType())
            && (storageProvider.getStorageSystems() != null
                && !storageProvider.getStorageSystems().isEmpty())) {
          throw APIException.badRequests.cannotUpdateProviderIP(
              existingIPAddress + "-" + existingPortNumber);
        }

        storageProvider.setIPAddress(ipAddress);
        storageProvider.setPortNumber(portNumber);
      }
      if (param.getUserName() != null && StringUtils.isNotBlank(param.getUserName())) {
        storageProvider.setUserName(param.getUserName());
      }
      if (param.getPassword() != null && StringUtils.isNotBlank(param.getPassword())) {
        storageProvider.setPassword(param.getPassword());
      }
      if (param.getUseSSL() != null) {
        storageProvider.setUseSSL(param.getUseSSL());
      }

      if (param.getInterfaceType() != null) {
        ArgValidator.checkFieldValueFromEnum(
            param.getInterfaceType(),
            "interface_type",
            EnumSet.of(
                StorageProvider.InterfaceType.hicommand,
                StorageProvider.InterfaceType.smis,
                StorageProvider.InterfaceType.scaleio,
                StorageProvider.InterfaceType.ibmxiv));
        storageProvider.setInterfaceType(param.getInterfaceType());
      }

      if (param.getSecondaryUsername() != null) {
        storageProvider.setSecondaryUsername(param.getSecondaryUsername());
      }
      if (param.getSecondaryPassword() != null) {
        storageProvider.setSecondaryPassword(param.getSecondaryPassword());
      }
      if (param.getElementManagerURL() != null) {
        storageProvider.setElementManagerURL(param.getElementManagerURL());
      }

      _dbClient.persistObject(storageProvider);
    }

    auditOp(
        OperationTypeEnum.UPDATE_STORAGEPROVIDER,
        true,
        null,
        storageProvider.getId().toString(),
        storageProvider.getLabel(),
        storageProvider.getIPAddress(),
        storageProvider.getPortNumber(),
        storageProvider.getUserName(),
        storageProvider.getInterfaceType());

    return map(storageProvider);
  }
  @POST
  @Consumes({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON})
  @Produces({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON})
  @CheckPermission(roles = {Role.SYSTEM_ADMIN, Role.RESTRICTED_SYSTEM_ADMIN})
  public TaskResourceRep registerStorageProvider(StorageProviderCreateParam param)
      throws ControllerException {
    ArgValidator.checkFieldNotEmpty(param.getName(), "name");
    checkForDuplicateName(param.getName(), StorageProvider.class);

    ArgValidator.checkFieldNotEmpty(param.getIpAddress(), "ip_address");
    ArgValidator.checkFieldNotNull(param.getPortNumber(), "port_number");
    ArgValidator.checkFieldNotEmpty(param.getUserName(), "user_name");
    ArgValidator.checkFieldNotEmpty(param.getPassword(), "password");
    ArgValidator.checkFieldRange(param.getPortNumber(), 1, 65535, "port_number");
    ArgValidator.checkFieldValueFromEnum(
        param.getInterfaceType(), "interface_type", StorageProvider.InterfaceType.class);
    String providerKey = param.getIpAddress() + "-" + param.getPortNumber();
    List<StorageProvider> providers =
        CustomQueryUtility.getActiveStorageProvidersByProviderId(_dbClient, providerKey);
    if (providers != null && !providers.isEmpty()) {
      throw APIException.badRequests.invalidParameterStorageProviderAlreadyRegistered(providerKey);
    }

    // Set the SSL parameter
    Boolean useSSL = param.getUseSSL();
    if (useSSL == null) {
      useSSL = StorageProviderCreateParam.USE_SSL_DEFAULT;
    }

    StorageProvider provider = new StorageProvider();
    provider.setId(URIUtil.createId(StorageProvider.class));
    provider.setLabel(param.getName());
    provider.setIPAddress(param.getIpAddress());
    provider.setPortNumber(param.getPortNumber());
    provider.setUserName(param.getUserName());
    provider.setPassword(param.getPassword());
    provider.setUseSSL(useSSL);
    provider.setInterfaceType(param.getInterfaceType());
    provider.setRegistrationStatus(RegistrationStatus.REGISTERED.toString());
    provider.setConnectionStatus(ConnectionStatus.INITIALIZING.name());
    provider.setSecondaryUsername(param.getSecondaryUsername());
    provider.setSecondaryPassword(param.getSecondaryPassword());
    provider.setElementManagerURL(param.getElementManagerURL());
    if (param.getSioCLI() != null) {
      // TODO: Validate the input?
      provider.addKey(StorageProvider.GlobalKeys.SIO_CLI.name(), param.getSioCLI());
    }

    if (StorageProvider.InterfaceType.ibmxiv.name().equalsIgnoreCase(provider.getInterfaceType())) {
      provider.setManufacturer("IBM");
    }

    _dbClient.createObject(provider);

    auditOp(
        OperationTypeEnum.REGISTER_STORAGEPROVIDER,
        true,
        null,
        provider.getLabel(),
        provider.getId().toString(),
        provider.getIPAddress(),
        provider.getPortNumber(),
        provider.getUserName(),
        provider.getInterfaceType());

    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());
    log.debug("controller.getClass().getName() :{}", controller.getClass().getName());
    log.debug("controller.getClass().getSimpleName() :{}", controller.getClass().getSimpleName());
    /**
     * Creates MonitoringJob token for vnxblock/vmax, hds, cinder and IBM XIV device on zooKeeper
     * queue
     */
    // TODO : If all interface types have monitoring impl class added (scaleIO is missing),
    // this check can be removed.
    String interfaceType = provider.getInterfaceType();
    if (StorageProvider.InterfaceType.hicommand.name().equalsIgnoreCase(interfaceType)
        || StorageProvider.InterfaceType.smis.name().equalsIgnoreCase(interfaceType)
        || StorageProvider.InterfaceType.cinder.name().equalsIgnoreCase(interfaceType)
        || StorageProvider.InterfaceType.ibmxiv.name().equalsIgnoreCase(interfaceType)) {
      controller.startMonitoring(
          new AsyncTask(StorageProvider.class, provider.getId(), taskId),
          getSystemTypeByInterface(interfaceType));
    }

    DiscoveredObjectTaskScheduler scheduler =
        new DiscoveredObjectTaskScheduler(_dbClient, new ScanJobExec(controller));
    TaskList taskList = scheduler.scheduleAsyncTasks(tasks);
    return taskList.getTaskList().listIterator().next();
  }