@Override
  protected Tasks<TaskResourceRep> doExecute() throws Exception {
    VolumeGroupUpdateParam input = new VolumeGroupUpdateParam();
    VolumeGroupVolumeList removeVolumesList = new VolumeGroupVolumeList();
    removeVolumesList.setVolumes(volumeIds);
    input.setRemoveVolumesList(removeVolumesList);

    TaskList taskList = getClient().application().updateApplication(applicationId, input);

    return new Tasks<TaskResourceRep>(
        getClient().auth().getClient(), taskList.getTaskList(), TaskResourceRep.class);
  }
  /**
   * 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();
  }
  /**
   * Vcenter Discovery
   *
   * @param vcenter the Vcenter to be discovered. provided, a new taskId is generated.
   * @return the task used to track the discovery job
   */
  protected TaskResourceRep doDiscoverVcenter(Vcenter vcenter) {
    ComputeSystemController controller = getController(ComputeSystemController.class, "vcenter");
    DiscoveredObjectTaskScheduler scheduler =
        new DiscoveredObjectTaskScheduler(_dbClient, new DiscoverJobExec(controller));
    String taskId = UUID.randomUUID().toString();
    ArrayList<AsyncTask> tasks = new ArrayList<AsyncTask>(1);
    tasks.add(new AsyncTask(Vcenter.class, vcenter.getId(), taskId));

    TaskList taskList = scheduler.scheduleAsyncTasks(tasks);

    TaskResourceRep taskResourceRep = taskList.getTaskList().iterator().next();
    updateTaskTenant(taskResourceRep);

    return taskResourceRep;
  }
  @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();
  }
  /** {@inheritDoc} */
  @Override
  public TaskList resynchronizeCopy(Volume sourceVolume, Volume fullCopyVolume) {
    // Create the task list.
    TaskList taskList = new TaskList();

    // Create a unique task id.
    String taskId = UUID.randomUUID().toString();

    // If the source is in a CG, then we will resynchronize the corresponding
    // full copies for all the volumes in the CG. Since we did not allow
    // full copies for volumes or snaps in CGs prior to Jedi, there should
    // be a full copy for all volumes in the CG.
    Map<URI, Volume> fullCopyMap = getFullCopySetMap(sourceVolume, fullCopyVolume);
    Set<URI> fullCopyURIs = fullCopyMap.keySet();

    // Get the storage system for the source volume.
    StorageSystem sourceSystem =
        _dbClient.queryObject(StorageSystem.class, sourceVolume.getStorageController());
    URI sourceSystemURI = sourceSystem.getId();

    // Create the resynchronize task on the full copy volumes.
    for (URI fullCopyURI : fullCopyURIs) {
      Operation op =
          _dbClient.createTaskOpStatus(
              Volume.class,
              fullCopyURI,
              taskId,
              ResourceOperationTypeEnum.RESYNCHRONIZE_VOLUME_FULL_COPY);
      fullCopyMap.get(fullCopyURI).getOpStatus().put(taskId, op);
      TaskResourceRep fullCopyVolumeTask =
          TaskMapper.toTask(fullCopyMap.get(fullCopyURI), taskId, op);
      taskList.getTaskList().add(fullCopyVolumeTask);
    }

    // Invoke the controller.
    try {
      VPlexController controller =
          getController(VPlexController.class, DiscoveredDataObject.Type.vplex.toString());
      controller.resyncFullCopy(sourceSystemURI, new ArrayList<URI>(fullCopyURIs), taskId);
    } catch (InternalException ie) {
      s_logger.error("Controller error", ie);

      // Update the status for the VPLEX volume copies and their
      // corresponding tasks.
      for (Volume vplexFullCopy : fullCopyMap.values()) {
        Operation op = vplexFullCopy.getOpStatus().get(taskId);
        if (op != null) {
          op.error(ie);
          vplexFullCopy.getOpStatus().updateTaskStatus(taskId, op);
          _dbClient.persistObject(vplexFullCopy);
          for (TaskResourceRep task : taskList.getTaskList()) {
            if (task.getResource().getId().equals(vplexFullCopy.getId())) {
              task.setState(op.getStatus());
              task.setMessage(op.getMessage());
              break;
            }
          }
        }
      }
    }
    return taskList;
  }
  /** {@inheritDoc} */
  @Override
  public TaskList detach(BlockObject fcSourceObj, Volume fullCopyVolume) {
    // If full copy volume is already detached or was never
    // activated, return detach action is completed successfully
    // as done in base class. Otherwise, send detach full copy
    // request to controller.
    TaskList taskList = new TaskList();
    String taskId = UUID.randomUUID().toString();
    if ((BlockFullCopyUtils.isFullCopyDetached(fullCopyVolume, _dbClient))
        || (BlockFullCopyUtils.isFullCopyInactive(fullCopyVolume, _dbClient))) {
      super.detach(fcSourceObj, fullCopyVolume);
    } else {
      // You cannot create a full copy of a VPLEX snapshot, so
      // the source will be a volume.
      Volume sourceVolume = (Volume) fcSourceObj;

      // If the source is in a CG, then we will detach the corresponding
      // full copies for all the volumes in the CG. Since we did not allow
      // full copies for volumes or snaps in CGs prior to Jedi, there should
      // be a full copy for all volumes in the CG.
      Map<URI, Volume> fullCopyMap = getFullCopySetMap(sourceVolume, fullCopyVolume);
      Set<URI> fullCopyURIs = fullCopyMap.keySet();

      // Get the storage system for the source volume.
      StorageSystem sourceSystem =
          _dbClient.queryObject(StorageSystem.class, sourceVolume.getStorageController());
      URI sourceSystemURI = sourceSystem.getId();

      // Create the detach task on the full copy volumes.
      for (URI fullCopyURI : fullCopyURIs) {
        Operation op =
            _dbClient.createTaskOpStatus(
                Volume.class,
                fullCopyURI,
                taskId,
                ResourceOperationTypeEnum.DETACH_VOLUME_FULL_COPY);
        fullCopyMap.get(fullCopyURI).getOpStatus().put(taskId, op);
        TaskResourceRep fullCopyVolumeTask =
            TaskMapper.toTask(fullCopyMap.get(fullCopyURI), taskId, op);
        taskList.getTaskList().add(fullCopyVolumeTask);
      }

      // Invoke the controller.
      try {
        VPlexController controller =
            getController(VPlexController.class, DiscoveredDataObject.Type.vplex.toString());
        controller.detachFullCopy(sourceSystemURI, new ArrayList<URI>(fullCopyURIs), taskId);
      } catch (InternalException ie) {
        s_logger.error("Controller error", ie);

        // Update the status for the VPLEX volume copies and their
        // corresponding tasks.
        for (Volume vplexFullCopy : fullCopyMap.values()) {
          Operation op = vplexFullCopy.getOpStatus().get(taskId);
          if (op != null) {
            op.error(ie);
            vplexFullCopy.getOpStatus().updateTaskStatus(taskId, op);
            _dbClient.persistObject(vplexFullCopy);
            for (TaskResourceRep task : taskList.getTaskList()) {
              if (task.getResource().getId().equals(vplexFullCopy.getId())) {
                task.setState(op.getStatus());
                task.setMessage(op.getMessage());
                break;
              }
            }
          }
        }
      }
    }
    return taskList;
  }
  /** {@inheritDoc} */
  @Override
  public TaskList create(
      List<BlockObject> fcSourceObjList,
      VirtualArray varray,
      String name,
      boolean createInactive,
      int count,
      String taskId) {

    // Populate the descriptors list with all volumes required
    // to create the VPLEX volume copies.
    int sourceCounter = 0;
    URI vplexSrcSystemId = null;
    TaskList taskList = new TaskList();
    List<Volume> vplexCopyVolumes = new ArrayList<Volume>();
    List<VolumeDescriptor> volumeDescriptors = new ArrayList<VolumeDescriptor>();
    List<BlockObject> sortedSourceObjectList = sortFullCopySourceList(fcSourceObjList);
    for (BlockObject fcSourceObj : sortedSourceObjectList) {
      URI fcSourceURI = fcSourceObj.getId();
      if (URIUtil.isType(fcSourceURI, BlockSnapshot.class)) {
        // Full copy of snapshots is not supported for VPLEX.
        return super.create(sortedSourceObjectList, varray, name, createInactive, count, taskId);
      }

      Volume vplexSrcVolume = (Volume) fcSourceObj;
      String copyName = name + (sortedSourceObjectList.size() > 1 ? "-" + ++sourceCounter : "");

      // Create a volume descriptor for the source VPLEX volume being copied.
      // and add it to the descriptors list. Be sure to identify this VPLEX
      // volume as the source volume being copied.
      vplexSrcSystemId = fcSourceObj.getStorageController();
      VolumeDescriptor vplexSrcVolumeDescr =
          new VolumeDescriptor(
              VolumeDescriptor.Type.VPLEX_VIRT_VOLUME, vplexSrcSystemId, fcSourceURI, null, null);
      Map<String, Object> descrParams = new HashMap<String, Object>();
      descrParams.put(VolumeDescriptor.PARAM_IS_COPY_SOURCE_ID, Boolean.TRUE);
      vplexSrcVolumeDescr.setParameters(descrParams);
      volumeDescriptors.add(vplexSrcVolumeDescr);

      // Get some info about the VPLEX volume being copied and its storage
      // system.
      Project vplexSrcProject =
          BlockFullCopyUtils.queryFullCopySourceProject(fcSourceObj, _dbClient);
      StorageSystem vplexSrcSystem = _dbClient.queryObject(StorageSystem.class, vplexSrcSystemId);
      Project vplexSystemProject =
          VPlexBlockServiceApiImpl.getVplexProject(vplexSrcSystem, _dbClient, _tenantsService);

      // For the VPLEX volume being copied, determine which of the associated
      // backend volumes is the primary and, for distributed volumes, which
      // is the HA volume. The primary volume will be natively copied and we
      // we need to place and prepare a volume to hold the copy. This copy
      // will be the primary backend volume for the VPLEX volume copy. For
      // a distributed virtual volume, we will need to place and prepare
      // a volume to hold the HA volume of the VPLEX volume copy.
      Volume vplexSrcPrimaryVolume = null;
      Volume vplexSrcHAVolume = null;
      StringSet assocVolumeURIs = vplexSrcVolume.getAssociatedVolumes();
      Iterator<String> assocVolumeURIsIter = assocVolumeURIs.iterator();
      while (assocVolumeURIsIter.hasNext()) {
        URI assocVolumeURI = URI.create(assocVolumeURIsIter.next());
        Volume assocVolume = _dbClient.queryObject(Volume.class, assocVolumeURI);
        if (assocVolume.getVirtualArray().toString().equals(varray.getId().toString())) {
          vplexSrcPrimaryVolume = assocVolume;
        } else {
          vplexSrcHAVolume = assocVolume;
        }
      }

      // Get the capabilities
      VirtualPool vpool = BlockFullCopyUtils.queryFullCopySourceVPool(fcSourceObj, _dbClient);
      VirtualPoolCapabilityValuesWrapper capabilities =
          getCapabilitiesForFullCopyCreate(fcSourceObj, vpool, count);

      // Get the number of copies to create and the size of the volumes.
      // Note that for the size, we must use the actual provisioned size
      // of the source side backend volume. The size passed in the
      // capabilities will be the size of the VPLEX volume. When the
      // source side backend volume for the copy is provisioned, you
      // might not get that actual size. On VMAX, the size will be slightly
      // larger while for VNX the size will be exactly what is requested.
      // So, if the source side is a VMAX, the source side for the copy
      // will be slightly larger than the size in the capabilities. If the HA
      // side is VNX and we use the size in the capabilities, then you will
      // get exactly that size for the HA backend volume. As a result, source
      // side backend volume for the copy will be slightly larger than the
      // HA side. Now the way a VPLEX copy is made is it uses native full
      // copy to create a native full copy of the source side backend
      // volume. It then provisions the HA side volume. The new source side
      // backend copy is then imported into VPLEX in the same way as is done
      // for a vpool change that imports a volume to VPLEX. This code in the
      // VPLEX controller creates a local VPLEX volume using the source side
      // copy and for a distributed volume it then attaches as a remote
      // mirror the HA backend volume that is provisioned. If the HA volume
      // is slightly smaller, then this will fail on the VPLEX. So, we must
      // ensure that HA side volume is big enough by using the provisioned
      // capacity of the source side backend volume of the VPLEX volume being
      // copied.
      long size = vplexSrcPrimaryVolume.getProvisionedCapacity();

      // Place and prepare a volume for each copy to serve as a native
      // copy of a VPLEX backend volume. The VPLEX backend volume that
      // is copied is the backend volume in the same virtual array as the
      // VPLEX volume i.e, the primary backend volume. Create
      // descriptors for these prepared volumes and add them to the list.
      List<Volume> vplexCopyPrimaryVolumes =
          prepareFullCopyPrimaryVolumes(
              copyName, count, vplexSrcPrimaryVolume, capabilities, volumeDescriptors);

      // If the VPLEX volume being copied is distributed, then the VPLEX
      // HA volume should be non-null. We use the VPLEX scheduler to place
      // and then prepare volumes for the HA volumes of the VPLEX volume
      // copies. This should be done in the same manner as is done for the
      // import volume routine. This is because to form the VPLEX volume
      // copy we import the copy of the primary backend volume.
      List<Volume> vplexCopyHAVolumes = new ArrayList<Volume>();
      if (vplexSrcHAVolume != null) {
        vplexCopyHAVolumes.addAll(
            prepareFullCopyHAVolumes(
                copyName,
                count,
                size,
                vplexSrcSystem,
                vplexSystemProject,
                varray,
                vplexSrcHAVolume,
                taskId,
                volumeDescriptors));
      }

      // For each copy to be created, place and prepare a volume for the
      // primary backend volume copy. When copying a distributed VPLEX
      // volume, we also must place and prepare a volume for the HA
      // backend volume copy. Lastly, we must prepare a volume for the
      // VPLEX volume copy. Create descriptors for these prepared volumes
      // and add them to the volume descriptors list.
      for (int i = 0; i < count; i++) {
        // Prepare a new VPLEX volume for each copy.
        Volume vplexCopyPrimaryVolume = vplexCopyPrimaryVolumes.get(i);
        Volume vplexCopyHAVolume = null;
        if (vplexCopyHAVolumes.size() != 0) {
          vplexCopyHAVolume = vplexCopyHAVolumes.get(i);
        }
        Volume vplexCopyVolume =
            prepareFullCopyVPlexVolume(
                copyName,
                count,
                i,
                size,
                vplexSrcVolume,
                vplexSrcProject,
                varray,
                vpool,
                vplexSrcSystemId,
                vplexCopyPrimaryVolume,
                vplexCopyHAVolume,
                taskId,
                volumeDescriptors);
        vplexCopyVolumes.add(vplexCopyVolume);

        // Create task for each copy.
        Operation op = vplexCopyVolume.getOpStatus().get(taskId);
        TaskResourceRep task = toTask(vplexCopyVolume, taskId, op);
        taskList.getTaskList().add(task);
      }
    }

    // Invoke the VPLEX controller to create the copies.
    try {
      s_logger.info("Getting VPlex controller {}.", taskId);
      VPlexController controller =
          getController(VPlexController.class, DiscoveredDataObject.Type.vplex.toString());
      // TBD controller needs to be updated to handle CGs.
      controller.createFullCopy(vplexSrcSystemId, volumeDescriptors, taskId);
      s_logger.info("Successfully invoked controller.");
    } catch (InternalException e) {
      s_logger.error("Controller error", e);

      // Update the status for the VPLEX volume copies and their
      // corresponding tasks.
      for (Volume vplexCopyVolume : vplexCopyVolumes) {
        Operation op = vplexCopyVolume.getOpStatus().get(taskId);
        if (op != null) {
          op.error(e);
          vplexCopyVolume.getOpStatus().updateTaskStatus(taskId, op);
          _dbClient.persistObject(vplexCopyVolume);
          for (TaskResourceRep task : taskList.getTaskList()) {
            if (task.getResource().getId().equals(vplexCopyVolume.getId())) {
              task.setState(op.getStatus());
              task.setMessage(op.getMessage());
              break;
            }
          }
        }
      }

      // Mark all volumes inactive, except for the VPLEX volume
      // we were trying to copy.
      for (VolumeDescriptor descriptor : volumeDescriptors) {
        if (descriptor.getParameters().get(VolumeDescriptor.PARAM_IS_COPY_SOURCE_ID) == null) {
          Volume volume = _dbClient.queryObject(Volume.class, descriptor.getVolumeURI());
          volume.setInactive(true);
          _dbClient.persistObject(volume);
        }
      }
    }

    return taskList;
  }