@Override
  public void exportGroupAddVolumes(
      URI storageURI, URI exportGroupURI, Map<URI, Integer> volumeMap, String token)
      throws Exception {
    ExportOrchestrationTask taskCompleter = null;
    try {
      BlockStorageDevice device = getDevice();
      taskCompleter = new ExportOrchestrationTask(exportGroupURI, token);
      StorageSystem storage = _dbClient.queryObject(StorageSystem.class, storageURI);
      ExportGroup exportGroup = _dbClient.queryObject(ExportGroup.class, exportGroupURI);
      boolean anyVolumesAdded = false;
      boolean createdNewMask = false;
      if (exportGroup.getExportMasks() != null) {
        // Set up workflow steps.
        Workflow workflow =
            _workflowService.getNewWorkflow(
                MaskingWorkflowEntryPoints.getInstance(), "exportGroupAddVolumes", true, token);
        List<ExportMask> exportMasksToZoneAddVolumes = new ArrayList<ExportMask>();
        List<URI> volumesToZoneAddVolumes = new ArrayList<URI>();
        List<URI> exportMasksToZoneCreate = new ArrayList<URI>();
        Map<URI, Integer> volumesToZoneCreate = new HashMap<URI, Integer>();
        // Add the volume to all the ExportMasks that are contained in the
        // ExportGroup. The volumes should be added only if they don't
        // already exist for the ExportMask.
        Collection<URI> initiatorURIs =
            Collections2.transform(
                exportGroup.getInitiators(), CommonTransformerFunctions.FCTN_STRING_TO_URI);
        List<URI> hostURIs = new ArrayList<URI>();
        Map<String, URI> portNameToInitiatorURI = new HashMap<String, URI>();
        List<String> portNames = new ArrayList<String>();
        processInitiators(exportGroup, initiatorURIs, portNames, portNameToInitiatorURI, hostURIs);
        // We always want to have the full list of initiators for the hosts involved in
        // this export. This will allow the export operation to always find any
        // existing exports for a given host.
        queryHostInitiatorsAndAddToList(portNames, portNameToInitiatorURI, initiatorURIs, hostURIs);
        Map<String, Set<URI>> foundMatches = device.findExportMasks(storage, portNames, false);
        Set<String> checkMasks = mergeWithExportGroupMaskURIs(exportGroup, foundMatches.values());
        for (String maskURIStr : checkMasks) {
          ExportMask exportMask = _dbClient.queryObject(ExportMask.class, URI.create(maskURIStr));
          _log.info(String.format("Checking mask %s", exportMask.getMaskName()));
          if (!exportMask.getInactive() && exportMask.getStorageDevice().equals(storageURI)) {
            exportMask = device.refreshExportMask(storage, exportMask);
            // BlockStorageDevice level, so that it has up-to-date
            // info from the array
            Map<URI, Integer> volumesToAdd =
                getVolumesToAdd(volumeMap, exportMask, exportGroup, token);
            // Not able to get VolumesToAdd due to error condition so, return
            if (null == volumesToAdd) {
              return;
            }
            _log.info(
                String.format(
                    "Mask %s, adding volumes %s",
                    exportMask.getMaskName(), Joiner.on(',').join(volumesToAdd.entrySet())));
            if (volumesToAdd.size() > 0) {
              exportMasksToZoneAddVolumes.add(exportMask);
              volumesToZoneAddVolumes.addAll(volumesToAdd.keySet());

              // Make sure the zoning map is getting updated for user-created masks
              updateZoningMap(exportGroup, exportMask, true);
              generateExportMaskAddVolumesWorkflow(
                  workflow,
                  EXPORT_GROUP_ZONING_TASK,
                  storage,
                  exportGroup,
                  exportMask,
                  volumesToAdd);
              anyVolumesAdded = true;
              // Need to check if the mask is not already associated with
              // ExportGroup. This is case when we are adding volume to
              // the export and there is an existing export on the array.
              // We have to reuse that existing export, but we need also
              // associated it with the ExportGroup.
              if (!exportGroup.hasMask(exportMask.getId())) {
                exportGroup.addExportMask(exportMask.getId());
                _dbClient.updateAndReindexObject(exportGroup);
              }
            }
          }
        }
        if (!anyVolumesAdded) {
          // This is the case where we were requested to add volumes to the
          // export for this storage array, but none were scheduled to be
          // added. This could be either because there are existing masks,
          // but the volumes are already in the export mask or there are no
          // masks for the storage array. We are checking if there are any
          // masks and if there are initiators for the export.
          if (!ExportMaskUtils.hasExportMaskForStorage(_dbClient, exportGroup, storageURI)
              && exportGroup.hasInitiators()) {
            _log.info(
                "No existing masks to which the requested volumes can be added. Creating a new mask");
            List<URI> initiators = StringSetUtil.stringSetToUriList(exportGroup.getInitiators());

            Map<String, List<URI>> hostInitiatorMap =
                mapInitiatorsToComputeResource(exportGroup, initiators);

            if (!hostInitiatorMap.isEmpty()) {
              for (Map.Entry<String, List<URI>> resourceEntry : hostInitiatorMap.entrySet()) {
                String computeKey = resourceEntry.getKey();
                List<URI> computeInitiatorURIs = resourceEntry.getValue();
                _log.info(String.format("New export masks for %s", computeKey));
                GenExportMaskCreateWorkflowResult result =
                    generateExportMaskCreateWorkflow(
                        workflow,
                        EXPORT_GROUP_ZONING_TASK,
                        storage,
                        exportGroup,
                        computeInitiatorURIs,
                        volumeMap,
                        token);
                exportMasksToZoneCreate.add(result.getMaskURI());
                volumesToZoneCreate.putAll(volumeMap);
              }
              createdNewMask = true;
            }
          }
        }

        if (!exportMasksToZoneAddVolumes.isEmpty()) {
          generateZoningAddVolumesWorkflow(
              workflow, null, exportGroup, exportMasksToZoneAddVolumes, volumesToZoneAddVolumes);
        }

        if (!exportMasksToZoneCreate.isEmpty()) {
          generateZoningCreateWorkflow(
              workflow, null, exportGroup, exportMasksToZoneCreate, volumesToZoneCreate);
        }

        String successMessage =
            String.format(
                "Successfully added volumes to export on StorageArray %s", storage.getLabel());
        workflow.executePlan(taskCompleter, successMessage);
      } else {
        if (exportGroup.hasInitiators()) {
          _log.info("There are no masks for this export. Need to create anew.");
          List<URI> initiatorURIs = new ArrayList<URI>();
          for (String initiatorURIStr : exportGroup.getInitiators()) {
            initiatorURIs.add(URI.create(initiatorURIStr));
          }
          // Invoke the export group create operation,
          // which should in turn create a workflow operations to
          // create the export for the newly added volume(s).
          exportGroupCreate(storageURI, exportGroupURI, initiatorURIs, volumeMap, token);
          anyVolumesAdded = true;
        } else {
          _log.warn("There are no initiator for export group: " + exportGroup.getLabel());
        }
      }
      if (!anyVolumesAdded && !createdNewMask) {
        taskCompleter.ready(_dbClient);
        _log.info(
            "No volumes pushed to array because either they already exist "
                + "or there were no initiators added to the export yet.");
      }
    } catch (Exception ex) {
      _log.error("ExportGroup Orchestration failed.", ex);
      // TODO add service code here
      if (taskCompleter != null) {
        ServiceError serviceError =
            DeviceControllerException.errors.jobFailedMsg(ex.getMessage(), ex);
        taskCompleter.error(_dbClient, serviceError);
      }
    }
  }