/*
* Copyright (c) 2013 EMC Corporation
* All Rights Reserved
*/
package com.emc.storageos.volumecontroller.impl.block.taskcompleter;
import static com.emc.storageos.volumecontroller.impl.smis.vmax.VmaxExportOperationContext.OPERATION_ADD_EXISTING_INITIATOR_TO_EXPORT_GROUP;
import static com.emc.storageos.volumecontroller.impl.smis.vmax.VmaxExportOperationContext.OPERATION_ADD_INITIATORS_TO_INITIATOR_GROUP;
import static com.emc.storageos.volumecontroller.impl.smis.vmax.VmaxExportOperationContext.OPERATION_ADD_PORTS_TO_PORT_GROUP;
import java.net.URI;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import org.slf4j.LoggerFactory;
import com.emc.storageos.db.client.DbClient;
import com.emc.storageos.db.client.URIUtil;
import com.emc.storageos.db.client.model.ExportGroup;
import com.emc.storageos.db.client.model.ExportMask;
import com.emc.storageos.db.client.model.Initiator;
import com.emc.storageos.db.client.model.Operation;
import com.emc.storageos.db.client.model.StoragePort;
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.ExportOperationContext;
import com.emc.storageos.volumecontroller.impl.utils.ExportOperationContext.ExportOperationContextOperation;
import com.emc.storageos.workflow.WorkflowService;
public class ExportMaskAddInitiatorCompleter extends ExportMaskInitiatorCompleter {
private static final org.slf4j.Logger _log = LoggerFactory
.getLogger(ExportMaskAddInitiatorCompleter.class);
private List<URI> _initiatorURIs;
private List<URI> _targetURIs;
public ExportMaskAddInitiatorCompleter(URI egUri, URI emUri, List<URI> initiatorURIs,
List<URI> targetURIs, String task) {
super(ExportGroup.class, egUri, emUri, task);
_initiatorURIs = new ArrayList<URI>();
_initiatorURIs.addAll(initiatorURIs);
_targetURIs = new ArrayList<URI>(targetURIs);
}
@Override
protected void complete(DbClient dbClient, Operation.Status status, ServiceCoded coded) throws DeviceControllerException {
try {
if (supportsPartialInitiatorAddition() && status != Operation.Status.ready) {
// Get the list of initiator/port URIs. Only update those specific entries.
List<URI> uris = getInitiatorsOrPortsPhysicallyAdded();
// Update the database with the context initiators and ports only
if (uris != null && !uris.isEmpty()) {
updateDatabase(dbClient, uris);
}
} else if (status == Operation.Status.ready) {
updateDatabase(dbClient, null);
}
_log.info(String.format(
"Done ExportMaskAddInitiator - Id: %s, OpId: %s, status: %s",
getId().toString(), getOpId(), status.name()));
} catch (Exception e) {
_log.error(String.format(
"Failed updating status for ExportMaskAddInitiator - Id: %s, OpId: %s",
getId().toString(), getOpId()), e);
} finally {
super.complete(dbClient, status, coded);
}
}
public void setTargetURIs(List<URI> targetURIs) {
this._targetURIs = targetURIs;
}
/**
* Update the export mask and export group with the initiators are ports
*
* @param dbClient
* dbclient
* @param uris
* uris of Initiators and storage ports
*/
private void updateDatabase(DbClient dbClient, Collection<URI> uris) {
List<URI> targetURIs = _targetURIs;
List<URI> initiatorURIs = _initiatorURIs;
// If there are any initiators or storage ports, let's only update the ports AND
// initiators that appear in the context.
if (uris != null && !uris.isEmpty()) {
targetURIs = URIUtil.getURIsofType(uris, Initiator.class);
initiatorURIs = URIUtil.getURIsofType(uris, StoragePort.class);
}
ExportGroup exportGroup = dbClient.queryObject(ExportGroup.class, getId());
ExportMask exportMask = (getMask() != null) ? dbClient.queryObject(ExportMask.class, getMask()) : null;
if (exportMask != null) {
// Update the initiator tracking containers
exportMask.addToUserCreatedInitiators(dbClient.queryObject(Initiator.class, initiatorURIs));
// Save the initiators to the ExportMask
for (URI initiatorURI : initiatorURIs) {
Initiator initiator = dbClient.queryObject(Initiator.class, initiatorURI);
if (initiator != null) {
exportMask.removeFromExistingInitiators(initiator);
exportMask.addInitiator(initiator);
exportGroup.addInitiator(initiator);
} else {
_log.warn("Initiator {} does not exist.", initiatorURI);
}
}
// Save the target StoragePort URIs to the ExportMask
for (URI newTarget : targetURIs) {
exportMask.addTarget(newTarget);
}
dbClient.updateObject(exportMask);
}
ExportUtils.reconcileExportGroupsHLUs(dbClient, exportGroup);
dbClient.updateObject(exportGroup);
}
/**
* This method will check to see if there is a context object associated with the step,
* which will tell us if the platform that performed the add initiator operation supports
* adding only a portion of the initiators in the request.
*
* @return true if the platform supports only adding a subset of initiators to the mask
*/
private boolean supportsPartialInitiatorAddition() {
if (getContextOperations() != null) {
return true;
}
return false;
}
/**
* Retrieves the context operations
*
* @return the operations context object from the step data
*/
private List<ExportOperationContext.ExportOperationContextOperation> getContextOperations() {
List<ExportOperationContext.ExportOperationContextOperation> operations = null;
try {
ExportOperationContext context = null;
// Only specific platforms create a context object. If there is no context object, default to updating the
// object in the DB
context = (ExportOperationContext) WorkflowService.getInstance().loadStepData(getOpId());
if (context == null) {
return null;
}
// If initiators/ports were added to the mask, there will be operations hanging off the context
operations = context.getOperations();
} catch (ClassCastException cce) {
// Step state data was stored, but it's not a context object, so return true by default.
}
return operations;
}
/**
* This method will determine if any initiators/ports were added as a result of this operation.
*
* @return list of initiator URIs if any were physically added
*/
private List<URI> getInitiatorsOrPortsPhysicallyAdded() {
List<URI> uris = new ArrayList<>();
List<ExportOperationContext.ExportOperationContextOperation> operations = getContextOperations();
if (operations == null) {
return null;
}
// Go through the operations in the context and find at least one initiator/port object.
for (ExportOperationContextOperation op : operations) {
// These are the specific operations that add initiators/ports to the mask.
// So if the operation saved isn't one of these, skip it.
if (!((op.getOperation().equals(OPERATION_ADD_PORTS_TO_PORT_GROUP)) ||
(op.getOperation().equals(OPERATION_ADD_EXISTING_INITIATOR_TO_EXPORT_GROUP)) ||
(op.getOperation().equals(OPERATION_ADD_INITIATORS_TO_INITIATOR_GROUP)))) {
continue;
}
// Check for valid arguments for this operation. If there are none, skip it.
List<Object> opArgs = op.getArgs();
if (opArgs == null || opArgs.isEmpty()) {
continue;
}
// Look for a List<URI> object within the list.
for (Object opArg : opArgs) {
// We're only interested in List types, skip all others
if (!(opArg instanceof List)) {
continue;
}
// Cast to a List<Object> to see if it contains anything.
List<Object> opArgObjList = (List<Object>) opArg;
if (opArgObjList.isEmpty()) {
continue;
}
// Grab the first object of the list. We assume the List contains same-typed objects
Object opArgObjListEntry = opArgObjList.get(0);
if (!(opArgObjListEntry instanceof URI)) {
continue;
}
// If the object is a URI of type Initiator or StoragePort, then we have added an initiator or port
URI uri = (URI) opArgObjListEntry;
if (URIUtil.isType(uri, Initiator.class) || URIUtil.isType(uri, StoragePort.class)) {
uris.add(uri);
}
}
}
return uris;
}
}