/* * Copyright (c) 2013 EMC Corporation * All Rights Reserved */ package com.emc.storageos.volumecontroller.impl.block.taskcompleter; import com.emc.storageos.db.client.DbClient; import com.emc.storageos.db.client.model.BlockObject; import com.emc.storageos.db.client.model.ExportGroup; import com.emc.storageos.db.client.model.ExportMask; import com.emc.storageos.db.client.model.Operation; import com.emc.storageos.exceptions.DeviceControllerException; import com.emc.storageos.svcs.errorhandling.model.ServiceCoded; import com.emc.storageos.util.ExportUtils; import com.emc.storageos.volumecontroller.impl.utils.ExportMaskUtils; import com.emc.storageos.volumecontroller.impl.utils.ExportOperationContext; import com.emc.storageos.workflow.WorkflowService; import org.slf4j.LoggerFactory; import java.net.URI; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import static com.emc.storageos.volumecontroller.impl.smis.vmax.VmaxExportOperationContext.OPERATION_ADD_VOLUMES_TO_STORAGE_GROUP; @SuppressWarnings("serial") public class ExportMaskAddVolumeCompleter extends ExportTaskCompleter { private static final org.slf4j.Logger _log = LoggerFactory.getLogger(ExportMaskAddVolumeCompleter.class); private final List<URI> _volumes; private final Map<URI, Integer> _volumeMap; private final String _forgetStepId; public ExportMaskAddVolumeCompleter(URI egUri, URI emUri, Map<URI, Integer> volumes, String task, String forgetStepId) { super(ExportGroup.class, egUri, emUri, task); _volumes = new ArrayList<>(); _volumes.addAll(volumes.keySet()); _volumeMap = new HashMap<>(); _volumeMap.putAll(volumes); _forgetStepId = forgetStepId; } public ExportMaskAddVolumeCompleter(URI egUri, URI emUri, Map<URI, Integer> volumes, String task) { super(ExportGroup.class, egUri, emUri, task); _volumes = new ArrayList<>(); _volumes.addAll(volumes.keySet()); _volumeMap = new HashMap<>(); _volumeMap.putAll(volumes); _forgetStepId = null; } @Override protected void complete(DbClient dbClient, Operation.Status status, ServiceCoded coded) throws DeviceControllerException { try { ExportGroup exportGroup = dbClient.queryObject(ExportGroup.class, getId()); ExportMask exportMask = (getMask() != null) ? dbClient.queryObject(ExportMask.class, getMask()) : null; if (exportMask == null) { _log.warn("Export mask was null for task {}", getOpId()); return; } if (shouldUpdateDatabase(status)) { for (URI volumeURI : _volumes) { BlockObject volume = BlockObject.fetch(dbClient, volumeURI); _log.info(String.format("Done ExportMaskAddVolume - Id: %s, OpId: %s, status: %s", getId().toString(), getOpId(), status.name())); exportMask.removeFromExistingVolumes(volume); exportMask.addToUserCreatedVolumes(volume); } exportMask.setCreatedBySystem(true); ExportMaskUtils.setExportMaskResource(dbClient, exportGroup, exportMask); exportMask.addVolumes(_volumeMap); exportGroup.addExportMask(exportMask.getId()); ExportUtils.reconcileHLUs(dbClient, exportGroup, exportMask, _volumeMap); dbClient.updateObject(exportMask); dbClient.updateObject(exportGroup); // In the case of VPLEX backend volumes being successfully masked to the VPLEX, // we store these volumes in the step data to know which volumes need to be forgotten // on rollback if subsequent steps in the workflow fail. if (_forgetStepId != null) { @SuppressWarnings("unchecked") Set<URI> maskedVolumeURIs = (Set<URI>) WorkflowService.getInstance().loadWorkflowData(_forgetStepId, "forget"); if (maskedVolumeURIs == null) { maskedVolumeURIs = new HashSet<>(); maskedVolumeURIs.addAll(_volumes); } else { maskedVolumeURIs.addAll(_volumes); } WorkflowService.getInstance().storeWorkflowData(_forgetStepId, "forget", maskedVolumeURIs); } } } catch (Exception e) { _log.error(String.format("Failed updating status for ExportMaskAddVolume - Id: %s, OpId: %s", getId().toString(), getOpId()), e); } finally { super.complete(dbClient, status, coded); } } /** * This completer may complete with a ready or error status. In the case of an error status, * we can check the {@link ExportOperationContext} to see if the volumes were added and perform * the necessary database updates. * * @param status Status of the operation. * @return true, if the status is ready or the volumes were added despite error status. */ private boolean shouldUpdateDatabase(Operation.Status status) { return status == Operation.Status.ready || wereVolumesAdded(); } private boolean wereVolumesAdded() { Object context = WorkflowService.getInstance().loadStepData(getOpId()); if (context != null && context instanceof ExportOperationContext) { List<ExportOperationContext.ExportOperationContextOperation> operations = ((ExportOperationContext)context).getOperations(); if (operations != null) { for (ExportOperationContext.ExportOperationContextOperation operation : operations) { // VMAX check if (OPERATION_ADD_VOLUMES_TO_STORAGE_GROUP.equalsIgnoreCase(operation.getOperation())) { // TODO Check arguments. return true; } } } } return false; } }