/*
* Copyright (c) 2016 EMC Corporation
* All Rights Reserved
*/
package com.emc.storageos.api.service.impl.resource.blockingestorchestration.cg;
import java.net.URI;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.Map.Entry;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.emc.storageos.api.service.impl.resource.blockingestorchestration.context.IngestionRequestContext;
import com.emc.storageos.api.service.impl.resource.utils.VolumeIngestionUtil;
import com.emc.storageos.db.client.model.AbstractChangeTrackingSet;
import com.emc.storageos.db.client.model.BlockConsistencyGroup;
import com.emc.storageos.db.client.model.BlockConsistencyGroup.Types;
import com.emc.storageos.db.client.model.BlockObject;
import com.emc.storageos.db.client.model.StringSetMap;
import com.emc.storageos.db.client.model.Volume;
import com.emc.storageos.db.client.model.UnManagedDiscoveredObjects.UnManagedVolume;
import com.emc.storageos.db.client.model.util.BlockConsistencyGroupUtils;
import com.emc.storageos.db.client.util.NullColumnValueGetter;
/**
* This Decorator is responsible for decorating CG with the VPLEX Volume properties.
*
* Ex:-
* In case of RP + VPLEX, BlockConsistencyGroup should belongs to RP and this class is responsible
* for decorating properties of VPLEX volumes protected by RP in RP consistencyGroup.
*
* In case of VPLEX + XIO, BlockConsistencyGroup belongs to VPLEX and it will be decorated with VPLEX
* Virtual volumes.
*
*/
public class BlockVplexCGIngestDecorator extends BlockCGIngestDecorator {
private static final Logger logger = LoggerFactory.getLogger(BlockVplexCGIngestDecorator.class);
@Override
public void decorateCG(BlockConsistencyGroup cg, Collection<BlockObject> associatedObjects,
IngestionRequestContext requestContext, UnManagedVolume unManagedVolume)
throws Exception {
if (null == associatedObjects || associatedObjects.isEmpty()) {
logger.info("No associated BlockObject's found for cg {}", cg.getLabel());
return;
}
for (BlockObject blockObj : associatedObjects) {
StringSetMap systemCGs = cg.getSystemConsistencyGroups();
// No entries yet in the system consistency groups list. That's OK, we'll create it.
if (null == systemCGs || systemCGs.isEmpty()) {
cg.setSystemConsistencyGroups(new StringSetMap());
}
// This volume is not in a CG of this type
if (NullColumnValueGetter.isNullValue(blockObj.getReplicationGroupInstance())) {
logger.info("BlockObject {} doesn't have replicationGroup name {}. No need to set system cg information.",
blockObj.getNativeGuid());
continue;
}
boolean found = false;
// Look through the existing entries in the CG and see if we find a match.
for (Entry<String, AbstractChangeTrackingSet<String>> systemCGEntry : systemCGs.entrySet()) {
if (systemCGEntry.getKey().equalsIgnoreCase(blockObj.getStorageController().toString())) {
if (checkIfCGNameAlreadyExists(systemCGEntry.getValue(), blockObj.getReplicationGroupInstance())) {
logger.info(String.format("Found BlockObject %s,%s system details in cg %s", blockObj.getNativeGuid(),
blockObj.getReplicationGroupInstance(), cg.getLabel()));
found = true;
break;
}
}
}
// If we didn't find this storage:cg combo, let's add it.
if (!found) {
logger.info(String.format("Adding BlockObject %s/%s in CG %s", blockObj.getNativeGuid(),
blockObj.getReplicationGroupInstance(), cg.getLabel()));
if (blockObj instanceof Volume) {
Volume volume = (Volume) blockObj;
if (volume.getAssociatedVolumes() != null
&& volume.getAssociatedVolumes().size() > 1) {
// Since this is a distributed volume, ensure there is a CG entry for each cluster
String cgName = BlockConsistencyGroupUtils.fetchCgName(volume.getReplicationGroupInstance());
cg.addSystemConsistencyGroup(volume.getStorageController().toString(),
BlockConsistencyGroupUtils.buildClusterCgName(BlockConsistencyGroupUtils.CLUSTER_1, cgName));
cg.addSystemConsistencyGroup(volume.getStorageController().toString(),
BlockConsistencyGroupUtils.buildClusterCgName(BlockConsistencyGroupUtils.CLUSTER_2, cgName));
logger.info(String.format("Found BlockObject [%s] is a Distributed VPLEX volume. "
+ "Adding cg entry [%s] for both cluster1 and cluster2.", blockObj.getNativeGuid(), cgName));
} else {
cg.addSystemConsistencyGroup(volume.getStorageController().toString(),
volume.getReplicationGroupInstance());
}
}
}
// VPLEX ingestion process uses the virtual volume's replicationGroupInstance as a transient variable to
// identify CGs along the way so when the BlockConsistencyGroup is created, we can create the correct
// systemConsistencyGroups on the BlockConsistencyGroup.
//
// Now that we've added the proper replicationGroupInstance to the cg's systemConsistencyGroup, we can remove
// the virtual volume's replicationGroupInstance reference. This does not impact the backing volumes.
if (blockObj instanceof Volume) {
Volume volume = (Volume) blockObj;
logger.info(String.format(
"Moving replication group instance name %s on virtual volume %s to backingReplicationGroupInstance",
volume.getReplicationGroupInstance(), volume.getLabel()));
volume.setBackingReplicationGroupInstance(volume.getReplicationGroupInstance());
volume.setReplicationGroupInstance(NullColumnValueGetter.getNullStr());
requestContext.addDataObjectToUpdate(volume, unManagedVolume);
if (volume.getAssociatedVolumes() != null) {
// setting BlockConsistencyGroup on the backend volumes
for (String volumeUriStr : volume.getAssociatedVolumes()) {
BlockObject backendVolume = requestContext.findDataObjectByType(Volume.class, URI.create(volumeUriStr), true);
if (backendVolume != null) {
logger.info("Setting BlockConsistencyGroup {} on VPLEX backend Volume {}",
cg.forDisplay(), backendVolume.forDisplay());
backendVolume.setConsistencyGroup(cg.getId());
// this volume is already set for creation by its parent volume context
// so there's no need to add it into an updated objects collection at this point
}
}
}
}
if (!cg.getTypes().contains(Types.VPLEX.toString())) {
cg.getTypes().add(Types.VPLEX.toString());
}
}
}
@Override
public Collection<BlockObject> getAssociatedObjects(BlockConsistencyGroup cg, Collection<BlockObject> allCGBlockObjects,
IngestionRequestContext requestContext) {
Collection<BlockObject> cgVplexAssocBlockObjects = new ArrayList<BlockObject>();
// Filter in vplex volumes
Iterator<BlockObject> allCGBlockObjectItr = allCGBlockObjects.iterator();
while (allCGBlockObjectItr.hasNext()) {
BlockObject cgBlockObject = allCGBlockObjectItr.next();
if (cgBlockObject instanceof Volume) {
Volume volume = (Volume) cgBlockObject;
if (volume.isVPlexVolume(getDbClient())) {
cgVplexAssocBlockObjects.add(volume);
}
}
}
return cgVplexAssocBlockObjects;
}
/**
* This utility verifies whether cg Name already exists in the list or not.
*
* Since VPLEX Ingestion already populates the cluster & cg name, we don't need to add again here.
*
* @param cgExistingNamesSet
* @param replicationGroupInstance
* @return
*/
private boolean checkIfCGNameAlreadyExists(AbstractChangeTrackingSet<String> cgExistingNamesSet, String replicationGroupInstance) {
if (null != cgExistingNamesSet && !cgExistingNamesSet.isEmpty()) {
for (String existingCgName : cgExistingNamesSet) {
if (existingCgName.contains(replicationGroupInstance)) {
return true;
}
}
}
return false;
}
@Override
public void setNextDecorator(BlockCGIngestDecorator decorator) {
this.nextCGIngestDecorator = decorator;
}
@Override
public boolean isExecuteDecorator(UnManagedVolume umv, IngestionRequestContext requestContext) {
return VolumeIngestionUtil.isRPProtectingVplexVolumes(umv, requestContext, getDbClient()) || VolumeIngestionUtil.isVplexVolume(umv);
}
}