/*
* Copyright (c) 2013 EMC Corporation
* All Rights Reserved
*/
package com.emc.storageos.volumecontroller.impl.utils;
import com.emc.storageos.db.client.DbClient;
import com.emc.storageos.db.client.model.*;
import com.emc.storageos.services.util.StorageDriverManager;
import com.emc.storageos.volumecontroller.impl.ControllerServiceImpl;
import com.emc.storageos.volumecontroller.impl.smis.MetaVolumeRecommendation;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.net.URI;
import java.util.List;
public class MetaVolumeUtils {
private static final Logger _log = LoggerFactory.getLogger(MetaVolumeUtils.class);
private static final int BYTESCONVERTER = 1024;
private static final int KB_TO_GB_CONVERTER = 1024 * 1024;
private static final int GB_32kb = 32 * KB_TO_GB_CONVERTER; // 32 GB in KB
private static final int GB_500kb = 500 * KB_TO_GB_CONVERTER; // 500 GB in KB
private static final int GB_1024kb = 1024 * KB_TO_GB_CONVERTER; // 1024 GB in KB
private static final int GB_240kb = 240 * KB_TO_GB_CONVERTER; // 240 GB in KB
private static StorageDriverManager driverManager = null;
public static StorageDriverManager getDriverManager() {
if (driverManager == null) {
// Cannot get this bean from ControllerServiceImpl context,
// since ControllerServiceImpl is not loaded by spring in apisvc (it is passed in ZK). The context is null.
driverManager = ((StorageDriverManager.getApplicationContext() != null) ?
(StorageDriverManager)StorageDriverManager.getApplicationContext().getBean("storageDriverManager") :
null);
}
return driverManager;
}
public static void prepareMetaVolumes(List<Volume> volumes, long metaMemberSize, long metaMemberCount, String metaType,
DbClient dbClient) {
// Set meta volume data in all volumes
for (Volume volume : volumes) {
prepareMetaVolume(volume, metaMemberSize, metaMemberCount, metaType, dbClient);
}
}
public static void prepareMetaVolume(Volume volume, long metaMemberSize, long metaMemberCount, String metaType, DbClient dbClient) {
// Set meta volume data in the volume
volume.setIsComposite(true);
volume.setCompositionType(metaType);
volume.setMetaMemberSize(metaMemberSize);
volume.setMetaMemberCount((int) metaMemberCount);
volume.setTotalMetaMemberCapacity(metaMemberSize * metaMemberCount);
dbClient.persistObject(volume);
}
/**
* Helper method to return if a given storage volume should be created as meta volume.
*
* @param volumeURI
* @param dbClient
* @return true/false
*/
public static boolean createAsMetaVolume(URI volumeURI, DbClient dbClient, VirtualPoolCapabilityValuesWrapper capabilities) {
boolean createAsMetaVolume = false;
// Check if volumes have to be created as meta volumes
Volume volume = dbClient.queryObject(Volume.class, volumeURI);
VirtualPool vPool = dbClient.queryObject(VirtualPool.class, volume.getVirtualPool());
StorageSystem storageSystem = dbClient.queryObject(StorageSystem.class, volume.getStorageController());
StoragePool storagePool = dbClient.queryObject(StoragePool.class, volume.getPool());
MetaVolumeRecommendation recommendation = MetaVolumeUtils.getCreateRecommendation(storageSystem, storagePool,
volume.getCapacity(), volume.getThinlyProvisioned(), vPool.getFastExpansion(), capabilities);
if (recommendation.isCreateMetaVolumes()) {
createAsMetaVolume = true;
}
return createAsMetaVolume;
}
/**
* Returns recommendation about use of meta volumes.
* The recommendation includes if meta volumes should be used, meta member count, meta member capacity and meta volume type.
*
* @param storageSystem storage system
* @param storagePool storage pool in which volume should be created
* @param capacity required size of volume
* @param isThinlyProvisioned
* @param fastExpansion if fast volume expansion is required
* @return recommendation
*/
public static MetaVolumeRecommendation getCreateRecommendation(StorageSystem storageSystem, StoragePool storagePool, long capacity,
boolean isThinlyProvisioned,
boolean fastExpansion, VirtualPoolCapabilityValuesWrapper capabilities) {
_log.info(String.format(
"Create recommendation for use of meta volumes: Storage type: %s, \n capacity: %s, isThinlyProvisioned: %s, " +
"fastExpansion: %s ", storageSystem.getSystemType(), capacity, isThinlyProvisioned, fastExpansion));
MetaVolumeRecommendation recommendation = new MetaVolumeRecommendation();
// For driver managed system we use regular volumes.
if (getDriverManager() != null && getDriverManager().isDriverManaged(storageSystem.getSystemType())) {
recommendation.setCreateMetaVolumes(false);
_log.info(String.format("Volume Create Recommendation (system type: %s): Use meta volumes: %s", storageSystem.getSystemType(),
recommendation.isCreateMetaVolumes()));
return recommendation;
}
// When capabilities indicate that a volume is meta volume, create recommendation based on meta volume data in capabilities.
// This is used to create srdf target with meta structure identical to the srdf source volume.
if (capabilities != null && capabilities.getIsMetaVolume()) {
recommendation.setCreateMetaVolumes(true);
if (capabilities.getMetaVolumeType().equalsIgnoreCase(Volume.CompositionType.CONCATENATED.toString())) {
recommendation.setMetaVolumeType(Volume.CompositionType.CONCATENATED);
} else if (capabilities.getMetaVolumeType().equalsIgnoreCase(Volume.CompositionType.STRIPED.toString())) {
recommendation.setMetaVolumeType(Volume.CompositionType.STRIPED);
}
recommendation.setMetaMemberSize(capabilities.getMetaVolumeMemberSize());
recommendation.setMetaMemberCount(capabilities.getMetaVolumeMemberCount());
_log.info(String
.format("Volume Create Recommendation (based on capabilities data): Use meta volumes: %s, Member Count: %s, Member size: %s, Meta type: %s",
recommendation.isCreateMetaVolumes(), recommendation.getMetaMemberCount(),
recommendation.getMetaMemberSize(), recommendation.getMetaVolumeType()));
return recommendation;
}
if (null == storagePool.getPoolClassName()
|| storagePool.getPoolClassName().equalsIgnoreCase(StoragePool.PoolClassNames.Symm_SRPStoragePool.toString())
|| storagePool.getPoolClassName().equalsIgnoreCase(StoragePool.PoolClassNames.IBMTSDS_VirtualPool.toString())
|| storagePool.getPoolClassName().equalsIgnoreCase(StoragePool.PoolClassNames.Clar_UnifiedStoragePool.toString())
|| storagePool.getPoolClassName().equalsIgnoreCase(StoragePool.PoolClassNames.VNXe_Pool.name())) {
// If the pool class name is not set, then it's not necessary to do MetaVolumes
// meta volumes are not supported for clarion volumes created in Unified pools, and not supported in VNXe either.
recommendation.setCreateMetaVolumes(false);
_log.info(String.format("Volume Create Recommendation: Use meta volumes: %s",
recommendation.isCreateMetaVolumes()));
return recommendation;
}
// On VMAX arrays only concatenated meta volumes can be expanded fast.
// If fast expansion is required use concatenated, otherwise use striped.
// On VNX we always use striped. No penalty for striped expansion on VNX (is this true?).
Volume.CompositionType metaVolumeType;
if (fastExpansion && storageSystem.getSystemType().equals(StorageSystem.Type.vmax.toString())) {
metaVolumeType = Volume.CompositionType.CONCATENATED;
} else {
metaVolumeType = Volume.CompositionType.STRIPED;
}
_log.info(String.format(
"Regular volume capacity limits for storage pool %s, %n max thin volume capacity: %s, max thick volume capacity: %s ",
storagePool.getId(), storagePool.getMaximumThinVolumeSize(), storagePool.getMaximumThickVolumeSize()));
// Get storage pool setting for maximum supported regular volume size.
// Typically this size is 240GB, but it can be different on VMAX if auto meta configuration is enabled and minimum meta volume size
// is defined there
// less than 240GB.
long regularVolumeSizeLimitKb = isThinlyProvisioned ?
storagePool.getMaximumThinVolumeSize() : storagePool.getMaximumThickVolumeSize();
_log.info(String.format("Regular volume size limit for storage pool: %s KB", regularVolumeSizeLimitKb));
long capacityKb = (capacity % BYTESCONVERTER == 0) ? capacity / BYTESCONVERTER : capacity / BYTESCONVERTER + 1;
// TODO temporary. remove later
// regularVolumeSizeLimitKb = 1048576 + 10; // 1GB + 10 kb
// Check if we need to use meta volumes.
// Use meta volumes when capacity is large than maximum regular volume size limit.
// This is a common case.
boolean useMetaVolumes = false;
long metaMemberCount = 1;
if (capacityKb >= regularVolumeSizeLimitKb) {
useMetaVolumes = true;
// Calculate meta member count
metaMemberCount = (capacityKb % regularVolumeSizeLimitKb == 0) ?
capacityKb / regularVolumeSizeLimitKb : capacityKb / regularVolumeSizeLimitKb + 1;
}
// For VMAX striped meta volumes, member count calculation is done differently
// For thin striped volumes use 8 way meta volumes starting from 240GB/regularVolumeSizeLimit volumes size and up
// to max possible limit available with 8 members.
// SAP asked to use 16 and 32 members for thin striped volumes after we exhausted capacity with 8 members.
// I do not see any issues with this and I added this support.
if (storageSystem.getSystemType().equals(StorageSystem.Type.vmax.toString()) &&
metaVolumeType == Volume.CompositionType.STRIPED) {
long maxLimitWith16Members = 16 * regularVolumeSizeLimitKb;
long maxLimitWith32Members = 32 * regularVolumeSizeLimitKb;
if (isThinlyProvisioned) { // create 8-way meta up to max possible size
if (capacityKb >= regularVolumeSizeLimitKb && capacityKb < 8 * regularVolumeSizeLimitKb) {
metaMemberCount = 8;
useMetaVolumes = true;
} else if (capacityKb >= 8 * regularVolumeSizeLimitKb && capacityKb < maxLimitWith16Members) {
metaMemberCount = 16;
useMetaVolumes = true;
} else if (capacityKb >= maxLimitWith16Members && capacityKb < maxLimitWith32Members) {
metaMemberCount = 32;
useMetaVolumes = true;
}
} else { // thick striped meta, use the following rules
// Current rules are to use 4 way meta for volumes between 32GB and 500GB, 8 way meta for volumes between 500GB and 1024GB,
// 16 way meta for volumes from 1024 and up. After that increase meta members count as needed according to max. regular
// volume size limit.
// On VMAX with auto meta configuration enabled, regular volume size limit can be small and we need to account this to make
// sure we do not create
// members bigger than auto meta limit.
// SAP asked to continue with 32 members after we exhausted 16 member capacity. I do not see any issues with
// this and I added this support.
long minimumMetaSizeLimit = (GB_32kb < regularVolumeSizeLimitKb) ? GB_32kb : regularVolumeSizeLimitKb;
long maxLimitWith4Members = (GB_500kb < 4 * regularVolumeSizeLimitKb) ? GB_500kb : 4 * regularVolumeSizeLimitKb;
long maxLimitWith8Members = (GB_1024kb < 8 * regularVolumeSizeLimitKb) ? GB_1024kb : 8 * regularVolumeSizeLimitKb;
if (capacityKb >= minimumMetaSizeLimit && capacityKb < maxLimitWith4Members) {
metaMemberCount = 4;
useMetaVolumes = true;
} else if (capacityKb >= maxLimitWith4Members && capacityKb < maxLimitWith8Members) {
metaMemberCount = 8;
useMetaVolumes = true;
} else if (capacityKb >= maxLimitWith8Members && capacityKb < maxLimitWith16Members) {
metaMemberCount = 16;
useMetaVolumes = true;
} else if (capacityKb >= maxLimitWith16Members && capacityKb < maxLimitWith32Members) {
metaMemberCount = 32;
useMetaVolumes = true;
}
}
}
if (useMetaVolumes) {
long metaMemberSize = (capacity % metaMemberCount == 0) ?
capacity / metaMemberCount : capacity / metaMemberCount + 1;
recommendation.setCreateMetaVolumes(true);
recommendation.setMetaVolumeType(metaVolumeType);
recommendation.setMetaMemberSize(metaMemberSize);
recommendation.setMetaMemberCount(metaMemberCount);
_log.info(String.format("Volume Create Recommendation: Use meta volumes: %s, Member Count: %s, Member size: %s, Meta type: %s",
recommendation.isCreateMetaVolumes(), recommendation.getMetaMemberCount(),
recommendation.getMetaMemberSize(), recommendation.getMetaVolumeType()));
} else {
recommendation.setCreateMetaVolumes(false);
_log.info(String.format("Volume Create Recommendation: Use meta volumes: %s", recommendation.isCreateMetaVolumes()));
}
return recommendation;
}
/**
* Returns recommendation about volume extension.
* The recommendation includes if meta volumes should be used, meta member count, meta member capacity and meta volume type.
*
* @param storageSystem storage system
* @param storagePool storage pool in which volume should be created
* @param capacity current size
* @param newCapacity new required size
* @param metaMemberSize size of meta members used for expansion
* @param fastExpansion if fast volume expansion is required
* @return recommendation
*/
public static MetaVolumeRecommendation getExpandRecommendation(StorageSystem storageSystem, StoragePool storagePool, long capacity,
long newCapacity, long metaMemberSize, boolean isThinlyProvisioned,
boolean fastExpansion) {
_log.info(String
.format(
"Create recommendation for volume expansion: Storage type: %s, \n capacity: %s, new capacity: %s, \n isThinlyProvisioned: %s, "
+
"metaMemeberSize: %s, fastExpansion: %s ", storageSystem.getSystemType(), capacity, newCapacity,
isThinlyProvisioned,
metaMemberSize, fastExpansion));
MetaVolumeRecommendation recommendation = new MetaVolumeRecommendation();
if (getDriverManager() != null && getDriverManager().isDriverManaged(storageSystem.getSystemType())) {
recommendation.setCreateMetaVolumes(false);
_log.info(String.format("Volume Expand Recommendation (system type: %s): Use meta volumes: %s", storageSystem.getSystemType(),
recommendation.isCreateMetaVolumes()));
return recommendation;
}
if (storageSystem.checkIfVmax3()) {
recommendation.setCreateMetaVolumes(false);
_log.info(String.format("Volume Expand Recommendation (VMAX3): Use meta volumes: %s", recommendation.isCreateMetaVolumes()));
return recommendation;
}
if (storageSystem.getSystemType().equals(StorageSystem.Type.xtremio.name())) {
recommendation.setCreateMetaVolumes(false);
_log.info(String.format("Volume Expand Recommendation: Use meta volumes: %s", recommendation.isCreateMetaVolumes()));
return recommendation;
}
// For Clarion Unified pool and VNXe pool extension can be done only as regular volume
if (null != storagePool.getPoolClassName()
&& (storagePool.getPoolClassName().equalsIgnoreCase(StoragePool.PoolClassNames.Clar_UnifiedStoragePool.toString())
|| storagePool.getPoolClassName().equalsIgnoreCase(StoragePool.PoolClassNames.VNXe_Pool.toString()))) {
recommendation.setCreateMetaVolumes(false);
_log.info(String.format("Volume Expand Recommendation: Use meta volumes: %s", recommendation.isCreateMetaVolumes()));
return recommendation;
}
// For Hitachi thin volume expansion can be done as regular volume
if (isThinlyProvisioned && storageSystem.getSystemType().equals(StorageSystem.Type.hds.name())) {
recommendation.setCreateMetaVolumes(false);
_log.info(String.format("Hitachi Volume Expand Recommendation: Use meta volumes: %s", recommendation.isCreateMetaVolumes()));
return recommendation;
}
// For Openstack, volume expansion can be done as regular volume
if (StorageSystem.Type.openstack.name().equalsIgnoreCase(storageSystem.getSystemType())) {
recommendation.setCreateMetaVolumes(false);
_log.info(String.format("Openstack Volume Expand Recommendation: Use meta volumes: %s", recommendation.isCreateMetaVolumes()));
return recommendation;
}
if (storageSystem.getSystemType().equals(StorageSystem.Type.scaleio.name())) {
recommendation.setCreateMetaVolumes(false);
_log.info(String.format("ScaleIO Volume Expand Recommendation: Use meta volumes: %s", recommendation.isCreateMetaVolumes()));
return recommendation;
}
// For IBM XIV, volume expansion can be done as regular volume
if (StorageSystem.Type.ibmxiv.name().equalsIgnoreCase(storageSystem.getSystemType())) {
recommendation.setCreateMetaVolumes(false);
_log.info(String.format("IBM XIV Volume Expand Recommendation: Use meta volumes: %s", recommendation.isCreateMetaVolumes()));
return recommendation;
}
if (storageSystem.getSystemType().equals(StorageSystem.Type.ceph.name())) {
recommendation.setCreateMetaVolumes(false);
_log.info(String.format("Ceph Volume Expand Recommendation: Use meta volumes: %s", recommendation.isCreateMetaVolumes()));
return recommendation;
}
// For all other volume types we have to use meta volumes for extension
recommendation.setCreateMetaVolumes(true);
// Set meta volume type.
// On VMAX arrays only concatenated meta volumes can be expanded fast
// Use concatenated volumes for fast expansion on VMAX. When fast expansion is not requested, use striped type on VMAX.
if (fastExpansion && storageSystem.getSystemType().equals(StorageSystem.Type.vmax.toString())) {
recommendation.setMetaVolumeType(Volume.CompositionType.CONCATENATED);
} else {
recommendation.setMetaVolumeType(Volume.CompositionType.STRIPED);
}
// Based on performance recommendations: On VNX use striped for thick meta volume and concatenated for thin meta volumes.
// On VNX only thick volumes (concrete pool volumes) can be meta volumes.
// All types of VNX volumes can be expanded fast (is this true?).
if (storageSystem.getSystemType().equals(StorageSystem.Type.vnxblock.toString())) {
Volume.CompositionType metaType = isThinlyProvisioned ? Volume.CompositionType.CONCATENATED : Volume.CompositionType.STRIPED;
recommendation.setMetaVolumeType(metaType);
}
// For Hitachi, all thick volumes are expanded as meta and they are concatenated.
// As per the Hitachi documentation, if a volume is exported then the data is safe when we expand, else data will be lost.
if (StorageSystem.Type.hds.name().equals(storageSystem.getSystemType()) && !isThinlyProvisioned) {
recommendation.setMetaVolumeType(Volume.CompositionType.CONCATENATED);
}
// Check if capacity bigger than newCapacity. In this case no expansion is required.
if (capacity >= newCapacity) {
recommendation.setMetaMemberSize(0);
recommendation.setMetaMemberCount(0);
} else {
// Calculate number of meta members for extension
long extensionSize = newCapacity - capacity;
long metaMemberCount = (extensionSize % metaMemberSize == 0) ?
extensionSize / metaMemberSize : extensionSize / metaMemberSize + 1;
recommendation.setMetaMemberSize(metaMemberSize);
recommendation.setMetaMemberCount(metaMemberCount);
}
_log.info(String.format("Volume Expand Recommendation: Use meta volumes: %s, Member Count: %s, Member size: %s, Meta type: %s",
recommendation.isCreateMetaVolumes(), recommendation.getMetaMemberCount(),
recommendation.getMetaMemberSize(), recommendation.getMetaVolumeType()));
return recommendation;
}
}