/* * Copyright (c) 2015 EMC Corporation * All Rights Reserved */ package com.emc.storageos.api.service.impl.resource.blockingestorchestration.context.impl; import java.net.URI; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import org.apache.commons.collections.CollectionUtils; import com.emc.storageos.api.service.impl.resource.blockingestorchestration.BlockIngestOrchestrator; import com.emc.storageos.api.service.impl.resource.blockingestorchestration.context.IngestionRequestContext; import com.emc.storageos.api.service.impl.resource.blockingestorchestration.context.VolumeIngestionContext; import com.emc.storageos.api.service.impl.resource.blockingestorchestration.context.impl.BaseIngestionRequestContext.VolumeIngestionContextFactory; import com.emc.storageos.api.service.impl.resource.utils.VolumeIngestionUtil; import com.emc.storageos.db.client.DbClient; import com.emc.storageos.db.client.URIUtil; import com.emc.storageos.db.client.model.BlockConsistencyGroup; import com.emc.storageos.db.client.model.BlockObject; import com.emc.storageos.db.client.model.BlockSnapshot; import com.emc.storageos.db.client.model.DataObject; import com.emc.storageos.db.client.model.DataObject.Flag; 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.NamedURI; import com.emc.storageos.db.client.model.Project; import com.emc.storageos.db.client.model.StorageSystem; import com.emc.storageos.db.client.model.StringMap; import com.emc.storageos.db.client.model.StringSet; import com.emc.storageos.db.client.model.TenantOrg; import com.emc.storageos.db.client.model.VirtualArray; import com.emc.storageos.db.client.model.VirtualPool; import com.emc.storageos.db.client.model.Volume; import com.emc.storageos.db.client.model.VplexMirror; import com.emc.storageos.db.client.model.UnManagedDiscoveredObjects.UnManagedConsistencyGroup; import com.emc.storageos.db.client.model.UnManagedDiscoveredObjects.UnManagedVolume; import com.emc.storageos.db.client.model.UnManagedDiscoveredObjects.UnManagedVolume.SupportedVolumeInformation; import com.emc.storageos.db.client.util.NullColumnValueGetter; import com.emc.storageos.util.ConnectivityUtil; import com.emc.storageos.vplex.api.VPlexApiConstants; import com.emc.storageos.vplexcontroller.VPlexControllerUtils; import com.emc.storageos.vplexcontroller.VplexBackendIngestionContext; /** * A combined implementation of VolumeIngestionContext and IngestionRequestContext * for VPLEX volumes. * * The VolumeIngestionContext implementation serves as context for ingestion of the * parent VPLEX virtual volume. The IngestionRequestContext implementation serves * as context for processing the VPLEX virtual volume's backend structure. * * This class extends VplexBackendIngestionContext which is the core of context * data for the backend volumes (and is also used separately * by the VPLEX discovery process). */ public class VplexVolumeIngestionContext extends VplexBackendIngestionContext implements VolumeIngestionContext, IngestionRequestContext { private Map<String, VolumeIngestionContext> _processedUnManagedVolumeMap; private Map<String, BlockObject> _blockObjectsToBeCreatedMap; private Map<String, Set<DataObject>> _dataObjectsToBeUpdatedMap; private Map<String, Set<DataObject>> _dataObjectsToBeCreatedMap; private List<UnManagedVolume> _unManagedVolumesToBeDeleted; private final IngestionRequestContext _parentRequestContext; private VolumeIngestionContext _currentBackendVolumeIngestionContext; private Iterator<UnManagedVolume> _backendVolumeUrisToProcessIterator; private List<VplexMirror> _createdVplexMirrors; private String _haClusterId; private String _virtualVolumeVplexClusterName; private List<String> _errorMessages; // export ingestion related items private boolean _exportGroupCreated = false; private ExportGroup _exportGroup; private List<Initiator> _deviceInitiators; private List<BlockObject> _objectsIngestedByExportProcessing; private Map<BlockObject, ExportGroup> _vplexBackendExportGroupMap; private Map<String, BlockConsistencyGroup> _cgsToCreateMap; private List<UnManagedConsistencyGroup> _umCGsToUpdate; /** * Constructor. * * @param unManagedVolume the parent UnManagedVolume for this context * @param dbClient a reference to the database client * @param parentRequestContext the parent IngestionRequestContext */ public VplexVolumeIngestionContext(UnManagedVolume unManagedVolume, DbClient dbClient, IngestionRequestContext parentRequestContext) { super(unManagedVolume, dbClient); _parentRequestContext = parentRequestContext; } /* * (non-Javadoc) * * @see com.emc.storageos.api.service.impl.resource.blockingestorchestration.context.VolumeIngestionContext#getUnManagedVolume() */ @Override public UnManagedVolume getUnmanagedVolume() { return super.getUnmanagedVirtualVolume(); } /* * (non-Javadoc) * * @see com.emc.storageos.api.service.impl.resource.blockingestorchestration.context.VolumeIngestionContext#isVolumeExported() */ @Override public boolean isVolumeExported() { return VolumeIngestionUtil.checkUnManagedResourceAlreadyExported(getUnmanagedVolume()); } /* * (non-Javadoc) * * @see com.emc.storageos.api.service.impl.resource.blockingestorchestration.context.VolumeIngestionContext#commit() */ @Override public void commit() { _logger.info("persisting VPLEX backend for virtual volume " + getUnmanagedVolume().forDisplay()); setFlagsAndUpdateExportMasks(); createVplexMirrorObjects(); _dbClient.createObject(getCreatedVplexMirrors()); _dbClient.createObject(getCreatedSnapshotMap().values()); // commit the basic IngestionRequestContext collections for (BlockObject bo : getObjectsIngestedByExportProcessing()) { _logger.info("Creating BlockObject {} (hash {})", bo.forDisplay(), bo.hashCode()); _dbClient.createObject(bo); } for (BlockObject bo : getBlockObjectsToBeCreatedMap().values()) { _logger.info("Creating BlockObject {} (hash {})", bo.forDisplay(), bo.hashCode()); _dbClient.createObject(bo); } for (Set<DataObject> createdObjects : getDataObjectsToBeCreatedMap().values()) { if (createdObjects != null && !createdObjects.isEmpty()) { for (DataObject dob : createdObjects) { _logger.info("Creating DataObject {} (hash {})", dob.forDisplay(), dob.hashCode()); _dbClient.createObject(dob); } } } for (Set<DataObject> updatedObjects : getDataObjectsToBeUpdatedMap().values()) { if (updatedObjects != null && !updatedObjects.isEmpty()) { for (DataObject dob : updatedObjects) { if (dob.getInactive()) { _logger.info("Deleting DataObject {} (hash {})", dob.forDisplay(), dob.hashCode()); } else { _logger.info("Updating DataObject {} (hash {})", dob.forDisplay(), dob.hashCode()); } _dbClient.updateObject(dob); } } } for (UnManagedVolume umv : getUnManagedVolumesToBeDeleted()) { _logger.info("Deleting UnManagedVolume {} (hash {})", umv.forDisplay(), umv.hashCode()); _dbClient.updateObject(umv); } for (Entry<BlockObject, ExportGroup> entry : getVplexBackendExportGroupMap().entrySet()) { BlockObject volume = entry.getKey(); ExportGroup exportGroup = entry.getValue(); ExportGroup egInDb = _dbClient.queryObject(ExportGroup.class, exportGroup.getId()); exportGroup.addVolume(volume.getId(), ExportGroup.LUN_UNASSIGNED); if (null == egInDb) { _logger.info("Creating VPLEX backend ExportGroup {} for Volume {}", exportGroup.forDisplay(), volume.forDisplay()); _dbClient.createObject(exportGroup); } else { _logger.info("Updating VPLEX backend ExportGroup {} for Volume {}", exportGroup.forDisplay(), volume.forDisplay()); _dbClient.updateObject(exportGroup); } } for (UnManagedConsistencyGroup umcg : getUmCGObjectsToUpdate()) { if (umcg.getInactive()) { _logger.info("Deleting UnManagedConsistencyGroup {} (hash {})", umcg.forDisplay(), umcg.hashCode()); } else { _logger.info("Updating UnManagedConsistencyGroup {} (hash {})", umcg.forDisplay(), umcg.hashCode()); } _dbClient.updateObject(umcg); } for (BlockConsistencyGroup bcg : getCGObjectsToCreateMap().values()) { _logger.info("Creating BlockConsistencyGroup {} (hash {})", bcg.forDisplay(), bcg.hashCode()); _dbClient.createObject(bcg); } } /** * Gets a Map of backend Volume objects to the ExportGroup to * which they are tied. This is necessary because a VPLEX distributed * volume could have two different backend ExportGroups. * * @return a Map of backend Volume objects to its ExportGroup */ public Map<BlockObject, ExportGroup> getVplexBackendExportGroupMap() { if (null == _vplexBackendExportGroupMap) { _vplexBackendExportGroupMap = new HashMap<BlockObject, ExportGroup>(); } return _vplexBackendExportGroupMap; } /* * (non-Javadoc) * * @see com.emc.storageos.api.service.impl.resource.blockingestorchestration.context.VolumeIngestionContext#rollback() */ @Override public void rollback() { _logger.warn("performing rollback for " + getUnmanagedVirtualVolume().forDisplay()); getObjectsIngestedByExportProcessing().clear(); getBlockObjectsToBeCreatedMap().clear(); getCreatedSnapshotMap().clear(); getDataObjectsToBeCreatedMap().clear(); getDataObjectsToBeUpdatedMap().clear(); getUnManagedVolumesToBeDeleted().clear(); getCreatedVplexMirrors().clear(); getUmCGObjectsToUpdate().clear(); getCGObjectsToCreateMap().clear(); getVplexBackendExportGroupMap().clear(); Set<DataObject> objectsToUpdateForUnManagedVolume = getRootIngestionRequestContext().getDataObjectsToBeUpdatedMap().get(getUnmanagedVirtualVolume().getNativeGuid()); if (null != objectsToUpdateForUnManagedVolume) { _logger.warn("clearing objects to update on rollback for {}: {}", getUnmanagedVirtualVolume().getNativeGuid(), objectsToUpdateForUnManagedVolume); objectsToUpdateForUnManagedVolume.clear(); } } /* * (non-Javadoc) * * @see com.emc.storageos.api.service.impl.resource.blockingestorchestration.context.VolumeIngestionContext#getErrorMessages() */ @Override public List<String> getErrorMessages() { if (null == _errorMessages) { _errorMessages = new ArrayList<String>(); } return _errorMessages; } /* * (non-Javadoc) * * @see java.util.Iterator#hasNext() */ @Override public boolean hasNext() { iteratorIniterator(); return _backendVolumeUrisToProcessIterator.hasNext(); } /* * (non-Javadoc) * * @see java.util.Iterator#next() */ @Override public UnManagedVolume next() { iteratorIniterator(); UnManagedVolume currentUnmanagedVolume = _backendVolumeUrisToProcessIterator.next(); setCurrentUnmanagedVolume(currentUnmanagedVolume); return currentUnmanagedVolume; } /* * (non-Javadoc) * * @see java.util.Iterator#remove() */ @Override public void remove() { iteratorIniterator(); _backendVolumeUrisToProcessIterator.remove(); } /** * Initializes the internal backend volume URI interator. */ private void iteratorIniterator() { if (null == _backendVolumeUrisToProcessIterator) { _backendVolumeUrisToProcessIterator = this.getUnmanagedVolumesToIngest().iterator(); } } /** * Private setter for the current backend UnManagedVolume, used by this class' implementation * of Iterator<UnManagedVolume>. Will also set the current VolumeIngestionContext. * * @param unManagedVolume the UnManagedVolume to set */ private void setCurrentUnmanagedVolume(UnManagedVolume unManagedVolume) { _currentBackendVolumeIngestionContext = VolumeIngestionContextFactory.getVolumeIngestionContext(unManagedVolume, _dbClient, this); } /* * (non-Javadoc) * * @see com.emc.storageos.api.service.impl.resource.blockingestorchestration.context.IngestionRequestContext#getCurrentUnmanagedVolume() */ @Override public UnManagedVolume getCurrentUnmanagedVolume() { if (_currentBackendVolumeIngestionContext == null) { return null; } return _currentBackendVolumeIngestionContext.getUnmanagedVolume(); } /* * (non-Javadoc) * * @see * com.emc.storageos.api.service.impl.resource.blockingestorchestration.context.IngestionRequestContext#getCurrentUnManagedVolumeUri() */ @Override public URI getCurrentUnManagedVolumeUri() { return getCurrentUnmanagedVolume().getId(); } /* * (non-Javadoc) * * @see com.emc.storageos.api.service.impl.resource.blockingestorchestration.context.IngestionRequestContext#getVolumeContext() */ @Override public VolumeIngestionContext getVolumeContext() { return _currentBackendVolumeIngestionContext; } /* * (non-Javadoc) * * @see * com.emc.storageos.api.service.impl.resource.blockingestorchestration.context.IngestionRequestContext#getVolumeContext(java.lang. * String * ) */ @Override public VolumeIngestionContext getVolumeContext(String unmanagedVolumeGuid) { if (getProcessedUnManagedVolumeMap().get(unmanagedVolumeGuid) != null) { return getProcessedUnManagedVolumeMap().get(unmanagedVolumeGuid); } return getVolumeContext(); } /* * (non-Javadoc) * * @see com.emc.storageos.api.service.impl.resource.blockingestorchestration.context.IngestionRequestContext#getStorageSystem() */ @Override public StorageSystem getStorageSystem() { URI storageSystemUri = getCurrentUnmanagedVolume().getStorageSystemUri(); StorageSystem storageSystem = getStorageSystemCache().get(storageSystemUri.toString()); if (null == storageSystem) { storageSystem = _dbClient.queryObject(StorageSystem.class, storageSystemUri); getStorageSystemCache().put(storageSystemUri.toString(), storageSystem); } return storageSystem; } /** * Returns the high availability VirtualPool for this VolumeIngestionContext's * UnManagedVolume virtual volume. * * @return the high availability VirtualPool */ public VirtualPool getHaVpool(UnManagedVolume unmanagedVolume) { VirtualPool haVpool = null; StringMap haVarrayVpoolMap = _parentRequestContext.getVpool(unmanagedVolume).getHaVarrayVpoolMap(); if (haVarrayVpoolMap != null && !haVarrayVpoolMap.isEmpty()) { String haVarrayStr = haVarrayVpoolMap.keySet().iterator().next(); String haVpoolStr = haVarrayVpoolMap.get(haVarrayStr); if (haVpoolStr != null && !(haVpoolStr.equals(NullColumnValueGetter.getNullURI().toString()))) { haVpool = _dbClient.queryObject(VirtualPool.class, URI.create(haVpoolStr)); } } return haVpool; } /** * Returns the high availability VirtualArray for this VolumeIngestionContext's * UnManagedVolume virtual volume. * * @return the high availability VirtualArray */ public VirtualArray getHaVarray(UnManagedVolume unmanagedVolume) { VirtualArray haVarray = null; StringMap haVarrayVpoolMap = _parentRequestContext.getVpool(unmanagedVolume).getHaVarrayVpoolMap(); if (haVarrayVpoolMap != null && !haVarrayVpoolMap.isEmpty()) { String haVarrayStr = haVarrayVpoolMap.keySet().iterator().next(); if (haVarrayStr != null && !(haVarrayStr.equals(NullColumnValueGetter.getNullURI().toString()))) { haVarray = _dbClient.queryObject(VirtualArray.class, URI.create(haVarrayStr)); } } return haVarray; } /** * Sets the VPLEX cluster ID for the high availability cluster. * * @param haClusterId the high availability cluster ID to set */ public void setHaClusterId(String haClusterId) { this._haClusterId = haClusterId; } /* * (non-Javadoc) * * @see com.emc.storageos.api.service.impl.resource.blockingestorchestration.context.IngestionRequestContext#getVpool() */ @Override public VirtualPool getVpool(UnManagedVolume unmanagedVolume) { VirtualPool vpoolForThisVolume = _parentRequestContext.getVpool(unmanagedVolume); // get the backend volume cluster id String backendClusterId = VplexBackendIngestionContext.extractValueFromStringSet( SupportedVolumeInformation.VPLEX_BACKEND_CLUSTER_ID.toString(), unmanagedVolume.getVolumeInformation()); if (null != backendClusterId && null != _haClusterId && backendClusterId.equals(_haClusterId)) { if (null != getHaVpool(unmanagedVolume)) { _logger.info("using high availability vpool " + getHaVpool(unmanagedVolume).getLabel()); vpoolForThisVolume = getHaVpool(unmanagedVolume); } } // finally, double check for a separate mirror / continuous copies vpool // TODO: verify separate mirror vpool if (getUnmanagedVplexMirrors().keySet().contains(unmanagedVolume) && vpoolForThisVolume.getMirrorVirtualPool() != null) { _logger.info("this associated volume is a mirror and separate mirror vpool is defined"); VirtualPool mirrorVpool = _dbClient.queryObject( VirtualPool.class, URI.create(vpoolForThisVolume.getMirrorVirtualPool())); _logger.info("using mirror vpool " + mirrorVpool.getLabel()); vpoolForThisVolume = mirrorVpool; } return vpoolForThisVolume; } /* * (non-Javadoc) * * @see com.emc.storageos.api.service.impl.resource.blockingestorchestration.context.IngestionRequestContext#getVarray() */ @Override public VirtualArray getVarray(UnManagedVolume unmanagedVolume) { VirtualArray varrayForThisVolume = _parentRequestContext.getVarray(unmanagedVolume); // get the backend volume cluster id String backendClusterId = VplexBackendIngestionContext.extractValueFromStringSet( SupportedVolumeInformation.VPLEX_BACKEND_CLUSTER_ID.toString(), unmanagedVolume.getVolumeInformation()); if (null != backendClusterId && null != _haClusterId && backendClusterId.equals(_haClusterId)) { _logger.info("using high availability varray " + getHaVarray(unmanagedVolume).getLabel()); varrayForThisVolume = getHaVarray(unmanagedVolume); } return varrayForThisVolume; } /* * (non-Javadoc) * * @see com.emc.storageos.api.service.impl.resource.blockingestorchestration.context.IngestionRequestContext#getProject() */ @Override public Project getProject() { // determine the correct project to use with this volume: // the backend volumes have the vplex backend Project, but // the rest have the same Project as the virtual volume. Project project = getUnmanagedBackendVolumes().contains(getCurrentUnmanagedVolume()) ? getBackendProject() : getFrontendProject(); return project; } /* * (non-Javadoc) * * @see com.emc.storageos.api.service.impl.resource.blockingestorchestration.context.IngestionRequestContext#getTenant() */ @Override public TenantOrg getTenant() { return _parentRequestContext.getTenant(); } /* * (non-Javadoc) * * @see com.emc.storageos.api.service.impl.resource.blockingestorchestration.context.IngestionRequestContext#getVplexIngestionMethod() */ @Override public String getVplexIngestionMethod() { return _parentRequestContext.getVplexIngestionMethod(); } /* * (non-Javadoc) * * @see com.emc.storageos.api.service.impl.resource.blockingestorchestration.context.IngestionRequestContext#getStorageSystemCache() */ @Override public Map<String, StorageSystem> getStorageSystemCache() { return _parentRequestContext.getStorageSystemCache(); } /* * (non-Javadoc) * * @see * com.emc.storageos.api.service.impl.resource.blockingestorchestration.context.IngestionRequestContext#getExhaustedStorageSystems() */ @Override public List<URI> getExhaustedStorageSystems() { return _parentRequestContext.getExhaustedStorageSystems(); } /* * (non-Javadoc) * * @see com.emc.storageos.api.service.impl.resource.blockingestorchestration.context.IngestionRequestContext#getExhaustedPools() */ @Override public List<URI> getExhaustedPools() { return _parentRequestContext.getExhaustedPools(); } /* * (non-Javadoc) * * @see * com.emc.storageos.api.service.impl.resource.blockingestorchestration.context.IngestionRequestContext#getUnManagedVolumesToBeDeleted() */ @Override public List<UnManagedVolume> getUnManagedVolumesToBeDeleted() { if (null == _unManagedVolumesToBeDeleted) { _unManagedVolumesToBeDeleted = new ArrayList<UnManagedVolume>(); } return _unManagedVolumesToBeDeleted; } /* * (non-Javadoc) * * @see com.emc.storageos.api.service.impl.resource.blockingestorchestration.context.IngestionRequestContext#getTaskStatusMap() */ @Override public Map<String, StringBuffer> getTaskStatusMap() { return _parentRequestContext.getTaskStatusMap(); } /* * (non-Javadoc) * * @see * com.emc.storageos.api.service.impl.resource.blockingestorchestration.context.IIngestionRequestContext#getProcessedUnManagedVolumeMap * () */ @Override public Map<String, VolumeIngestionContext> getProcessedUnManagedVolumeMap() { if (null == _processedUnManagedVolumeMap) { _processedUnManagedVolumeMap = new HashMap<String, VolumeIngestionContext>(); } return _processedUnManagedVolumeMap; } /* * (non-Javadoc) * * @see * com.emc.storageos.api.service.impl.resource.blockingestorchestration.context.IIngestionRequestContext#getProcessedUnManagedVolume * (java.lang.String) */ @Override public UnManagedVolume getProcessedUnManagedVolume(String nativeGuid) { VolumeIngestionContext volumeContext = getProcessedUnManagedVolumeMap().get(nativeGuid); if (null != volumeContext) { return volumeContext.getUnmanagedVolume(); } return null; } /* * (non-Javadoc) * * @see * com.emc.storageos.api.service.impl.resource.blockingestorchestration.context.IngestionRequestContext#getProcessedBlockObject(java * .lang.String) */ @Override public BlockObject getProcessedBlockObject(String unmanagedVolumeGuid) { String objectGUID = unmanagedVolumeGuid.replace(VolumeIngestionUtil.UNMANAGEDVOLUME, VolumeIngestionUtil.VOLUME); return getBlockObjectsToBeCreatedMap().get(objectGUID); } /* * (non-Javadoc) * * @see * com.emc.storageos.api.service.impl.resource.blockingestorchestration.context.IIngestionRequestContext#getProcessedVolumeContext(java * .lang.String) */ @Override public VolumeIngestionContext getProcessedVolumeContext(String nativeGuid) { return getProcessedUnManagedVolumeMap().get(nativeGuid); } /* * (non-Javadoc) * * @see * com.emc.storageos.api.service.impl.resource.blockingestorchestration.context.IngestionRequestContext#getErrorMessagesForVolume(java * .lang.String) */ @Override public List<String> getErrorMessagesForVolume(String nativeGuid) { // for VPLEX, we want to return the error messages List for the // main UnManagedVolume, whose status would be returned to the user return getErrorMessages(); } /* * (non-Javadoc) * * @see com.emc.storageos.api.service.impl.resource.blockingestorchestration.context.IIngestionRequestContext# * getObjectsIngestedByExportProcessing() */ @Override public List<BlockObject> getObjectsIngestedByExportProcessing() { if (null == _objectsIngestedByExportProcessing) { _objectsIngestedByExportProcessing = new ArrayList<BlockObject>(); } return _objectsIngestedByExportProcessing; } /* * (non-Javadoc) * * @see com.emc.storageos.api.service.impl.resource.blockingestorchestration.context.IngestionRequestContext#isExportGroupCreated() */ @Override public boolean isExportGroupCreated() { return _exportGroupCreated; } /* * (non-Javadoc) * * @see * com.emc.storageos.api.service.impl.resource.blockingestorchestration.context.IngestionRequestContext#setExportGroupCreated(boolean) */ @Override public void setExportGroupCreated(boolean exportGroupCreated) { this._exportGroupCreated = exportGroupCreated; } /* * (non-Javadoc) * * @see com.emc.storageos.api.service.impl.resource.blockingestorchestration.context.IngestionRequestContext#getExportGroup() */ @Override public ExportGroup getExportGroup() { return _exportGroup; } /* * (non-Javadoc) * * @see * com.emc.storageos.api.service.impl.resource.blockingestorchestration.context.IngestionRequestContext#setExportGroup(com.emc.storageos * .db.client.model.ExportGroup) */ @Override public void setExportGroup(ExportGroup exportGroup) { this._exportGroup = exportGroup; } /* * (non-Javadoc) * * @see com.emc.storageos.api.service.impl.resource.blockingestorchestration.context.IngestionRequestContext#getHost() */ @Override public URI getHost() { // VPLEX backend volumes would never be exported directly to a Host return null; } /* * (non-Javadoc) * * @see com.emc.storageos.api.service.impl.resource.blockingestorchestration.context.IngestionRequestContext#setHost(java.net.URI) */ @Override public void setHost(URI host) { // no-op; vplex ingestion only uses device initiators for export } /* * (non-Javadoc) * * @see com.emc.storageos.api.service.impl.resource.blockingestorchestration.context.IngestionRequestContext#getCluster() */ @Override public URI getCluster() { // VPLEX backend volumes would never be exported directly to a Cluster return null; } /* * (non-Javadoc) * * @see com.emc.storageos.api.service.impl.resource.blockingestorchestration.context.IngestionRequestContext#setCluster(java.net.URI) */ @Override public void setCluster(URI cluster) { // no-op; vplex ingestion only uses device initiators for export } /* * (non-Javadoc) * * @see com.emc.storageos.api.service.impl.resource.blockingestorchestration.context.IngestionRequestContext#getDeviceInitiators() */ @Override public List<Initiator> getDeviceInitiators() { return _deviceInitiators; } /* * (non-Javadoc) * * @see * com.emc.storageos.api.service.impl.resource.blockingestorchestration.context.IngestionRequestContext#setDeviceInitiators(java.util * .List) */ @Override public void setDeviceInitiators(List<Initiator> deviceInitiators) { this._deviceInitiators = deviceInitiators; } /* * (non-Javadoc) * * @see com.emc.storageos.api.service.impl.resource.blockingestorchestration.context.IngestionRequestContext#getObjectsToBeCreatedMap() */ @Override public Map<String, BlockObject> getBlockObjectsToBeCreatedMap() { if (null == _blockObjectsToBeCreatedMap) { _blockObjectsToBeCreatedMap = new HashMap<String, BlockObject>(); } return _blockObjectsToBeCreatedMap; } /* * (non-Javadoc) * * @see * com.emc.storageos.api.service.impl.resource.blockingestorchestration.context.IngestionRequestContext#getDataObjectsToBeCreatedMap() */ @Override public Map<String, Set<DataObject>> getDataObjectsToBeCreatedMap() { if (null == _dataObjectsToBeCreatedMap) { _dataObjectsToBeCreatedMap = new HashMap<String, Set<DataObject>>(); } return _dataObjectsToBeCreatedMap; } /* * (non-Javadoc) * * @see com.emc.storageos.api.service.impl.resource.blockingestorchestration.context.IngestionRequestContext#getObjectsToBeUpdatedMap() */ @Override public Map<String, Set<DataObject>> getDataObjectsToBeUpdatedMap() { if (null == _dataObjectsToBeUpdatedMap) { _dataObjectsToBeUpdatedMap = new HashMap<String, Set<DataObject>>(); } return _dataObjectsToBeUpdatedMap; } /** * Updates any internal flags on the ingested backend resources. * * @param context the VplexBackendIngestionContext */ private void setFlagsAndUpdateExportMasks() { // assemble a map of backend Volume native guid(s) to backend ExportMasks. Map<String, Set<ExportMask>> backendVolumeGuidToExportMasksMap = new HashMap<String, Set<ExportMask>>(); collectExportMasksToUpdate(getDataObjectsToBeCreatedMap(), backendVolumeGuidToExportMasksMap); collectExportMasksToUpdate(getDataObjectsToBeUpdatedMap(), backendVolumeGuidToExportMasksMap); // set internal object flag on any backend volumes for (BlockObject o : getBlockObjectsToBeCreatedMap().values()) { if (getBackendVolumeGuids().contains(o.getNativeGuid())) { o.clearInternalFlags(BlockIngestOrchestrator.INTERNAL_VOLUME_FLAGS); _logger.info("setting INTERNAL_OBJECT flag on " + o.getLabel()); o.addInternalFlags(Flag.INTERNAL_OBJECT); // check if any backend ExportMasks need to be updated Set<ExportMask> exportMasks = backendVolumeGuidToExportMasksMap.get(o.getNativeGuid()); if (!CollectionUtils.isEmpty(exportMasks)) { for (ExportMask exportMask : exportMasks) { if (null != exportMask) { _logger.info( "Removing block object {} from existing volumes and adding to user created volumes of export mask {}", o.getNativeGuid(), exportMask.getMaskName()); exportMask.removeFromExistingVolumes(o); exportMask.addToUserCreatedVolumes(o); } } } } } // Look to see if the backend ingestion resulted in the creation of a // BlockSnapshot instance, which would occur if the backend volume is // also a snapshot target volume. It is possible that the snapshot is // still marked internal if the VPLEX volume built on the snapshot // is ingested after the VPLEX volume whose backend volume is the // snapshot source volume. If the snapshot source is set, then snapshot // and source are fully ingested and we need to make sure the snapshot // is public. for (BlockSnapshot snapshot : getCreatedSnapshotMap().values()) { if (!NullColumnValueGetter.isNullValue(snapshot.getSourceNativeId())) { snapshot.clearInternalFlags(BlockIngestOrchestrator.INTERNAL_VOLUME_FLAGS); } } } /** * Collect any export masks that need to be updated by this ingestion context. * * @param dataObjectMap the Map of guids to data objects from the ingestion context * @param exportMaskMap the map of guids to ExportMasks to update * @return the map of guids to ExportMasks to update */ private Map<String, Set<ExportMask>> collectExportMasksToUpdate( Map<String, Set<DataObject>> dataObjectMap, Map<String, Set<ExportMask>> exportMaskMap) { if (null != exportMaskMap && null != dataObjectMap) { // the dataObjectMap is a map of UNMANAGEDVOLUME native GUIDs to a Set of associated data objects for (Entry<String, Set<DataObject>> entry : dataObjectMap.entrySet()) { Set<DataObject> values = entry.getValue(); if (!CollectionUtils.isEmpty(values)) { for (DataObject dataObject : values) { if (dataObject instanceof ExportMask) { _logger.info("collecting ExportMask: " + dataObject.forDisplay()); // the key is the Volume native GUID, but the dataObjectMap will have the UnManagedVolume GUID, so swap. // e.g.: CLARIION+APM00140844986+UNMANAGEDVOLUME+02876 -> CLARIION+APM00140844986+VOLUME+02876 String key = entry.getKey().replace(VolumeIngestionUtil.UNMANAGEDVOLUME, VolumeIngestionUtil.VOLUME); Set<ExportMask> exportMasks = exportMaskMap.get(key); // if no export masks yet, create an empty set and add it to the return map. if (null == exportMasks) { exportMasks = new HashSet<ExportMask>(); exportMaskMap.put(key, exportMasks); } exportMasks.add((ExportMask) dataObject); } } } } } return exportMaskMap; } /** * Returns a List of created VplexMirror Objects. * * @return a List of created VplexMirror Objects */ public List<VplexMirror> getCreatedVplexMirrors() { if (null == _createdVplexMirrors) { _createdVplexMirrors = new ArrayList<VplexMirror>(); } return _createdVplexMirrors; } /** * Create a VplexMirror database object if a VPLEX native mirror is present. * This should be called after the parent virtual volume has already been ingested. * * @param context the VplexBackendIngestionContext * @param virtualVolume the ingested virtual volume's Volume object. */ private void createVplexMirrorObjects() { if (!getUnmanagedVplexMirrors().isEmpty()) { Volume virtualVolume = (Volume) _parentRequestContext.getProcessedBlockObject( getUnmanagedVirtualVolume().getNativeGuid()); _logger.info("creating VplexMirror object for virtual volume " + virtualVolume.getLabel()); for (Entry<UnManagedVolume, String> entry : getUnmanagedVplexMirrors().entrySet()) { // find mirror and create a VplexMirror object BlockObject mirror = getBlockObjectsToBeCreatedMap().get(entry.getKey().getNativeGuid() .replace(VolumeIngestionUtil.UNMANAGEDVOLUME, VolumeIngestionUtil.VOLUME)); if (null != mirror) { _logger.info("processing mirror " + mirror.getLabel()); if (mirror instanceof Volume) { Volume mirrorVolume = (Volume) mirror; // create VplexMirror set all the basic properties VplexMirror vplexMirror = new VplexMirror(); vplexMirror.setId(URIUtil.createId(VplexMirror.class)); vplexMirror.setCapacity(mirrorVolume.getCapacity()); vplexMirror.setLabel(mirrorVolume.getLabel()); vplexMirror.setNativeId(entry.getValue()); // For Vplex virtual volumes set allocated capacity to 0 (cop-18608) vplexMirror.setAllocatedCapacity(0L); vplexMirror.setProvisionedCapacity(mirrorVolume.getProvisionedCapacity()); vplexMirror.setSource(new NamedURI(virtualVolume.getId(), virtualVolume.getLabel())); vplexMirror.setStorageController(virtualVolume.getStorageController()); vplexMirror.setTenant(mirrorVolume.getTenant()); vplexMirror.setThinPreAllocationSize(mirrorVolume.getThinVolumePreAllocationSize()); vplexMirror.setThinlyProvisioned(mirrorVolume.getThinlyProvisioned()); vplexMirror.setVirtualArray(mirrorVolume.getVirtualArray()); vplexMirror.setVirtualPool(mirrorVolume.getVirtualPool()); // set the associated volume for this VplexMirror StringSet associatedVolumes = new StringSet(); associatedVolumes.add(mirrorVolume.getId().toString()); vplexMirror.setAssociatedVolumes(associatedVolumes); // VplexMirror will have the same project // as the virtual volume (i.e., the front-end project) // but the mirror backend will have the backend project vplexMirror.setProject(new NamedURI( getFrontendProject().getId(), mirrorVolume.getLabel())); mirrorVolume.setProject(new NamedURI( getBackendProject().getId(), mirrorVolume.getLabel())); // update flags on mirror volume Set<DataObject> updatedObjects = getDataObjectsToBeUpdatedMap().get(mirrorVolume.getNativeGuid()); if (updatedObjects == null) { updatedObjects = new HashSet<DataObject>(); getDataObjectsToBeUpdatedMap().put(mirrorVolume.getNativeGuid(), updatedObjects); } VolumeIngestionUtil.clearInternalFlags(this, mirrorVolume, updatedObjects, _dbClient); // VPLEX backend volumes should still have the INTERNAL_OBJECT flag mirrorVolume.addInternalFlags(Flag.INTERNAL_OBJECT); // deviceLabel will be the very last part of the native guid String[] devicePathParts = entry.getValue().split("/"); String deviceName = devicePathParts[devicePathParts.length - 1]; vplexMirror.setDeviceLabel(deviceName); // save the new VplexMirror & persist backend & updated objects getCreatedVplexMirrors().add(vplexMirror); // set mirrors property on the parent virtual volume StringSet mirrors = virtualVolume.getMirrors(); if (mirrors == null) { mirrors = new StringSet(); } mirrors.add(vplexMirror.getId().toString()); virtualVolume.setMirrors(mirrors); } } } } } /* * (non-Javadoc) * * @see * com.emc.storageos.api.service.impl.resource.blockingestorchestration.context.IngestionRequestContext#findCreatedBlockObject(java. * lang. * String) */ @Override public BlockObject findCreatedBlockObject(String nativeGuid) { BlockObject blockObject = getBlockObjectsToBeCreatedMap().get(nativeGuid); return blockObject; } /** * Returns a detailed report on the state of everything in this context, * useful for debugging. * * @return a detailed report on the context */ public String toStringDebug() { StringBuilder s = new StringBuilder("\n\nVplexBackendIngestionContext \n\t "); s.append("unmanaged virtual volume: ").append(getUnmanagedVirtualVolume()).append(" \n\t "); s.append("unmanaged backend volume(s): ").append(this.getUnmanagedBackendVolumes()).append(" \n\t "); s.append("unmanaged snapshots: ").append(this.getUnmanagedSnapshots()).append(" \n\t "); s.append("unmanaged full clones: ").append(this.getUnmanagedVplexClones()).append(" \n\t "); s.append("unmanaged backend only clones: ").append(this.getUnmanagedBackendOnlyClones()).append(" \n\t "); s.append("unmanaged mirrors: ").append(this.getUnmanagedVplexMirrors()).append(" \n\t "); s.append("ingested objects: ").append(this.getObjectsIngestedByExportProcessing()).append(" \n\t "); s.append("created objects map: ").append(this.getBlockObjectsToBeCreatedMap()).append(" \n\t "); s.append("updated objects map: "); for (Entry<String, Set<DataObject>> e : this.getDataObjectsToBeUpdatedMap().entrySet()) { s.append(e.getKey()).append(": "); for (DataObject o : e.getValue()) { s.append(o.getLabel()).append("; "); } } s.append(" \n\t "); s.append("processed unmanaged volumes: ").append(this.getProcessedUnManagedVolumeMap()).append("\n\n"); return s.toString(); } @Override public String toString() { if (_logger.isDebugEnabled()) { return toStringDebug(); } return super.toString(); } /* * (non-Javadoc) * * @see com.emc.storageos.api.service.impl.resource.blockingestorchestration.context.IngestionRequestContext# * findAllUnManagedVolumesToBeDeleted() */ @Override public List<UnManagedVolume> findAllUnManagedVolumesToBeDeleted() { return _parentRequestContext.findAllUnManagedVolumesToBeDeleted(); } /* * (non-Javadoc) * * @see * com.emc.storageos.api.service.impl.resource.blockingestorchestration.context.IngestionRequestContext#findInUpdatedObjects(java.net. * URI) */ @Override public DataObject findInUpdatedObjects(URI uri) { if (!URIUtil.isValid(uri)) { _logger.warn("URI ({}) for findCreatedBlockObject is null or invalid", uri); return null; } for (Set<DataObject> objectsToBeUpdated : this.getDataObjectsToBeUpdatedMap().values()) { for (DataObject o : objectsToBeUpdated) { if (o.getId().equals(uri)) { _logger.info("\tfound data object in vplex request context: " + o.forDisplay()); return o; } } } return _parentRequestContext.findInUpdatedObjects(uri); } /* * (non-Javadoc) * * @see * com.emc.storageos.api.service.impl.resource.blockingestorchestration.context.IngestionRequestContext#findCreatedBlockObject(java.net. * URI) */ @Override public BlockObject findCreatedBlockObject(URI uri) { if (!URIUtil.isValid(uri)) { _logger.warn("URI ({}) for findCreatedBlockObject is null or invalid", uri); return null; } for (BlockObject bo : getBlockObjectsToBeCreatedMap().values()) { if (bo.getId() != null && uri.toString().equals(bo.getId().toString())) { _logger.info("\tfound block object in vplex request context: " + bo.forDisplay()); return bo; } } return null; } /* * (non-Javadoc) * * @see com.emc.storageos.api.service.impl.resource.blockingestorchestration.context.IngestionRequestContext#getCGObjectsToCreateMap() */ @Override public Map<String, BlockConsistencyGroup> getCGObjectsToCreateMap() { if (null == _cgsToCreateMap) { _cgsToCreateMap = new HashMap<String, BlockConsistencyGroup>(); } return _cgsToCreateMap; } @Override public List<UnManagedConsistencyGroup> getUmCGObjectsToUpdate() { if (null == _umCGsToUpdate) { _umCGsToUpdate = new ArrayList<UnManagedConsistencyGroup>(); } return _umCGsToUpdate; } /* * (non-Javadoc) * * @see * com.emc.storageos.api.service.impl.resource.blockingestorchestration.context.IngestionRequestContext#findUnManagedConsistencyGroup( * com.emc.storageos.db.client.model.BlockConsistencyGroup) */ @Override public UnManagedConsistencyGroup findUnManagedConsistencyGroup(String cgName) { return _parentRequestContext.findUnManagedConsistencyGroup(cgName); } /* * (non-Javadoc) * * @see com.emc.storageos.api.service.impl.resource.blockingestorchestration.context.IngestionRequestContext#addObjectToCreate(com.emc. * storageos.db.client.model.BlockObject) */ @Override public void addBlockObjectToCreate(BlockObject blockObject) { getBlockObjectsToBeCreatedMap().put(blockObject.getNativeGuid(), blockObject); } /* * (non-Javadoc) * * @see com.emc.storageos.api.service.impl.resource.blockingestorchestration.context.IngestionRequestContext#addObjectToUpdate(com.emc. * storageos.db.client.model.DataObject) */ @Override public void addDataObjectToUpdate(DataObject dataObject, UnManagedVolume unManagedVolume) { if (null == getDataObjectsToBeUpdatedMap().get(unManagedVolume.getNativeGuid())) { getDataObjectsToBeUpdatedMap().put(unManagedVolume.getNativeGuid(), new HashSet<DataObject>()); } getDataObjectsToBeUpdatedMap().get(unManagedVolume.getNativeGuid()).add(dataObject); } /* * (non-Javadoc) * * @see * com.emc.storageos.api.service.impl.resource.blockingestorchestration.context.IngestionRequestContext#addDataObjectToCreate(com.emc. * storageos.db.client.model.DataObject) */ @Override public void addDataObjectToCreate(DataObject dataObject, UnManagedVolume unManagedVolume) { if (null == getDataObjectsToBeCreatedMap().get(unManagedVolume.getNativeGuid())) { getDataObjectsToBeCreatedMap().put(unManagedVolume.getNativeGuid(), new HashSet<DataObject>()); } getDataObjectsToBeCreatedMap().get(unManagedVolume.getNativeGuid()).add(dataObject); } /* (non-Javadoc) * @see com.emc.storageos.api.service.impl.resource.blockingestorchestration.context.IngestionRequestContext#findExportGroup(java.lang.String) */ @Override public ExportGroup findExportGroup(String exportGroupLabel, URI project, URI varray, URI computeResource, String resourceType) { if (exportGroupLabel != null) { ExportGroup localExportGroup = getExportGroup(); if (null != localExportGroup && exportGroupLabel.equals(localExportGroup.getLabel())) { if (VolumeIngestionUtil.verifyExportGroupMatches(localExportGroup, exportGroupLabel, project, varray, computeResource, resourceType)) { _logger.info("Found existing local ExportGroup {} in VPLEX ingestion request context", localExportGroup.forDisplay()); return localExportGroup; } } for (ExportGroup backendExportGroup : getVplexBackendExportGroupMap().values()) { if (VolumeIngestionUtil.verifyExportGroupMatches(backendExportGroup, exportGroupLabel, project, varray, computeResource, resourceType)) { _logger.info("Found existing nested ExportGroup {} in VPLEX backend ingestion request context", backendExportGroup.forDisplay()); return backendExportGroup; } } } _logger.info("Could not find existing export group for label " + exportGroupLabel); return null; } /* (non-Javadoc) * @see com.emc.storageos.api.service.impl.resource.blockingestorchestration.context.IngestionRequestContext#findAllNewExportMasks() */ @Override public List<ExportMask> findAllNewExportMasks() { List<ExportMask> newExportMasks = new ArrayList<ExportMask>(); for (Set<DataObject> createdObjects : this.getDataObjectsToBeCreatedMap().values()) { for (DataObject createdObject : createdObjects) { if (createdObject instanceof ExportMask) { newExportMasks.add((ExportMask) createdObject); } } } return newExportMasks; } /* (non-Javadoc) * @see com.emc.storageos.api.service.impl.resource.blockingestorchestration.context.IngestionRequestContext#getRootIngestionRequestContext() */ @Override public IngestionRequestContext getRootIngestionRequestContext() { return _parentRequestContext; } /* (non-Javadoc) * @see com.emc.storageos.api.service.impl.resource.blockingestorchestration.context.IngestionRequestContext#findDataObjectByType(java.lang.Class, java.net.URI) */ @Override public <T extends DataObject> T findDataObjectByType(Class<T> clazz, URI id, boolean fallbackToDatabase) { return getRootIngestionRequestContext().findDataObjectByType(clazz, id, fallbackToDatabase); } /** * Get the name of the VPLEX cluster on which this virtual volume resides * according to the Virtual Array that is currently being ingested. * * @return the virtualVolumeVplexClusterName the VPLEX cluster name for this virtual volume */ public String getVirtualVolumeVplexClusterName() { if (_virtualVolumeVplexClusterName == null) { // this should be set by the BlockVplexVolumeIngestOrchestrator to use the cluster // name cache, but in the case of re-ingestion, it may not be set, so call from here URI varrayUri = getRootIngestionRequestContext().getVarray(getUnmanagedVolume()).getId(); URI vplexUri = getRootIngestionRequestContext().getStorageSystem().getId(); String varrayClusterId = ConnectivityUtil.getVplexClusterForVarray(varrayUri, vplexUri, _dbClient); _virtualVolumeVplexClusterName = VPlexControllerUtils.getClusterNameForId(varrayClusterId, vplexUri, _dbClient); } return _virtualVolumeVplexClusterName; } /** * Sets the name of the VPLEX cluster on which this virtual volume resides * according to the Virtual Array that is currently being ingested. * * @param virtualVolumeVplexClusterName the VPLEX cluster name to set */ public void setVirtualVolumeVplexClusterName(String virtualVolumeVplexClusterName) { _logger.info("setting virtual volume VPLEX cluster name to " + virtualVolumeVplexClusterName); this._virtualVolumeVplexClusterName = virtualVolumeVplexClusterName; } /** * Returns a StringSet of associated volume URIs for this context's VPLEX virtual volume. * If the volume already has associated backend volume URIs, then that Set is returned. * Otherwise, the block objects to be created map will be checked for the existence of * ingested backend volumes that haven't been associated yet. * * An incomplete or empty Set may be returned depending on the state of ingestion, but a warning * will be logged if the count of volume URIs to be returned is not the expected number for the VPLEX * virtual volume type (2 for distributed type, 1 for local type). * * @param volume the VPLEX virtual volume object to check * @return a StringSet of associated volume URIs for this context's VPLEX virtual volume */ public StringSet getAssociatedVolumeIds(Volume volume) { if (volume != null && volume.getAssociatedVolumes() != null && !volume.getAssociatedVolumes().isEmpty()) { _logger.info("getAssociatedVolumes: volumes are already associated with the virtual volume, returning {}", volume.getAssociatedVolumes()); return volume.getAssociatedVolumes(); } StringSet associatedVolumes = new StringSet(); for (String backendVolumeNativeGuid : getBackendVolumeGuids()) { BlockObject backendVolume = getBlockObjectsToBeCreatedMap().get(backendVolumeNativeGuid); associatedVolumes.add(backendVolume.getId().toString()); } if (this.isDistributed() && associatedVolumes.size() != VPlexApiConstants.DISTRIBUTED_BACKEND_VOLUME_COUNT) { _logger.warn("getAssociatedVolumes: virtual volume is distributed, but {} backend volumes were found (expected 2)", associatedVolumes.size()); } else if (this.isLocal() && associatedVolumes.size() != VPlexApiConstants.LOCAL_BACKEND_VOLUME_COUNT) { _logger.warn("getAssociatedVolumes: virtual volume is local, but {} backend volumes were found (expected 1)", associatedVolumes.size()); } _logger.info("getAssociatedVolumes: backend volumes assembled, returning {}", associatedVolumes); return associatedVolumes; } }