/*
* Copyright (c) 2016 EMC Corporation
* All Rights Reserved
*/
package com.emc.storageos.db.client.upgrade.callbacks;
import java.net.URI;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.emc.storageos.db.client.DbClient;
import com.emc.storageos.db.client.constraint.ContainmentConstraint;
import com.emc.storageos.db.client.constraint.URIQueryResultList;
import com.emc.storageos.db.client.model.DiscoveredDataObject;
import com.emc.storageos.db.client.model.StorageSystem;
import com.emc.storageos.db.client.model.StringSet;
import com.emc.storageos.db.client.model.Volume;
import com.emc.storageos.db.client.upgrade.BaseCustomMigrationCallback;
import com.emc.storageos.db.client.util.NullColumnValueGetter;
import com.emc.storageos.svcs.errorhandling.resources.InternalServerErrorException;
import com.emc.storageos.svcs.errorhandling.resources.MigrationCallbackException;
/**
* If we are upgrading from any version 3.5 or before, the Volume.backingReplicationGroupInstance
* column should be set on VPLEX virtual volumes to match the source side backend volume's
* replicationGroupInstance column.
*
* @author beachn
* @since 3.5+
*/
public class VplexVolumeBackingReplicationGroupInstanceMigration extends BaseCustomMigrationCallback {
private static final Logger logger = LoggerFactory.getLogger(VplexVolumeBackingReplicationGroupInstanceMigration.class);
@Override
public void process() throws MigrationCallbackException {
DbClient dbClient = getDbClient();
int volumeUpdatedCount = 0;
List<URI> vplexUris = new ArrayList<URI>();
List<StorageSystem> vplexes = getAllVplexStorageSystems(dbClient);
for (StorageSystem vplex : vplexes) {
if (null != vplex) {
vplexUris.add(vplex.getId());
}
}
logger.info("found {} vplex storage systems in the database", vplexUris.size());
for (URI vplexUri : vplexUris) {
// fetch all Volumes for this VPLEX URI
URIQueryResultList result = new URIQueryResultList();
dbClient.queryByConstraint(
ContainmentConstraint.Factory.getStorageDeviceVolumeConstraint(vplexUri), result);
Iterator<Volume> volumesIter = dbClient.queryIterativeObjects(Volume.class, result);
while (volumesIter.hasNext()) {
Volume volume = volumesIter.next();
// Must skip VPLEX vols with no backend volumes (ingested case)
if (null == volume.getAssociatedVolumes() || volume.getAssociatedVolumes().isEmpty()) {
logger.warn("VPLEX volume {} has no backend volumes, so cannot update backingReplicationGroupInstance, skipping...",
volume.forDisplay());
continue;
}
// This is a VPLEX volume. If we are upgrading from any version
// 3.5 or before, we should set the new backingReplicationGroupInstance field
// to be the same as the source side backend volume (both sides should be the same,
// but we will default to the source side in an HA situation for consistency).
if (NullColumnValueGetter.isNullValue(volume.getBackingReplicationGroupInstance())) {
Volume sourceSideBackingVolume = getVPLEXBackendVolume(volume, true, dbClient, true);
if (sourceSideBackingVolume != null) {
String instance = sourceSideBackingVolume.getReplicationGroupInstance();
if (NullColumnValueGetter.isNotNullValue(instance)) {
logger.info("updating backingReplicationGroupInstance property on volume {} to {}",
volume.forDisplay(), instance);
volume.setBackingReplicationGroupInstance(instance);
dbClient.updateObject(volume);
volumeUpdatedCount++;
}
}
}
}
}
logger.info("VplexVolumeBackingReplicationGroupInstanceMigration completed, updated backingReplicationGroupInstance on {} volumes",
volumeUpdatedCount);
}
/**
* Returns all VPLEX storage systems in ViPR.
*
* @param dbClient a database client reference
* @return a List of StorageSystems that are "vplex" type
*/
private List<StorageSystem> getAllVplexStorageSystems(DbClient dbClient) {
List<StorageSystem> vplexStorageSystems = new ArrayList<StorageSystem>();
List<URI> allStorageSystemUris = dbClient.queryByType(StorageSystem.class, true);
List<StorageSystem> allStorageSystems = dbClient.queryObject(StorageSystem.class, allStorageSystemUris);
for (StorageSystem storageSystem : allStorageSystems) {
if ((storageSystem != null)
&& (DiscoveredDataObject.Type.vplex.name().equals(storageSystem.getSystemType()))) {
vplexStorageSystems.add(storageSystem);
}
}
return vplexStorageSystems;
}
/**
* NOTE: copied from VPlexUtil, which isn't visible from dbclient.
*
* Returns the source or ha backend volume of the passed VPLEX volume.
*
* @param vplexVolume A reference to the VPLEX volume.
* @param sourceVolume A boolean thats used to return either source
* or ha backend volume.
* @param dbClient an instance of {@link DbClient}
* @param errorIfNotFound A boolean thats used to either return null or throw error
*
* @return A reference to the backend volume
* If sourceVolume is true returns source backend
* volume else returns ha backend volume.
*
*/
private Volume getVPLEXBackendVolume(Volume vplexVolume, boolean sourceVolume, DbClient dbClient, boolean errorIfNotFound) {
StringSet associatedVolumeIds = vplexVolume.getAssociatedVolumes();
Volume backendVolume = null;
if (associatedVolumeIds == null) {
if (errorIfNotFound) {
throw InternalServerErrorException.internalServerErrors
.noAssociatedVolumesForVPLEXVolume(vplexVolume.forDisplay());
} else {
return backendVolume;
}
}
// Get the backend volume either source or ha.
for (String associatedVolumeId : associatedVolumeIds) {
Volume associatedVolume = dbClient.queryObject(Volume.class,
URI.create(associatedVolumeId));
if (associatedVolume != null) {
if (sourceVolume && associatedVolume.getVirtualArray().equals(vplexVolume.getVirtualArray())) {
backendVolume = associatedVolume;
break;
}
if (!sourceVolume && !(associatedVolume.getVirtualArray().equals(vplexVolume.getVirtualArray()))) {
backendVolume = associatedVolume;
break;
}
}
}
return backendVolume;
}
}