/*
* Copyright 2012 EMC Corporation
* Copyright 2016 Intel Corporation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package com.emc.storageos.api.service.impl.resource;
import static com.emc.storageos.api.mapper.DbObjectMapper.toNamedRelatedResource;
import static com.emc.storageos.api.mapper.VirtualPoolMapper.toBlockVirtualPool;
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.Set;
import javax.ws.rs.Consumes;
import javax.ws.rs.DefaultValue;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import org.springframework.util.CollectionUtils;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.emc.storageos.api.service.impl.placement.VirtualPoolUtil;
import com.emc.storageos.api.service.impl.resource.cinder.QosService;
import com.emc.storageos.api.service.impl.response.BulkList;
import com.emc.storageos.db.client.DbClient;
import com.emc.storageos.db.client.URIUtil;
import com.emc.storageos.db.client.constraint.ContainmentConstraint;
import com.emc.storageos.db.client.constraint.URIQueryResultList;
import com.emc.storageos.db.client.model.NamedURI;
import com.emc.storageos.db.client.model.QosSpecification;
import com.emc.storageos.db.client.model.StoragePool;
import com.emc.storageos.db.client.model.StringMap;
import com.emc.storageos.db.client.model.StringSet;
import com.emc.storageos.db.client.model.StringSetMap;
import com.emc.storageos.db.client.model.VirtualArray;
import com.emc.storageos.db.client.model.VirtualPool;
import com.emc.storageos.db.client.model.VirtualPool.Type;
import com.emc.storageos.db.client.model.Volume;
import com.emc.storageos.db.client.model.VpoolProtectionVarraySettings;
import com.emc.storageos.db.client.model.VpoolRemoteCopyProtectionSettings;
import com.emc.storageos.db.client.model.VpoolRemoteCopyProtectionSettings.CopyModes;
import com.emc.storageos.db.client.util.NullColumnValueGetter;
import com.emc.storageos.db.common.VdcUtil;
import com.emc.storageos.db.exceptions.DatabaseException;
import com.emc.storageos.model.BulkIdParam;
import com.emc.storageos.model.ResourceTypeEnum;
import com.emc.storageos.model.auth.ACLAssignmentChanges;
import com.emc.storageos.model.auth.ACLAssignments;
import com.emc.storageos.model.pools.StoragePoolList;
import com.emc.storageos.model.quota.QuotaInfo;
import com.emc.storageos.model.quota.QuotaUpdateParam;
import com.emc.storageos.model.vpool.BlockVirtualPoolBulkRep;
import com.emc.storageos.model.vpool.BlockVirtualPoolParam;
import com.emc.storageos.model.vpool.BlockVirtualPoolProtectionUpdateParam;
import com.emc.storageos.model.vpool.BlockVirtualPoolRestRep;
import com.emc.storageos.model.vpool.BlockVirtualPoolUpdateParam;
import com.emc.storageos.model.vpool.CapacityResponse;
import com.emc.storageos.model.vpool.ProtectionSourcePolicy;
import com.emc.storageos.model.vpool.VirtualPoolChangeList;
import com.emc.storageos.model.vpool.VirtualPoolHighAvailabilityParam;
import com.emc.storageos.model.vpool.VirtualPoolHighAvailabilityParam.VirtualArrayVirtualPoolMapEntry;
import com.emc.storageos.model.vpool.VirtualPoolList;
import com.emc.storageos.model.vpool.VirtualPoolPoolUpdateParam;
import com.emc.storageos.model.vpool.VirtualPoolProtectionMirrorParam;
import com.emc.storageos.model.vpool.VirtualPoolProtectionVirtualArraySettingsParam;
import com.emc.storageos.model.vpool.VirtualPoolRemoteProtectionVirtualArraySettingsParam;
import com.emc.storageos.protectioncontroller.impl.recoverpoint.RPHelper;
import com.emc.storageos.security.authorization.ACL;
import com.emc.storageos.security.authorization.CheckPermission;
import com.emc.storageos.security.authorization.DefaultPermissions;
import com.emc.storageos.security.authorization.Role;
import com.emc.storageos.security.geo.GeoServiceClient;
import com.emc.storageos.services.OperationTypeEnum;
import com.emc.storageos.svcs.errorhandling.resources.APIException;
import com.emc.storageos.volumecontroller.AttributeMatcher;
import com.emc.storageos.volumecontroller.impl.smis.srdf.SRDFUtils;
import com.emc.storageos.volumecontroller.impl.utils.ImplicitPoolMatcher;
import com.emc.storageos.volumecontroller.impl.utils.ImplicitUnManagedObjectsMatcher;
import com.emc.storageos.volumecontroller.impl.utils.VirtualPoolCapabilityValuesWrapper;
import com.google.common.base.Function;
@Path("/block/vpools")
@DefaultPermissions(readRoles = { Role.SYSTEM_ADMIN, Role.SYSTEM_MONITOR },
readAcls = { ACL.USE },
writeRoles = { Role.SYSTEM_ADMIN, Role.RESTRICTED_SYSTEM_ADMIN })
public class BlockVirtualPoolService extends VirtualPoolService {
private static final Logger _log = LoggerFactory.getLogger(BlockVirtualPoolService.class);
private static final String NONE = "NONE";
/**
* Returns all potential virtual pools, which supported the given virtual pool change operation
* for a virtual pool change of the volumes specified in the request
*
* @prereq none
*
* @param param
*
* @brief Show potential virtual pools
* @return A VirtualPoolChangeList that identifies each potential virtual
* pool, whether or not a change is allowed for the virtual pool,
* and if not, the reason why.
*/
@POST
@Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
@Path("/{id}/vpool-change/vpool")
@CheckPermission(roles = { Role.SYSTEM_MONITOR, Role.TENANT_ADMIN }, acls = { ACL.OWN, ACL.ALL })
public VirtualPoolChangeList getVirtualPoolForVirtualPoolChange(@PathParam("id") URI id, BulkIdParam param) {
VirtualPool vpool = getVirtualPool(VirtualPool.Type.block, id);
ArgValidator.checkFieldNotEmpty(param.getIds(), "volume_id");
// We only need one volume from the current vpool to determine
// which other vpools we can move to.
Volume volume = _dbClient.queryObject(Volume.class, param.getIds().get(0));
VirtualPoolChangeList virtualPoolChangeList = new VirtualPoolChangeList();
if (volume != null) {
if (!volume.getVirtualPool().equals(id)) {
throw APIException.badRequests.volumeNotInVirtualPool(volume.getLabel(), vpool.getLabel());
}
// Get the block service implementation for this volume.
BlockServiceApi blockServiceApi = BlockService.getBlockServiceImpl(volume, _dbClient);
_log.info("Got BlockServiceApi for volume, now checking for vpool change candidates...");
// Return the list of candidate VirtualPools for a VirtualPool change for this volume.
VirtualPoolChangeList volumeVirturalPoolChangeList = blockServiceApi.getVirtualPoolForVirtualPoolChange(volume);
virtualPoolChangeList.getVirtualPools().addAll(volumeVirturalPoolChangeList.getVirtualPools());
}
return virtualPoolChangeList;
}
/**
* Creates a block store virtual pool
*
* @prereq none
* @param param VirtualPool parameters
* @brief Create block store virtual pool
* @return VirtualPool details
* @throws Exception
*/
@POST
@Consumes({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
@CheckPermission(roles = { Role.SYSTEM_ADMIN, Role.RESTRICTED_SYSTEM_ADMIN })
public BlockVirtualPoolRestRep createBlockVirtualPool(BlockVirtualPoolParam param)
throws DatabaseException {
ArgValidator.checkFieldNotEmpty(param.getName(), VPOOL_NAME);
checkForDuplicateName(param.getName(), VirtualPool.class);
ArgValidator.checkFieldNotEmpty(param.getDescription(), VPOOL_DESCRIPTION);
VirtualPoolUtil.validateBlockVirtualPoolCreateParams(param, _dbClient);
Map<URI, VpoolRemoteCopyProtectionSettings> remoteSettingsMap = new HashMap<URI, VpoolRemoteCopyProtectionSettings>();
List<VpoolProtectionVarraySettings> protectionSettings = new ArrayList<VpoolProtectionVarraySettings>();
Map<URI, VpoolProtectionVarraySettings> protectionSettingsMap = new HashMap<URI, VpoolProtectionVarraySettings>();
VirtualPool vpool = prepareVirtualPool(param, remoteSettingsMap, protectionSettingsMap, protectionSettings);
// Set the underlying protection setting objects
if (!protectionSettings.isEmpty()) {
_dbClient.createObject(protectionSettings);
}
if (!remoteSettingsMap.isEmpty()) {
_dbClient.createObject(new ArrayList(remoteSettingsMap.values()));
}
StringBuffer errorMessage = new StringBuffer();
// update the implicit pools matching with this VirtualPool.
ImplicitPoolMatcher.matchVirtualPoolWithAllStoragePools(vpool, _dbClient, _coordinator, errorMessage);
Set<URI> allSrdfTargetVPools = SRDFUtils.fetchSRDFTargetVirtualPools(_dbClient);
Set<URI> allRpTargetVPools = RPHelper.fetchRPTargetVirtualPools(_dbClient);
if (null != vpool.getMatchedStoragePools() || null != vpool.getInvalidMatchedPools()) {
ImplicitUnManagedObjectsMatcher.matchVirtualPoolsWithUnManagedVolumes(vpool, allSrdfTargetVPools, allRpTargetVPools, _dbClient, true);
}
_dbClient.createObject(vpool);
// Creates a new QoS object in DB based on data from given Virtual Pool
QosService.createQosSpecification(vpool, _dbClient);
recordOperation(OperationTypeEnum.CREATE_VPOOL, VPOOL_CREATED_DESCRIPTION, vpool);
return toBlockVirtualPool(_dbClient, vpool, VirtualPool.getProtectionSettings(vpool, _dbClient),
VirtualPool.getRemoteProtectionSettings(vpool, _dbClient));
}
/**
* Return the matching pools for a given set of VirtualPool attributes.
* This API is useful for user to find the matching pools before creating a VirtualPool.
*
* @prereq none
* @param param : VirtualPoolAttributeParam
* @brief List matching pools for virtual pool properties
* @return matching pools.
*/
@POST
@Consumes({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
@Path("/matching-pools")
@CheckPermission(roles = { Role.SYSTEM_ADMIN, Role.RESTRICTED_SYSTEM_ADMIN })
public StoragePoolList getMatchingPoolsForVirtualPoolAttributes(BlockVirtualPoolParam param) {
StoragePoolList poolList = new StoragePoolList();
Map<URI, VpoolRemoteCopyProtectionSettings> remoteSettingsMap = new HashMap<URI, VpoolRemoteCopyProtectionSettings>();
List<VpoolProtectionVarraySettings> protectionSettings = new ArrayList<VpoolProtectionVarraySettings>();
Map<URI, VpoolProtectionVarraySettings> protectionSettingsMap = new HashMap<URI, VpoolProtectionVarraySettings>();
VirtualPool vpool = prepareVirtualPool(param, remoteSettingsMap, protectionSettingsMap, protectionSettings);
List<URI> storagePoolURIs = _dbClient.queryByType(StoragePool.class, true);
List<StoragePool> allPools = _dbClient.queryObject(StoragePool.class, storagePoolURIs);
StringBuffer errorMessage = new StringBuffer();
List<StoragePool> matchedPools = ImplicitPoolMatcher.getMatchedPoolWithStoragePools(vpool, allPools,
protectionSettingsMap,
remoteSettingsMap,
null,
_dbClient, _coordinator, AttributeMatcher.VPOOL_MATCHERS, errorMessage);
for (StoragePool pool : matchedPools) {
poolList.getPools().add(toNamedRelatedResource(pool, pool.getNativeGuid()));
}
return poolList;
}
/**
* List the virtual pools for Block Store
*
* @prereq none
* @brief List virtual pools for block store
* @return Returns the VirtualPool user is authorized to see
*/
@GET
@Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
public VirtualPoolList getBlockVirtualPool(
@DefaultValue("") @QueryParam(TENANT_ID_QUERY_PARAM) String tenantId,
@DefaultValue("") @QueryParam(VDC_ID_QUERY_PARAM) String shortVdcId) {
_geoHelper.verifyVdcId(shortVdcId);
return getVirtualPoolList(VirtualPool.Type.block, shortVdcId, tenantId);
}
/**
* Get info for block store virtual pool
*
* @prereq none
* @param id the URN of a ViPR VirtualPool
* @brief Show block store virtual pool
* @return VirtualPool details
*/
@GET
@Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
@Path("/{id}")
@CheckPermission(roles = { Role.SYSTEM_ADMIN, Role.SYSTEM_MONITOR }, acls = { ACL.USE })
public BlockVirtualPoolRestRep getVirtualPool(@PathParam("id") URI id) {
VirtualPool vpool = getVirtualPool(VirtualPool.Type.block, id);
BlockVirtualPoolRestRep restRep = getBlockVirtualPoolWithProtection(vpool);
restRep.setNumResources(getNumResources(vpool, _dbClient));
return restRep;
}
protected BlockVirtualPoolRestRep getBlockVirtualPoolWithProtection(VirtualPool vpool) {
return toBlockVirtualPool(_dbClient, vpool, VirtualPool.getProtectionSettings(vpool, _dbClient),
VirtualPool.getRemoteProtectionSettings(vpool, _dbClient));
}
/**
* Deactivate block store virtual pool, this will move the virtual pool to a "marked-for-deletion" state,
* and no more resource may be created using it.
* The virtual pool will be deleted when all references to this virtual pool of type Volume are deleted
*
* @prereq Dependent resources such as volumes and snapshots must be deleted
* @param id the URN of a ViPR VirtualPool
* @brief Delete block store virtual pool
* @return No data returned in response body
*/
@POST
@Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
@Path("/{id}/deactivate")
@CheckPermission(roles = { Role.SYSTEM_ADMIN, Role.RESTRICTED_SYSTEM_ADMIN })
public Response deleteBlockVirtualPool(@PathParam("id") URI id) {
return deleteVirtualPool(VirtualPool.Type.block, id);
}
/**
* Get block store virtual pool ACL
*
* @prereq none
* @param id the URN of a ViPR VirtualPool
* @brief Show ACL assignment for block store virtual pool
* @return ACL Assignment details
*/
@GET
@Path("/{id}/acl")
@Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
@CheckPermission(roles = { Role.SECURITY_ADMIN, Role.SYSTEM_ADMIN, Role.SYSTEM_MONITOR })
public ACLAssignments getAcls(@PathParam("id") URI id) {
return getAclsOnVirtualPool(VirtualPool.Type.block, id);
}
/**
* Add or remove individual block store virtual pool ACL entry(s). Request body must include at least one add or remove operation.
*
* @prereq none
* @param id the URN of a ViPR VirtualPool
* @param changes ACL assignment changes
* @brief Add or remove block store virtual pool ACL entries
* @return No data returned in response body
*/
@PUT
@Path("/{id}/acl")
@Consumes({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
@CheckPermission(roles = { Role.SECURITY_ADMIN, Role.SYSTEM_ADMIN, Role.RESTRICTED_SYSTEM_ADMIN }, blockProxies = true)
public ACLAssignments updateAcls(@PathParam("id") URI id,
ACLAssignmentChanges changes) {
return updateAclsOnVirtualPool(VirtualPool.Type.block, id, changes);
}
/**
* Returns list of computed id's for all storage pools matching with the virtual pool.
* This list of pools will be used when creating volumes.
*
* @prereq none
* @param id the URN of a ViPR VirtualPool.
*
* @brief List storage pool ids matching the virtual pool
* @return The ids for all storage pools that satisfy the VirtualPool.
*/
@GET
@Path("/{id}/storage-pools")
@Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
@CheckPermission(roles = { Role.SYSTEM_ADMIN, Role.SYSTEM_MONITOR })
public StoragePoolList getStoragePools(@PathParam("id") URI id) {
return getStoragePoolsForVirtualPool(id);
}
/**
* This method re-computes the matched pools for this virtual pool and returns this information.
*
* Where as getStoragePools {id}/storage-pools returns whatever is already computed, for matched pools.
*
* @prereq none
* @param id : the URN of a ViPR Block VirtualPool
* @brief Refresh list of storage pools matching the virtual pool
* @return : List of Pool Ids matching with this VirtualPool.
*/
@GET
@Path("/{id}/refresh-matched-pools")
@Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
@CheckPermission(roles = { Role.SYSTEM_ADMIN, Role.RESTRICTED_SYSTEM_ADMIN })
public StoragePoolList refreshMatchedStoragePools(@PathParam("id") URI id) {
return refreshMatchedPools(VirtualPool.Type.block, id);
}
/**
* The block virtual pool can be modified only if there are no associated resources.
*
* @prereq No associated resources such as volumes or snapshots should exist
* @param param VirtualPool parameters
* @brief Update block store virtual pool
* @return VirtualPool details
*/
@PUT
@Consumes({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
@Path("/{id}")
@Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
@CheckPermission(roles = { Role.SYSTEM_ADMIN, Role.RESTRICTED_SYSTEM_ADMIN })
public BlockVirtualPoolRestRep updateBlockVirtualPool(@PathParam("id") URI id, BlockVirtualPoolUpdateParam param) {
VirtualPool vpool = null;
ArgValidator.checkFieldUriType(id, VirtualPool.class, "id");
vpool = _dbClient.queryObject(VirtualPool.class, id);
ArgValidator.checkEntity(vpool, id, isIdEmbeddedInURL(id));
if (!vpool.getType().equals(VirtualPool.Type.block.name())) {
throw APIException.badRequests.providedVirtualPoolNotCorrectType();
}
// Get the QoS for the VirtualPool, otherwise throw exception.
QosSpecification qosSpecification = QosService.getQos(vpool.getId(), _dbClient);
URIQueryResultList resultList = new URIQueryResultList();
_dbClient.queryByConstraint(
ContainmentConstraint.Factory.getVirtualPoolVolumeConstraint(id), resultList);
boolean isActiveVolumePartOfPool = false;
for (URI uri : resultList) {
Volume volume = _dbClient.queryObject(Volume.class, uri);
if (!volume.getInactive()) {
isActiveVolumePartOfPool = true;
break;
}
}
if (isActiveVolumePartOfPool && checkAttributeValuesChanged(param, vpool)) {
throw APIException.badRequests.updateVirtualPoolOnlyAllowedToChange();
}
// set common VirtualPool update parameters here.
populateCommonVirtualPoolUpdateParams(vpool, param);
if (null != param.getSystemType()) {
if (vpool.getArrayInfo() == null) {
vpool.setArrayInfo(new StringSetMap());
}
if (vpool.getArrayInfo().containsKey(VirtualPoolCapabilityValuesWrapper.SYSTEM_TYPE)) {
for (String systemType : vpool.getArrayInfo().get(VirtualPoolCapabilityValuesWrapper.SYSTEM_TYPE)) {
vpool.getArrayInfo().remove(VirtualPoolCapabilityValuesWrapper.SYSTEM_TYPE, systemType);
}
}
if (!(VirtualPool.SystemType.NONE.name().equalsIgnoreCase(param.getSystemType())
|| VirtualPool.SystemType.isBlockTypeSystem(param.getSystemType()))) {
throw APIException.badRequests.invalidSystemType("Block");
}
if (VirtualPool.SystemType.vnxblock.name().equalsIgnoreCase(param.getSystemType())) {
Integer thinVolumePreAllocation = vpool.getThinVolumePreAllocationPercentage();
Integer thinVolumePreAllocationParam = param.getThinVolumePreAllocationPercentage();
if (thinVolumePreAllocation != null && thinVolumePreAllocation > 0
&& (thinVolumePreAllocationParam == null || thinVolumePreAllocationParam > 0)) {
throw APIException.badRequests.thinVolumePreallocationPercentageOnlyApplicableToVMAX();
}
}
vpool.getArrayInfo().put(VirtualPoolCapabilityValuesWrapper.SYSTEM_TYPE, param.getSystemType());
} else {
if (vpool.getArrayInfo() == null) {
vpool.setArrayInfo(new StringSetMap());
vpool.getArrayInfo().put(VirtualPoolCapabilityValuesWrapper.SYSTEM_TYPE, NONE);
}
}
if (null != param.getRaidLevelChanges()) {
if (null != param.getRaidLevelChanges().getAdd()) {
for (String raidLevel : param.getRaidLevelChanges().getAdd().getRaidLevels()) {
vpool.getArrayInfo()
.put(VirtualPoolCapabilityValuesWrapper.RAID_LEVEL, raidLevel);
}
}
if (null != param.getRaidLevelChanges().getRemove()) {
for (String raidLevel : param.getRaidLevelChanges().getRemove().getRaidLevels()) {
vpool.getArrayInfo().remove(VirtualPoolCapabilityValuesWrapper.RAID_LEVEL,
raidLevel);
}
}
}
if (null != param.getAutoTieringPolicyName()) {
if (param.getAutoTieringPolicyName().isEmpty()) { // To unset a policy name.
vpool.setAutoTierPolicyName(NONE);
} else {
vpool.setAutoTierPolicyName(param.getAutoTieringPolicyName());
}
}
if (null != param.getCompressionEnabled()) {
vpool.setCompressionEnabled(param.getCompressionEnabled());
}
vpool.setHostIOLimitBandwidth(param.getHostIOLimitBandwidth());
vpool.setHostIOLimitIOPs(param.getHostIOLimitIOPs());
if (null != param.getDriveType()) {
vpool.setDriveType(param.getDriveType());
} else {
vpool.setDriveType(NONE);
}
validateAndSetPathParams(vpool, param.getMaxPaths(), param.getMinPaths(), param.getPathsPerInitiator());
if (param.getThinVolumePreAllocationPercentage() != null) {
vpool.setThinVolumePreAllocationPercentage(param.getThinVolumePreAllocationPercentage());
}
if (param.getMultiVolumeConsistency() != null) {
vpool.setMultivolumeConsistency(param.getMultiVolumeConsistency());
}
if (null != param.getExpandable()) {
vpool.setExpandable(param.getExpandable());
}
if (param.getFastExpansion() != null) {
vpool.setFastExpansion(param.getFastExpansion());
}
if (null != param.getUniquePolicyNames()) {
vpool.setUniquePolicyNames(param.getUniquePolicyNames());
}
// Update HA settings.
if (null != param.getHighAvailability()) {
updateHAParametersForVirtualPool(vpool, param.getHighAvailability());
}
// Update Protection settings.
if (null != param.getProtection()) {
updateProtectionParamsForVirtualPool(vpool, param.getProtection(), param.getHighAvailability());
}
// update placement policy
if (param.getPlacementPolicy() != null) {
vpool.setPlacementPolicy(param.getPlacementPolicy());
}
// non-dedep vpool can NOT be made dedup if it has volumes created
// dedup vpool can be made non-dedup because dedup storage pools will always remain
if (null != param.getDedupCapable()) {
if (vpool.getDedupCapable() != null && !vpool.getDedupCapable() &&
param.getDedupCapable()) {
ArgValidator.checkReference(VirtualPool.class, id, checkForDelete(vpool));
}
vpool.setDedupCapable(param.getDedupCapable());
}
// Validate Block VirtualPool update params.
VirtualPoolUtil.validateBlockVirtualPoolUpdateParams(vpool, param, _dbClient);
StringBuffer errorMessage = new StringBuffer();
// invokes implicit pool matching algorithm.
ImplicitPoolMatcher.matchVirtualPoolWithAllStoragePools(vpool, _dbClient, _coordinator, errorMessage);
if (null != vpool.getMatchedStoragePools() || null != vpool.getInvalidMatchedPools()) {
Set<URI> allSrdfTargetVPools = SRDFUtils.fetchSRDFTargetVirtualPools(_dbClient);
Set<URI> allRpTargetVPools = RPHelper.fetchRPTargetVirtualPools(_dbClient);
ImplicitUnManagedObjectsMatcher.matchVirtualPoolsWithUnManagedVolumes(vpool, allSrdfTargetVPools, allRpTargetVPools, _dbClient, true);
}
// Validate Mirror Vpool
if (vpool.getMirrorVirtualPool() != null && !NullColumnValueGetter.isNullURI(URI.create(vpool.getMirrorVirtualPool()))) {
VirtualPool protectionMirrorVPool = _permissionsHelper.getObjectById(URI.create(vpool.getMirrorVirtualPool()),
VirtualPool.class);
validateMirrorVpool(vpool.getHighAvailability(), protectionMirrorVPool);
}
// Validate Max Native Continuous Copies
if (vpool.getMaxNativeContinuousCopies() != null) {
validateMaxNativeContinuousCopies(vpool.getMaxNativeContinuousCopies(), vpool.getHighAvailability());
}
_dbClient.updateObject(vpool);
// Update VirtualPool and QoS with new parameters
QosService.updateQos(vpool, qosSpecification, _dbClient);
recordOperation(OperationTypeEnum.UPDATE_VPOOL, VPOOL_UPDATED_DESCRIPTION, vpool);
return toBlockVirtualPool(_dbClient, vpool, VirtualPool.getProtectionSettings(vpool, _dbClient),
VirtualPool.getRemoteProtectionSettings(vpool, _dbClient));
}
/**
* Updates the virtual pool high availability parameters based
* values of the update request.
*
* @param vPool A reference to the virtual pool to update.
* @param haParam The HA update parameters.
*/
private void updateHAParametersForVirtualPool(VirtualPool vPool,
VirtualPoolHighAvailabilityParam haParam) {
// Get the HA varray/vpool map.
StringMap haVarrayVpoolMap = vPool.getHaVarrayVpoolMap();
// Set the MetroPoint value
if (haParam != null) {
vPool.setMetroPoint(haParam.getMetroPoint());
}
// Set the autoCrossConnectExport if set in the haParam
if (haParam.getAutoCrossConnectExport() != null) {
vPool.setAutoCrossConnectExport(haParam.getAutoCrossConnectExport());
}
// Check if high availability is changing, if yes check if this virtual pool
// is used as continuous copies vpool, if yes then throw exception.
if (vPool.getHighAvailability() != null && haParam.getType() != null && !vPool.getHighAvailability().equals(haParam.getType())) {
checkIfVpoolIsSetAsContinuousCopiesVpool(vPool);
} else if (vPool.getHighAvailability() == null && haParam.getType() != null
&& (haParam.getType().equals(VirtualPool.HighAvailabilityType.vplex_local.name())
|| haParam.getType().equals(VirtualPool.HighAvailabilityType.vplex_distributed.name()))) {
checkIfVpoolIsSetAsContinuousCopiesVpool(vPool);
} else if (vPool.getHighAvailability() != null &&
(vPool.getHighAvailability().equals(VirtualPool.HighAvailabilityType.vplex_local.name())
|| vPool.getHighAvailability().equals(VirtualPool.HighAvailabilityType.vplex_distributed.name()))
&& (haParam.getType() == null || String.valueOf(haParam.getType()).isEmpty())) {
checkIfVpoolIsSetAsContinuousCopiesVpool(vPool);
}
// Check for removal of HA from the virtual pool.
if ((haParam.getType() == null || String.valueOf(haParam.getType()).isEmpty())
&& (haParam.getHaVirtualArrayVirtualPool() == null
|| String.valueOf(haParam.getHaVirtualArrayVirtualPool()).isEmpty())) {
_log.info("Removing HA from vPool");
// An empty HA parameter indicates HA should be removed
// for the virtual pool.
if (vPool.getHighAvailability() != null) {
vPool.setHighAvailability(NullColumnValueGetter.getNullStr());
}
if ((haVarrayVpoolMap != null) && (!haVarrayVpoolMap.isEmpty())) {
haVarrayVpoolMap.remove(haVarrayVpoolMap.keySet().iterator().next());
vPool.setHaVarrayConnectedToRp(NullColumnValueGetter.getNullStr());
}
}
// If the type is specified, set it. If the type is local
// Make sure the HA virtual array/pool map is empty.
if (haParam.getType() != null && !String.valueOf(haParam.getType()).isEmpty()) {
// Update the type.
vPool.setHighAvailability(haParam.getType());
if ((haParam.getType().equals(VirtualPool.HighAvailabilityType.vplex_local.name()))
&& (haVarrayVpoolMap != null) && (!haVarrayVpoolMap.isEmpty())) {
haVarrayVpoolMap.remove(haVarrayVpoolMap.keySet().iterator().next());
vPool.setHaVarrayConnectedToRp(NullColumnValueGetter.getNullStr());
}
}
// Update the HA virtual array and pool settings.
if (haParam.getHaVirtualArrayVirtualPool() != null) {
// Check for removal.
if (haParam.getHaVirtualArrayVirtualPool().getVirtualArray() == null
&& haParam.getHaVirtualArrayVirtualPool().getVirtualPool() == null) {
// If empty, indicates any HA virtual array and pool
// settings should be removed.
if ((haVarrayVpoolMap != null) && (!haVarrayVpoolMap.isEmpty())) {
haVarrayVpoolMap.remove(haVarrayVpoolMap.keySet().iterator().next());
vPool.setHaVarrayConnectedToRp(NullColumnValueGetter.getNullStr());
// If the type is vplex_distributed, remove the type as well
if (vPool.getHighAvailability() != null && vPool.getHighAvailability().equals(
VirtualPool.HighAvailabilityType.vplex_distributed.name())) {
vPool.setHighAvailability(NullColumnValueGetter.getNullStr());
}
}
} else {
// Determine the HA virtual pool.
String haVpoolId = NullColumnValueGetter.getNullURI().toString();
if (haParam.getHaVirtualArrayVirtualPool().getVirtualPool() != null) {
// Pool is specified in the update.
if (!haParam.getHaVirtualArrayVirtualPool().getVirtualPool().toString().isEmpty()) {
// Note that an empty pool will mean remove the pool.
haVpoolId = haParam.getHaVirtualArrayVirtualPool().getVirtualPool().toString();
// If an HA vpool is specified and it is changing then verify the
// value specified.
if (NullColumnValueGetter.isNotNullValue(haVpoolId)) {
if ((haVarrayVpoolMap == null) || (haVarrayVpoolMap.isEmpty())) {
// No HA vpool is currently specified, so just verify the new HA vpool.
verifyNewHAVpoolForHAVpoolUpdate(vPool, haVpoolId);
} else {
String currentHAVpoolId = haVarrayVpoolMap.get(haVarrayVpoolMap.keySet().iterator().next());
if (!NullColumnValueGetter.isNotNullValue(currentHAVpoolId)) {
// The current specified is null, so just verify the new HA vpool.
verifyNewHAVpoolForHAVpoolUpdate(vPool, haVpoolId);
} else if (!haVpoolId.equals(currentHAVpoolId)) {
// Otherwise, verify the HA vpool only if it is changing.
verifyNewHAVpoolForHAVpoolUpdate(vPool, haVpoolId);
}
}
}
}
}
// Update settings for the HA virtual array.
if (haParam.getHaVirtualArrayVirtualPool().getVirtualArray() != null) {
// HA virtual array specified in update. When specified in
// update must supply a valid value.
if ((haVarrayVpoolMap != null) && (!haVarrayVpoolMap.isEmpty())) {
// Replace entry with new HA virtual array.
String haVarray = haVarrayVpoolMap.keySet().iterator().next();
if (!haParam.getHaVirtualArrayVirtualPool().getVirtualArray().toString().equals(haVarray)) {
// only remove the entry if the varray has changed
haVarrayVpoolMap.remove(haVarray);
vPool.setHaVarrayConnectedToRp(NullColumnValueGetter.getNullStr());
}
haVarrayVpoolMap.put(haParam.getHaVirtualArrayVirtualPool().getVirtualArray().toString(),
haVpoolId);
// Use HA Varray as RP Source if boolean is set to true
if (haParam.getHaVirtualArrayVirtualPool().getActiveProtectionAtHASite() != null
&& haParam.getHaVirtualArrayVirtualPool().getActiveProtectionAtHASite()) {
vPool.setHaVarrayConnectedToRp(haParam.getHaVirtualArrayVirtualPool().getVirtualArray().toString());
}
else {
vPool.setHaVarrayConnectedToRp(NullColumnValueGetter.getNullStr());
}
} else if (haVarrayVpoolMap == null) {
// Create new map and entry.
haVarrayVpoolMap = new StringMap();
haVarrayVpoolMap.put(haParam.getHaVirtualArrayVirtualPool().getVirtualArray().toString(),
haVpoolId);
vPool.setHaVarrayVpoolMap(haVarrayVpoolMap);
// Use HA Varray as RP Source if boolean is set to true
if (haParam.getHaVirtualArrayVirtualPool().getActiveProtectionAtHASite() != null
&& haParam.getHaVirtualArrayVirtualPool().getActiveProtectionAtHASite()) {
vPool.setHaVarrayConnectedToRp(haParam.getHaVirtualArrayVirtualPool().getVirtualArray().toString());
}
else {
vPool.setHaVarrayConnectedToRp(NullColumnValueGetter.getNullStr());
}
} else {
// Map exists but is empty, just add.
haVarrayVpoolMap.put(haParam.getHaVirtualArrayVirtualPool().getVirtualArray().toString(),
haVpoolId);
// Use HA Varray as RP Source if boolean is set to true
if (haParam.getHaVirtualArrayVirtualPool().getActiveProtectionAtHASite() != null
&& haParam.getHaVirtualArrayVirtualPool().getActiveProtectionAtHASite()) {
vPool.setHaVarrayConnectedToRp(haParam.getHaVirtualArrayVirtualPool().getVirtualArray().toString());
}
else {
vPool.setHaVarrayConnectedToRp(NullColumnValueGetter.getNullStr());
}
}
} else if ((haParam.getHaVirtualArrayVirtualPool().getVirtualPool() != null)
&& (haVarrayVpoolMap != null) && (!haVarrayVpoolMap.isEmpty())) {
// Only HA virtual pool update, update setting for current
// HA virtual array.
String haVarray = haVarrayVpoolMap.keySet().iterator().next();
haVarrayVpoolMap.put(haVarray, haVpoolId);
// Use HA Varray as RP Source if boolean is set to true
if (haParam.getHaVirtualArrayVirtualPool().getActiveProtectionAtHASite() != null
&& haParam.getHaVirtualArrayVirtualPool().getActiveProtectionAtHASite()) {
vPool.setHaVarrayConnectedToRp(haParam.getHaVirtualArrayVirtualPool().getVirtualArray().toString());
}
else {
vPool.setHaVarrayConnectedToRp(NullColumnValueGetter.getNullStr());
}
}
}
}
}
/**
* When updating a virtual pool, this function verifies that if there is
* and update to the HA virtual pool, that the specified pool is valid
* for the virtual pool being updated.
*
* @param vPoolBeingUpdated The vpool being updated.
* @param newHAVpoolId The non-null id of the new HA vpool.
*/
private void verifyNewHAVpoolForHAVpoolUpdate(VirtualPool vPoolBeingUpdated, String newHAVpoolId) {
URI newHAVpoolURI = URI.create(newHAVpoolId);
VirtualPool newHAVpool = _dbClient.queryObject(VirtualPool.class, newHAVpoolURI);
if (newHAVpool == null) {
throw APIException.badRequests.haVpoolForVpoolUpdateDoesNotExist(newHAVpoolId);
}
if (newHAVpool.getInactive()) {
throw APIException.badRequests.haVpoolForVpoolUpdateIsInactive(newHAVpool.getLabel());
}
StringMap newHAVpoolHAMap = newHAVpool.getHaVarrayVpoolMap();
if ((newHAVpoolHAMap == null) || (newHAVpoolHAMap.isEmpty())) {
// New HA vpool does not specify VPLEX HA.
return;
}
String newHAVpoolHAVpoolId = newHAVpoolHAMap.get(newHAVpoolHAMap.keySet().iterator().next());
if (!NullColumnValueGetter.isNotNullValue(newHAVpoolHAVpoolId)) {
// No HA Vpool specified.
return;
}
VirtualPool newHAVpoolHAVpool = _dbClient.queryObject(VirtualPool.class, URI.create(newHAVpoolHAVpoolId));
if (newHAVpoolHAVpool == null) {
// Invalid HA vpool for the new HA vpool does not exist.
throw APIException.badRequests.haVpoolForNewHAVpoolForVpoolUpdateDoesNotExist(newHAVpoolHAVpoolId, newHAVpool.getLabel());
}
// Make sure that if the HA vpool itself specifies HA, that the
// HA vpool is not this vpool being updated. See Jira 6797.
if (newHAVpoolHAVpool.getId().equals(vPoolBeingUpdated.getId())) {
throw APIException.badRequests.haVpoolForVpoolUpdateHasInvalidHAVpool(newHAVpool.getLabel());
}
}
/**
* Performs the protection updates on <code>VirtualPool</code>.
*
* @param virtualPool Reference to the virtual pool to update.
* @param param The updates that need to be applied to the virtual pool.
*/
private void updateProtectionParamsForVirtualPool(VirtualPool virtualPool,
BlockVirtualPoolProtectionUpdateParam param, VirtualPoolHighAvailabilityParam haParam) {
// If the update specifies protection, we need to process the update.
if (param != null) {
// If the update specifies protection mirroring, process the mirroring update.
if (param.getContinuousCopies() != null) {
if ((param.getContinuousCopies().getVpool() == null || String.valueOf(param.getContinuousCopies().getVpool()).isEmpty()) &&
(param.getContinuousCopies().getMaxMirrors() == null
|| param.getContinuousCopies().getMaxMirrors() == VirtualPoolProtectionMirrorParam.MAX_DISABLED
|| String.valueOf(param.getContinuousCopies().getMaxMirrors()).isEmpty())) {
// An empty protection mirror request was sent indicating removal of mirrored protection
// so remove protection mirror vpool and set max native continuous copies to disabled value.
if (virtualPool.getMirrorVirtualPool() != null) {
virtualPool.setMirrorVirtualPool(String.valueOf(NullColumnValueGetter.getNullURI()));
}
virtualPool.setMaxNativeContinuousCopies(VirtualPool.MAX_DISABLED);
} else if ((param.getContinuousCopies().getVpool() == null || String.valueOf(param.getContinuousCopies().getVpool())
.isEmpty())) {
// Setting Mirror Virtual Pool is optional so user can choose to remove mirror virtual pool
if (virtualPool.getMirrorVirtualPool() != null) {
virtualPool.setMirrorVirtualPool(String.valueOf(NullColumnValueGetter.getNullURI()));
}
if (param.getContinuousCopies().getMaxMirrors() != null) {
// Updating max mirrors
virtualPool.setMaxNativeContinuousCopies(param.getContinuousCopies().getMaxMirrors());
}
} else if ((param.getContinuousCopies().getVpool() != null && !String.valueOf(param.getContinuousCopies().getVpool())
.isEmpty())
&& (param.getContinuousCopies().getMaxMirrors() == null)) {
// Update protection mirror vpool
updateProtectionMirrorVPool(param.getContinuousCopies().getVpool(), virtualPool);
} else {
// continuous protection has been specified with both
// the protection vpool and max mirrors
if (param.getContinuousCopies().getVpool() != null && !String.valueOf(param.getContinuousCopies().getVpool()).isEmpty()) {
updateProtectionMirrorVPool(param.getContinuousCopies().getVpool(), virtualPool);
}
if (param.getContinuousCopies().getMaxMirrors() != null) {
virtualPool.setMaxNativeContinuousCopies(param.getContinuousCopies().getMaxMirrors());
}
}
}
// Handle the protection snapshot updates
if (param.getSnapshots() != null) {
// By default the maxSnapshots value should be 0 so this should never be null
// but good to have just in case...
if (param.getSnapshots().getMaxSnapshots() != null) {
// Keep in mind that if an empty or 0 value is specified snapshots
// will be removed from the virtual pool.
virtualPool.setMaxNativeSnapshots(param.getSnapshots().getMaxSnapshots());
} else {
// Remove snapshots by setting the disabled value
virtualPool.setMaxNativeSnapshots(VirtualPool.MAX_DISABLED);
}
}
// Handle SRDF update
updateRemoteCopyVPool(virtualPool, param);
// Handle the RP protection updates
if (param.getRecoverPoint() != null) {
if (param.getRecoverPoint().getAdd() == null && param.getRecoverPoint().getRemove() == null
&& param.getRecoverPoint().getSourcePolicy() == null) {
// Empty RP protection specified. This indicates removal of
// RP protection so remove it.
deleteVPoolProtectionVArraySettings(virtualPool);
} else {
// If the source policy is omitted, do nothing.
ProtectionSourcePolicy sourcePolicy = param.getRecoverPoint().getSourcePolicy();
if (sourcePolicy != null) {
String nullValue = NullColumnValueGetter.getNullStr();
virtualPool.setJournalSize(StringUtils.defaultString(sourcePolicy.getJournalSize(), nullValue));
virtualPool.setJournalVarray(!NullColumnValueGetter.isNullURI(sourcePolicy.getJournalVarray()) ? sourcePolicy
.getJournalVarray().toString()
: nullValue);
if (NullColumnValueGetter.isNullValue(virtualPool.getJournalVarray())) {
// If the journal varray is null, the journal vpool has to be null too.
virtualPool.setJournalVpool(nullValue);
} else {
// Set the journal virtual pool. If none is specified, we must determine the default, which
// will be the parent vpool or the ha vpool.
String defaultVpoolId = nullValue;
if (haParam == null || Boolean.TRUE.equals(haParam.getMetroPoint())) {
// Default the virtual pool to the parent virtual pool in cases where no high availability
// is specified or when HA is specified but not MetroPoint.
defaultVpoolId = virtualPool.getId().toString();
} else if (Boolean.FALSE.equals(haParam.getMetroPoint()) && haParam.getHaVirtualArrayVirtualPool() != null
&& Boolean.TRUE.equals(haParam.getHaVirtualArrayVirtualPool().getActiveProtectionAtHASite())) {
// If active protection at HA site is specified, our default vpool should be the HA
// virtual pool.
if (haParam.getHaVirtualArrayVirtualPool().getVirtualPool() != null) {
defaultVpoolId = haParam.getHaVirtualArrayVirtualPool().getVirtualPool().toString();
}
}
virtualPool.setJournalVpool(!NullColumnValueGetter.isNullURI(sourcePolicy.getJournalVpool()) ? sourcePolicy
.getJournalVpool().toString()
: defaultVpoolId);
}
if (NullColumnValueGetter.isNotNullValue(virtualPool.getHighAvailability())) {
virtualPool.setStandbyJournalVarray(
!NullColumnValueGetter.isNullURI(sourcePolicy.getStandbyJournalVarray()) ? sourcePolicy
.getStandbyJournalVarray().toString()
: nullValue);
if (NullColumnValueGetter.isNullValue(virtualPool.getStandbyJournalVarray())) {
// If the ha journal varray is null, the ha journal vpool has to be null too.
virtualPool.setStandbyJournalVpool(nullValue);
} else {
String defaultHaVpool = nullValue;
// Obtain the default HA virtual pool
Map<String, String> haVarrayVpoolMap = virtualPool.getHaVarrayVpoolMap();
if (haVarrayVpoolMap != null && !haVarrayVpoolMap.isEmpty()) {
if (NullColumnValueGetter.isNotNullValue(haVarrayVpoolMap.get(virtualPool.getStandbyJournalVarray()))) {
defaultHaVpool = haVarrayVpoolMap.get(virtualPool.getStandbyJournalVarray());
}
}
// By default, if no standby vpool is set, set the HA journal vpool to the HA vpool.
virtualPool.setStandbyJournalVpool(
!NullColumnValueGetter.isNullURI(sourcePolicy.getStandbyJournalVpool()) ? sourcePolicy
.getStandbyJournalVpool().toString()
: defaultHaVpool);
}
}
virtualPool.setRpCopyMode(StringUtils.defaultString(sourcePolicy.getRemoteCopyMode(), nullValue));
// If the RPO value is null, set the value to 0 to unset this field.
long rpoValue = (sourcePolicy.getRpoValue() == null) ? 0L : sourcePolicy.getRpoValue();
virtualPool.setRpRpoValue(rpoValue);
virtualPool.setRpRpoType(StringUtils.defaultString(sourcePolicy.getRpoType(), nullValue));
}
if (param.getRecoverPoint().getRemove() != null && !param.getRecoverPoint().getRemove().isEmpty()) {
// Only remove protection copies if there are copies to remove
if (virtualPool.getProtectionVarraySettings() != null &&
!virtualPool.getProtectionVarraySettings().isEmpty()) {
StringMap settingsMap = virtualPool.getProtectionVarraySettings();
for (VirtualPoolProtectionVirtualArraySettingsParam settingsParam : param.getRecoverPoint().getRemove()) {
if (settingsParam.getVarray() != null && !settingsParam.getVarray().toString().isEmpty()) {
String vpoolProtectionVarraySettingsUri =
virtualPool.getProtectionVarraySettings().get(settingsParam.getVarray().toString());
if (vpoolProtectionVarraySettingsUri == null) {
throw APIException.badRequests.protectionNoCopyCorrespondingToVirtualArray(settingsParam
.getVarray());
}
deleteVPoolProtectionVArraySettings(virtualPool.getProtectionVarraySettings().get(
settingsParam.getVarray().toString()));
settingsMap.remove(settingsParam.getVarray().toString());
}
}
virtualPool.setProtectionVarraySettings(settingsMap);
}
}
if (param.getRecoverPoint().getAdd() != null && !param.getRecoverPoint().getAdd().isEmpty()) {
Set<VpoolProtectionVarraySettings> protectionSettingsToAdd = null;
StringMap settingsMap = virtualPool.getProtectionVarraySettings();
if (settingsMap == null) {
settingsMap = new StringMap();
}
for (VirtualPoolProtectionVirtualArraySettingsParam settingsParam : param.getRecoverPoint().getAdd()) {
VirtualArray virtualArray = _permissionsHelper.getObjectById(settingsParam.getVarray(), VirtualArray.class);
ArgValidator.checkEntity(virtualArray, settingsParam.getVarray(), false);
VpoolProtectionVarraySettings setting = new VpoolProtectionVarraySettings();
setting.setId(URIUtil.createId(VpoolProtectionVarraySettings.class));
setting.setParent(new NamedURI(virtualPool.getId(), virtualPool.getLabel()));
if (settingsParam.getVpool() != null && !String.valueOf(settingsParam.getVpool()).isEmpty()) {
setting.setVirtualPool(settingsParam.getVpool());
}
setting.setJournalSize(settingsParam.getCopyPolicy() != null ? settingsParam.getCopyPolicy().getJournalSize()
: null);
setting.setJournalVarray(settingsParam.getCopyPolicy() != null ? settingsParam.getCopyPolicy()
.getJournalVarray() : settingsParam.getVarray());
setting.setJournalVpool(settingsParam.getCopyPolicy() != null ? settingsParam.getCopyPolicy().getJournalVpool()
: settingsParam.getVpool());
settingsMap.put(settingsParam.getVarray().toString(), setting.getId().toString());
if (protectionSettingsToAdd == null) {
protectionSettingsToAdd = new HashSet<VpoolProtectionVarraySettings>();
}
protectionSettingsToAdd.add(setting);
}
// Set the underlying protection setting objects
if (protectionSettingsToAdd != null) {
for (VpoolProtectionVarraySettings setting : protectionSettingsToAdd) {
_dbClient.createObject(setting);
}
}
virtualPool.setProtectionVarraySettings(settingsMap);
}
StringMap settingsMap = virtualPool.getProtectionVarraySettings();
if (settingsMap != null && settingsMap.size() == 0) {
// If there are no copies after the add/remove, remove the source policy
virtualPool.setJournalSize(NullColumnValueGetter.getNullStr());
virtualPool.setJournalVarray(NullColumnValueGetter.getNullStr());
virtualPool.setJournalVpool(NullColumnValueGetter.getNullStr());
}
}
}
}
}
private void updateRemoteCopyVPool(VirtualPool virtualPool,
BlockVirtualPoolProtectionUpdateParam param) {
if (param.getRemoteCopies() != null) {
StringMap remoteCopySettingsMap = virtualPool.getProtectionRemoteCopySettings();
if (remoteCopySettingsMap == null) {
remoteCopySettingsMap = new StringMap();
virtualPool.setProtectionRemoteCopySettings(remoteCopySettingsMap);
}
// Determine if we are updating an existing remote copy setting on the vpool. This case
// occurs if we are removing and adding a remote copy settings for the same varray. In
// this case we want to update the existing remote copy settings vpool instead of performing a remove
// and an add, which is buggy in the StringMap.
if (param.getRemoteCopies().getRemove() != null && !param.getRemoteCopies().getRemove().isEmpty()
&& param.getRemoteCopies().getAdd() != null && !param.getRemoteCopies().getAdd().isEmpty()) {
Iterator<VirtualPoolRemoteProtectionVirtualArraySettingsParam> removeRemoteCopies =
param.getRemoteCopies().getRemove().iterator();
Iterator<VirtualPoolRemoteProtectionVirtualArraySettingsParam> addRemoteCopies =
param.getRemoteCopies().getAdd().iterator();
while (removeRemoteCopies.hasNext()) {
VirtualPoolRemoteProtectionVirtualArraySettingsParam removeRemoteCopySettingsParam = removeRemoteCopies.next();
while (addRemoteCopies.hasNext()) {
VirtualPoolRemoteProtectionVirtualArraySettingsParam addRemoteCopySettingsParam = addRemoteCopies.next();
// If we are adding and removing a remote copy setting for the same virtual array, we simply need
// to update the remote copy setting vpool in the database. After which we can delete the
// remote copy setting from the add/remove lists so they do not get processed individually.
if (removeRemoteCopySettingsParam.getVarray().toString().equals(
addRemoteCopySettingsParam.getVarray().toString())) {
String remoteCopySettingsUri = remoteCopySettingsMap.get(addRemoteCopySettingsParam.getVarray().toString());
VpoolRemoteCopyProtectionSettings remoteSettingsObj = _dbClient.queryObject(
VpoolRemoteCopyProtectionSettings.class, URI.create(remoteCopySettingsUri));
if (remoteSettingsObj != null) {
remoteSettingsObj.setVirtualPool(addRemoteCopySettingsParam.getVpool());
if (null != addRemoteCopySettingsParam.getRemoteCopyMode()) {
if (!CopyModes.lookup(addRemoteCopySettingsParam.getRemoteCopyMode())) {
throw APIException.badRequests.invalidCopyMode(addRemoteCopySettingsParam.getRemoteCopyMode());
}
remoteSettingsObj.setCopyMode(addRemoteCopySettingsParam.getRemoteCopyMode());
}
_dbClient.persistObject(remoteSettingsObj);
// Only remove from the add list if the remote copy setting actually
// exists. We will still want to add it if it does not exist.
param.getRemoteCopies().getAdd().remove(addRemoteCopySettingsParam);
}
// Always remove the remote copy setting from the remove list
param.getRemoteCopies().getRemove().remove(removeRemoteCopySettingsParam);
}
}
}
}
if (param.getRemoteCopies().getRemove() != null && !param.getRemoteCopies().getRemove().isEmpty()) {
for (VirtualPoolRemoteProtectionVirtualArraySettingsParam remoteSettings : param.getRemoteCopies().getRemove()) {
if (remoteSettings.getVarray() != null && remoteCopySettingsMap.containsKey(remoteSettings.getVarray().toString())) {
String remoteCopySettingsUri = remoteCopySettingsMap.get(remoteSettings.getVarray().toString());
remoteCopySettingsMap.remove(remoteSettings.getVarray().toString());
VpoolRemoteCopyProtectionSettings remoteSettingsObj = _dbClient.queryObject(
VpoolRemoteCopyProtectionSettings.class, URI.create(remoteCopySettingsUri));
remoteSettingsObj.setInactive(true);
_dbClient.persistObject(remoteSettingsObj);
}
}
}
if (param.getRemoteCopies().getAdd() != null && !param.getRemoteCopies().getAdd().isEmpty()) {
List<VpoolRemoteCopyProtectionSettings> remoteSettingsList = new ArrayList<VpoolRemoteCopyProtectionSettings>();
// already existing remote VArrays
List<String> existingRemoteUris = new ArrayList<String>(remoteCopySettingsMap.keySet());
for (VirtualPoolRemoteProtectionVirtualArraySettingsParam remoteSettings : param.getRemoteCopies().getAdd()) {
// CTRL-275 fix
VirtualArray remoteVArray = _dbClient.queryObject(VirtualArray.class, remoteSettings.getVarray());
if (null == remoteVArray || remoteVArray.getInactive()) {
throw APIException.badRequests.inactiveRemoteVArrayDetected(remoteSettings.getVarray());
}
VpoolRemoteCopyProtectionSettings remoteCopySettingsParam = new VpoolRemoteCopyProtectionSettings();
remoteSettingsList.add(remoteCopySettingsParam);
remoteCopySettingsParam.setId(URIUtil.createId(VpoolRemoteCopyProtectionSettings.class));
remoteCopySettingsParam.setVirtualArray(remoteSettings.getVarray());
if (existingRemoteUris.contains(remoteSettings.getVarray().toString()) ||
remoteCopySettingsMap.containsKey(remoteSettings.getVarray().toString())) {
throw APIException.badRequests.duplicateRemoteSettingsDetected(remoteSettings.getVarray());
}
remoteCopySettingsMap.put(remoteSettings.getVarray().toString(), remoteCopySettingsParam.getId().toString());
remoteCopySettingsParam.setVirtualPool(remoteSettings.getVpool());
if (null != remoteSettings.getRemoteCopyMode()) {
if (!CopyModes.lookup(remoteSettings.getRemoteCopyMode())) {
throw APIException.badRequests.invalidCopyMode(remoteSettings.getRemoteCopyMode());
}
remoteCopySettingsParam.setCopyMode(remoteSettings.getRemoteCopyMode());
}
}
_dbClient.createObject(remoteSettingsList);
}
}
}
private void updateProtectionMirrorVPool(URI vPoolUri, VirtualPool virtualPool) {
ArgValidator.checkUri(vPoolUri);
VirtualPool protectionMirrorVPool = _permissionsHelper.getObjectById(vPoolUri, VirtualPool.class);
ArgValidator.checkEntity(protectionMirrorVPool, vPoolUri, false);
virtualPool.setMirrorVirtualPool(vPoolUri.toString());
}
/**
*
* Check if any VirtualPool attribute values have changed.
*
* @TODO Whenever a new attribute is added to VirtualPool, we should change this
* logic. Constraint to check: can we change new attribute, if there
* are resources associated with VirtualPool?
*
* @param param
* @param vpool : VirtualPool in DB.
* @return : flag to check whether to update VirtualPool or not.
*/
public boolean checkAttributeValuesChanged(BlockVirtualPoolUpdateParam param, VirtualPool vpool) {
return super.checkAttributeValuesChanged(param, vpool)
|| checkPathParameterModified(vpool.getNumPaths(), param.getMaxPaths())
|| checkPathParameterModified(vpool.getMinPaths(), param.getMinPaths())
|| checkPathParameterModified(vpool.getPathsPerInitiator(), param.getPathsPerInitiator())
|| checkPathParameterModified(vpool.getHostIOLimitBandwidth(), param.getHostIOLimitBandwidth())
|| checkPathParameterModified(vpool.getHostIOLimitIOPs(), param.getHostIOLimitIOPs())
|| VirtualPoolUtil.checkRaidLevelsChanged(vpool.getArrayInfo(), param.getRaidLevelChanges())
|| VirtualPoolUtil.checkForVirtualPoolAttributeModification(vpool.getDriveType(), param.getDriveType())
|| VirtualPoolUtil.checkThinVolumePreAllocationChanged(vpool.getThinVolumePreAllocationPercentage(),
param.getThinVolumePreAllocationPercentage())
|| VirtualPoolUtil.checkProtectionChanged(vpool, param.getProtection())
|| VirtualPoolUtil.checkHighAvailabilityChanged(vpool, param.getHighAvailability());
}
/**
* check the if there is any change in VirtualPool maxPaths, minPaths, pathsPerInitiator attributes.
*
* @param vpoolValue
* @param paramValue
*/
private boolean checkPathParameterModified(Integer vpoolValue, Integer paramValue) {
boolean isModified = false;
if (null != vpoolValue) {
if (paramValue == null) {
isModified = false;
} else if (paramValue != vpoolValue) {
isModified = true;
}
} else {
if (null != paramValue) {
isModified = true;
}
}
return isModified;
}
/**
* This method allows a user to update assigned matched pools.
*
* @prereq none
* @param param
* : VirtualPool parameter
* @brief Update pools in block store virtual pool
* @return VirtualPool details
*/
@PUT
@Consumes({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
@Path("/{id}/assign-matched-pools")
@Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
@CheckPermission(roles = { Role.SYSTEM_ADMIN, Role.RESTRICTED_SYSTEM_ADMIN })
public BlockVirtualPoolRestRep updateBlockVirtualPoolWithAssignedPools(@PathParam("id") URI id,
VirtualPoolPoolUpdateParam param) {
return toBlockVirtualPool(_dbClient, updateVirtualPoolWithAssignedStoragePools(id, param));
}
/**
* Gets storage capacity information for specified virtual pool and neighborhood instances.
*
* The method returns set of metrics for capacity available for block storage provisioning:
* - free_gb : free storage capacity
* - used_gb : used storage capacity
* - provisioned_gb : subscribed storage capacity (may be larger than usable capacity)
* - percent_used : percent of usable capacity which is used
* - percent_subscribed : percent of usable capacity which is subscribed (may be more than 100)
*
* @prereq none
* @param id the URN of a ViPR VirtualPool.
* @param varrayId The id of VirtualArray.
* @brief Show storage capacity for the virtual pool and virtual array
* @return Capacity metrics in GB and percent indicators for used and subscribed capacity.
*/
@GET
@Path("/{id}/varrays/{varrayId}/capacity")
@Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
@CheckPermission(roles = { Role.SYSTEM_ADMIN, Role.SYSTEM_MONITOR }, acls = { ACL.USE })
public CapacityResponse getCapacity(@PathParam("id") URI id,
@PathParam("varrayId") URI varrayId) {
return getCapacityForVirtualPoolAndVirtualArray(getVirtualPool(Type.block, id), varrayId);
}
@Override
protected URI getTenantOwner(URI id) {
return null;
}
@Override
protected Type getVirtualPoolType() {
return Type.block;
}
/**
* List all instances of block virtual pools
*
* @prereq none
* @brief List all instances of block virtual pools
*/
@POST
@Path("/bulk")
@Consumes({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
@Override
public BlockVirtualPoolBulkRep getBulkResources(BulkIdParam param) {
return (BlockVirtualPoolBulkRep) super.getBulkResources(param);
}
/**
* Show quota and available capacity before quota is exhausted
*
* @prereq none
* @param id the URN of a ViPR VirtualPool.
* @brief Show quota and available capacity before quota is exhausted
* @return QuotaInfo Quota metrics.
*/
@GET
@Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
@CheckPermission(roles = { Role.SYSTEM_MONITOR, Role.SYSTEM_ADMIN })
@Path("/{id}/quota")
public QuotaInfo getQuota(@PathParam("id") URI id) throws DatabaseException {
return getQuota(getVirtualPool(Type.block, id));
}
/**
* Updates quota and available capacity before quota is exhausted
*
* @prereq none
* @param id the URN of a ViPR VirtualPool.
* @param param new values for the quota
* @brief Updates quota and available capacity before quota is exhausted
* @return QuotaInfo Quota metrics.
*/
@PUT
@Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
@Consumes({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
@CheckPermission(roles = { Role.SYSTEM_ADMIN, Role.RESTRICTED_SYSTEM_ADMIN })
@Path("/{id}/quota")
public QuotaInfo updateQuota(@PathParam("id") URI id,
QuotaUpdateParam param) throws DatabaseException {
return updateQuota(getVirtualPool(Type.block, id), param);
}
@Override
public BlockVirtualPoolBulkRep queryBulkResourceReps(List<URI> ids) {
if (!ids.iterator().hasNext()) {
return new BlockVirtualPoolBulkRep();
}
// get vdc id from the first id; assume all id's are from the same vdc
String shortVdcId = VdcUtil.getVdcId(getResourceClass(), ids.iterator().next()).toString();
Iterator<VirtualPool> dbIterator;
if (shortVdcId.equals(VdcUtil.getLocalShortVdcId())) {
dbIterator = _dbClient.queryIterativeObjects(getResourceClass(), ids);
} else {
GeoServiceClient geoClient = _geoHelper.getClient(shortVdcId);
try {
dbIterator = geoClient.queryObjects(getResourceClass(), ids);
} catch (Exception ex) {
// TODO: revisit this exception
_log.error("error retrieving bulk virtual pools from vdc " + shortVdcId, ex);
throw APIException.internalServerErrors.genericApisvcError("error retrieving remote virtual pool", ex);
}
}
return new BlockVirtualPoolBulkRep(BulkList.wrapping(dbIterator, BLOCK_VPOOL_MAPPER, new BulkList.VirtualPoolFilter(Type.block)));
}
private final BlockVirtualPoolMapper BLOCK_VPOOL_MAPPER = new BlockVirtualPoolMapper();
private class BlockVirtualPoolMapper implements Function<VirtualPool, BlockVirtualPoolRestRep> {
@Override
public BlockVirtualPoolRestRep apply(final VirtualPool vpool) {
BlockVirtualPoolRestRep restRep = getBlockVirtualPoolWithProtection(vpool);
restRep.setNumResources(getNumResources(vpool, _dbClient));
return restRep;
}
}
@SuppressWarnings({ "unchecked", "rawtypes" })
@Override
protected BlockVirtualPoolBulkRep queryFilteredBulkResourceReps(
List<URI> ids) {
if (isSystemOrRestrictedSystemAdmin()) {
return queryBulkResourceReps(ids);
}
if (!ids.iterator().hasNext()) {
return new BlockVirtualPoolBulkRep();
}
// get vdc id from the first id; assume all id's are from the same vdc
String shortVdcId = VdcUtil.getVdcId(getResourceClass(), ids.iterator().next()).toString();
Iterator<VirtualPool> dbIterator;
if (shortVdcId.equals(VdcUtil.getLocalShortVdcId())) {
dbIterator = _dbClient.queryIterativeObjects(getResourceClass(), ids);
} else {
GeoServiceClient geoClient = _geoHelper.getClient(shortVdcId);
try {
dbIterator = geoClient.queryObjects(getResourceClass(), ids);
} catch (Exception ex) {
// TODO: revisit this exception
_log.error("error retrieving bulk virtual pools from vdc " + shortVdcId, ex);
throw APIException.internalServerErrors.genericApisvcError("error retrieving remote virtual pool", ex);
}
}
BulkList.ResourceFilter filter = new BulkList.VirtualPoolFilter(Type.block, getUserFromContext(), _permissionsHelper);
return new BlockVirtualPoolBulkRep(BulkList.wrapping(dbIterator, BLOCK_VPOOL_MAPPER, filter));
}
@Override
protected ResourceTypeEnum getResourceType() {
return ResourceTypeEnum.BLOCK_VPOOL;
}
static Integer getNumResources(VirtualPool vpool, DbClient dbClient) {
return dbClient.countObjects(Volume.class, "virtualPool", vpool.getId());
}
/**
* Validate and update the various path parameters:
* 1. If minPaths or pathsPerInitiator are specified, maxPaths must be specified
* 2. The resulting minPaths cannot be greater than maxPaths
* 3. The resulting pathsPerInitiator cannot be greater than maxPaths
*
* @param vpool
* @param maxPaths
* @param minPaths
* @param pathsPerInitiator
*/
private void validateAndSetPathParams(VirtualPool vpool,
Integer maxPaths, Integer minPaths, Integer pathsPerInitiator) {
// If minPaths is specified, or pathsPerInitiator is specified, maxPaths must be specified
if ((minPaths != null || pathsPerInitiator != null) && maxPaths == null) {
throw APIException.badRequests.maxPathsRequired();
}
if (maxPaths != null) {
ArgValidator.checkFieldMinimum(maxPaths, 1, "max_paths");
}
if (minPaths != null) {
ArgValidator.checkFieldMinimum(minPaths, 1, "min_paths");
}
if (pathsPerInitiator != null) {
ArgValidator.checkFieldMinimum(pathsPerInitiator, 1, "paths_per_initiator");
}
Integer min = (minPaths != null) ? minPaths : vpool.getMinPaths();
Integer max = (maxPaths != null) ? maxPaths : vpool.getNumPaths();
Integer ppi = (pathsPerInitiator != null) ? pathsPerInitiator : vpool.getPathsPerInitiator();
// Default the parameters to one if not set either in vPool or parameters
if (min == null) {
minPaths = min = 1;
}
if (max == null) {
maxPaths = max = 1;
}
if (ppi == null) {
pathsPerInitiator = ppi = 1;
}
// minPaths must be <= than maxPaths.
if (min > max) {
throw APIException.badRequests.minPathsGreaterThanMaxPaths();
}
// pathsPerInitiator must be <= maxPaths.
if (ppi > max) {
throw APIException.badRequests.pathsPerInitiatorGreaterThanMaxPaths();
}
// If the parameters were set, save them in the vpool.
if (minPaths != null) {
vpool.setMinPaths(minPaths);
}
if (maxPaths != null) {
vpool.setNumPaths(maxPaths);
}
if (pathsPerInitiator != null) {
vpool.setPathsPerInitiator(pathsPerInitiator);
}
}
// Validate to make sure continuous copies greater than 1 is not allowed for Vplex
private void validateMaxNativeContinuousCopies(Integer maxMirrors, String highAvailability) {
if (highAvailability != null
&& (VirtualPool.HighAvailabilityType.vplex_local.name().equals(highAvailability) || VirtualPool.HighAvailabilityType.vplex_distributed
.name().equals(highAvailability))) {
if (maxMirrors > 1) {
throw APIException.badRequests.invalidMaxContinuousCopiesForVplex(maxMirrors);
}
}
}
// Validate the High Availability for virtual pool and the mirror vpool.
// If HA for virtual pool is NONE then HA for mirror vpool should be NONE
// If HA for virtual pool is vplex_local then HA for mirror vpool should be vplex_local
// If HA for virtual pool is vplex_distributed then HA for mirror vpool should be vplex_local
// This is required because when only detach operation is performed then BlockMirror/VplexMirror
// is converted into independent volume/vplex volume and virtual pool association do not change.
private void validateMirrorVpool(String vpoolHighAvailability, VirtualPool mirrorVpool) {
if (mirrorVpool.getHighAvailability() != null && vpoolHighAvailability != null
&& vpoolHighAvailability.equals(VirtualPool.HighAvailabilityType.vplex_local.name())
&& !(vpoolHighAvailability.equals(mirrorVpool.getHighAvailability()))) {
throw APIException.badRequests.invalidHighAvailabilityForMirrorVpool(mirrorVpool.getLabel(), mirrorVpool.getHighAvailability(),
vpoolHighAvailability, VirtualPool.HighAvailabilityType.vplex_local.name());
} else if (mirrorVpool.getHighAvailability() != null && vpoolHighAvailability != null
&& vpoolHighAvailability.equals(VirtualPool.HighAvailabilityType.vplex_distributed.name())
&& !(mirrorVpool.getHighAvailability().equals(VirtualPool.HighAvailabilityType.vplex_local.name()))) {
throw APIException.badRequests.invalidHighAvailabilityForMirrorVpool(mirrorVpool.getLabel(), mirrorVpool.getHighAvailability(),
vpoolHighAvailability, VirtualPool.HighAvailabilityType.vplex_local.name());
} else if (mirrorVpool.getHighAvailability() == null && vpoolHighAvailability != null) {
throw APIException.badRequests.invalidHighAvailabilityForMirrorVpool(mirrorVpool.getLabel(), "None", vpoolHighAvailability,
VirtualPool.HighAvailabilityType.vplex_local.name());
} else if (mirrorVpool.getHighAvailability() != null && vpoolHighAvailability == null) {
throw APIException.badRequests.invalidHighAvailabilityForMirrorVpool(mirrorVpool.getLabel(), mirrorVpool.getHighAvailability(),
"None", "None");
}
}
// This method must not persist anything to the DB
private VirtualPool prepareVirtualPool(BlockVirtualPoolParam param,
Map<URI, VpoolRemoteCopyProtectionSettings> remoteSettingsMap,
Map<URI, VpoolProtectionVarraySettings> protectionSettingsMap,
List<VpoolProtectionVarraySettings> protectionSettingsList) {
if (remoteSettingsMap == null) {
remoteSettingsMap = new HashMap<URI, VpoolRemoteCopyProtectionSettings>();
}
if (protectionSettingsMap == null) {
protectionSettingsMap = new HashMap<URI, VpoolProtectionVarraySettings>();
}
if (protectionSettingsList == null) {
protectionSettingsList = new ArrayList<VpoolProtectionVarraySettings>();
}
VirtualPool vpool = new VirtualPool();
vpool.setType(VirtualPool.Type.block.name());
// set common VirtualPool parameters.
populateCommonVirtualPoolCreateParams(vpool, param);
// By default, mirrors and snaps are disabled
vpool.setMaxNativeContinuousCopies(VirtualPool.MAX_DISABLED);
vpool.setMaxNativeSnapshots(VirtualPool.MAX_DISABLED);
if (param.getThinVolumePreAllocationPercentage() != null) {
vpool.setThinVolumePreAllocationPercentage(param.getThinVolumePreAllocationPercentage());
}
if (param.getMultiVolumeConsistency() != null) {
vpool.setMultivolumeConsistency(param.getMultiVolumeConsistency());
}
StringSetMap arrayInfo = new StringSetMap();
if (null != param.getRaidLevels()) {
for (String raidLevel : param.getRaidLevels()) {
arrayInfo.put(VirtualPoolCapabilityValuesWrapper.RAID_LEVEL, raidLevel);
}
}
if (null != param.getSystemType()) {
arrayInfo.put(VirtualPoolCapabilityValuesWrapper.SYSTEM_TYPE, param.getSystemType());
}
if (arrayInfo.isEmpty()) {
arrayInfo.put(VirtualPoolCapabilityValuesWrapper.SYSTEM_TYPE, NONE);
}
vpool.addArrayInfoDetails(arrayInfo);
if (param.getProtection() != null) {
if (param.getProtection().getContinuousCopies() != null) {
URI ccVpoolURI = param.getProtection().getContinuousCopies().getVpool();
if (!NullColumnValueGetter.isNullURI(ccVpoolURI)) {
URI vpoolUri = param.getProtection().getContinuousCopies().getVpool();
ArgValidator.checkUri(vpoolUri);
VirtualPool protectionMirrorVpool = _permissionsHelper.getObjectById(vpoolUri,
VirtualPool.class);
ArgValidator.checkEntity(protectionMirrorVpool, vpoolUri, false);
if (param.getHighAvailability() != null) {
validateMirrorVpool(param.getHighAvailability().getType(), protectionMirrorVpool);
} else {
validateMirrorVpool(null, protectionMirrorVpool);
}
vpool.setMirrorVirtualPool(vpoolUri.toString());
}
}
if ((param.getProtection().getSnapshots() != null)
&& (param.getProtection().getSnapshots().getMaxSnapshots() != null)) {
vpool.setMaxNativeSnapshots(param.getProtection().getSnapshots().getMaxSnapshots());
}
if ((param.getProtection().getContinuousCopies() != null)
&& (param.getProtection().getContinuousCopies().getMaxMirrors() != null)) {
if (param.getHighAvailability() != null) {
validateMaxNativeContinuousCopies(param.getProtection().getContinuousCopies().getMaxMirrors(), param
.getHighAvailability().getType());
if (param.getProtection().getContinuousCopies().getVpool() == null
&& VirtualPool.HighAvailabilityType.vplex_distributed.name().equals(param.getHighAvailability().getType())
&& (param.getProtection().getContinuousCopies().getMaxMirrors() > 0)) {
throw APIException.badRequests.invalidMirrorVpoolForVplexDistributedVpool();
}
}
vpool.setMaxNativeContinuousCopies(param.getProtection().getContinuousCopies().getMaxMirrors());
}
// Special protection-based per-copy information. Which VirtualPool
// to use for that copy,
// what RP policies to use, what journal sizing strategy to
// employ...
if ((param.getProtection().getRecoverPoint() != null) && (param.getProtection().getRecoverPoint().getCopies() != null)) {
ProtectionSourcePolicy sourcePolicy = param.getProtection().getRecoverPoint().getSourcePolicy();
if (sourcePolicy != null) {
vpool.setJournalSize(sourcePolicy.getJournalSize());
vpool.setRpRpoValue(sourcePolicy.getRpoValue());
vpool.setRpRpoType(sourcePolicy.getRpoType());
vpool.setRpCopyMode(sourcePolicy.getRemoteCopyMode());
if (!NullColumnValueGetter.isNullURI(sourcePolicy.getJournalVarray())) {
vpool.setJournalVarray(sourcePolicy.getJournalVarray().toString());
if (!NullColumnValueGetter.isNullURI(sourcePolicy.getJournalVpool())) {
vpool.setJournalVpool(sourcePolicy.getJournalVpool().toString());
} else {
String journalVpoolId = NullColumnValueGetter.getNullStr();
if (param.getHighAvailability() == null || Boolean.TRUE.equals(param.getHighAvailability().getMetroPoint())) {
// In cases of MetroPoint or when high availability is not specified, default the journal virtual pool
// to the parent virtual pool
journalVpoolId = vpool.getId().toString();
} else if (Boolean.FALSE.equals(param.getHighAvailability().getMetroPoint())
&& param.getHighAvailability().getHaVirtualArrayVirtualPool() != null
&& Boolean.TRUE.equals(param.getHighAvailability().getHaVirtualArrayVirtualPool()
.getActiveProtectionAtHASite())) {
// If active protection at HA site is specified (not MetroPoint), our default ha journal vpool should be the
// HA virtual pool.
if (param.getHighAvailability().getHaVirtualArrayVirtualPool().getVirtualPool() != null) {
journalVpoolId = param.getHighAvailability().getHaVirtualArrayVirtualPool().getVirtualPool()
.toString();
}
} else {
// In cases of MetroPoint or when high availability is not specified, default the journal virtual pool
// to the parent virtual pool
journalVpoolId = vpool.getId().toString();
}
vpool.setJournalVpool(journalVpoolId);
}
}
if (param.getHighAvailability() != null && param.getHighAvailability().getMetroPoint() != null
&& param.getHighAvailability().getMetroPoint()) {
if (!NullColumnValueGetter.isNullURI(sourcePolicy.getStandbyJournalVarray())) {
vpool.setStandbyJournalVarray(sourcePolicy.getStandbyJournalVarray().toString());
if (!NullColumnValueGetter.isNullURI(sourcePolicy.getStandbyJournalVpool())) {
vpool.setStandbyJournalVpool(sourcePolicy.getStandbyJournalVpool().toString());
}
}
}
}
StringMap settingsMap = new StringMap();
for (VirtualPoolProtectionVirtualArraySettingsParam settingsParam : param.getProtection().getRecoverPoint().getCopies()) {
VpoolProtectionVarraySettings setting = new VpoolProtectionVarraySettings();
setting.setId(URIUtil.createId(VpoolProtectionVarraySettings.class));
setting.setParent(new NamedURI(vpool.getId(), vpool.getLabel()));
if (settingsParam.getVpool() != null && !String.valueOf(settingsParam.getVpool()).isEmpty()) {
setting.setVirtualPool(settingsParam.getVpool());
}
if (settingsParam.getCopyPolicy() != null) {
setting.setJournalSize(settingsParam.getCopyPolicy().getJournalSize() != null ? settingsParam.getCopyPolicy()
.getJournalSize() : null);
setting.setJournalVarray(settingsParam.getCopyPolicy().getJournalVarray() != null ? settingsParam.getCopyPolicy()
.getJournalVarray() : settingsParam.getVarray());
setting.setJournalVpool(settingsParam.getCopyPolicy().getJournalVpool() != null ? settingsParam.getCopyPolicy()
.getJournalVpool() : settingsParam.getVpool());
}
if (settingsParam.getVarray() != null) {
settingsMap.put(settingsParam.getVarray().toString(), setting.getId().toString());
protectionSettingsMap.put(settingsParam.getVarray(), setting);
}
protectionSettingsList.add(setting);
}
vpool.setProtectionVarraySettings(settingsMap);
}
// SRDF remote protection Settings
if (null != param.getProtection().getRemoteCopies() && null != param.getProtection().getRemoteCopies().getRemoteCopySettings()) {
StringMap remoteCopysettingsMap = new StringMap();
for (VirtualPoolRemoteProtectionVirtualArraySettingsParam remoteSettings : param.getProtection().getRemoteCopies()
.getRemoteCopySettings()) {
VirtualArray remoteVArray = _dbClient.queryObject(VirtualArray.class, remoteSettings.getVarray());
if (null == remoteVArray || remoteVArray.getInactive()) {
throw APIException.badRequests.inactiveRemoteVArrayDetected(remoteSettings.getVarray());
}
VpoolRemoteCopyProtectionSettings remoteCopySettingsParam = new VpoolRemoteCopyProtectionSettings();
remoteCopySettingsParam.setId(URIUtil.createId(VpoolRemoteCopyProtectionSettings.class));
remoteCopySettingsParam.setVirtualArray(remoteSettings.getVarray());
if (remoteCopysettingsMap.containsKey(remoteSettings.getVarray().toString())) {
throw APIException.badRequests.duplicateRemoteSettingsDetected(remoteSettings.getVarray());
}
remoteCopysettingsMap.put(remoteSettings.getVarray().toString(), remoteCopySettingsParam.getId().toString());
// The remote virtual pool is an optional field. If it is not set, this value will be null and the source
// virtual pool will be used to provision the target storage for that remote copy.
remoteCopySettingsParam.setVirtualPool(remoteSettings.getVpool());
if (null != remoteSettings.getRemoteCopyMode()) {
if (!CopyModes.lookup(remoteSettings.getRemoteCopyMode())) {
throw APIException.badRequests.invalidCopyMode(remoteSettings.getRemoteCopyMode());
}
remoteCopySettingsParam.setCopyMode(remoteSettings.getRemoteCopyMode());
}
remoteSettingsMap.put(remoteSettings.getVarray(), remoteCopySettingsParam);
}
vpool.setProtectionRemoteCopySettings(remoteCopysettingsMap);
}
}
// Validate and set high availability.
if (param.getHighAvailability() != null) {
_log.debug("Vpool specifies high availability {}", param.getHighAvailability());
// High availability type must be specified and valid.
vpool.setHighAvailability(param.getHighAvailability().getType());
if (!VirtualPool.vPoolSpecifiesHighAvailability(vpool)) {
throw APIException.badRequests.requiredParameterMissingOrEmpty("highAvailability.type");
}
// Set the MetroPoint value.
vpool.setMetroPoint(param.getHighAvailability().getMetroPoint());
// Auto cross-connect export can work for either local or distributed volumes
// Default if no parameter is supplied is enabled
if (param.getHighAvailability().getAutoCrossConnectExport() != null) {
vpool.setAutoCrossConnectExport(param.getHighAvailability().getAutoCrossConnectExport());
}
// If the high availability type is distributed, then the user
// must also specify the high availability varray. The
// user may also specify the high availability VirtualPool.
if (VirtualPool.HighAvailabilityType.vplex_distributed.name().equals(
param.getHighAvailability().getType())) {
if (param.getHighAvailability().getHaVirtualArrayVirtualPool() == null
|| param.getHighAvailability().getHaVirtualArrayVirtualPool().getVirtualArray() == null) {
throw APIException.badRequests.invalidParameterVirtualPoolHighAvailabilityMismatch(param.getHighAvailability()
.getType());
}
// High availability varray must be specified and valid.
_log.debug("HA varray VirtualPool map specifies the HA varray {}",
param.getHighAvailability().getHaVirtualArrayVirtualPool().getVirtualArray());
VirtualArray haVarray = _dbClient.queryObject(VirtualArray.class,
param.getHighAvailability().getHaVirtualArrayVirtualPool().getVirtualArray());
ArgValidator.checkEntity(haVarray,
param.getHighAvailability().getHaVirtualArrayVirtualPool().getVirtualArray(), false);
String haVarrayId = param.getHighAvailability().getHaVirtualArrayVirtualPool().getVirtualArray()
.toString();
// Check the HA varray VirtualPool, which is not required.
String haVarrayVpoolId = null;
if ((param.getHighAvailability().getHaVirtualArrayVirtualPool().getVirtualPool() != null) &&
(!param.getHighAvailability().getHaVirtualArrayVirtualPool().getVirtualPool().toString().isEmpty())) {
_log.debug("HA varray VirtualPool map specifies the HA vpool {}",
param.getHighAvailability().getHaVirtualArrayVirtualPool().getVirtualPool());
VirtualPool haVpool = _dbClient.queryObject(VirtualPool.class,
param.getHighAvailability().getHaVirtualArrayVirtualPool().getVirtualPool());
ArgValidator.checkEntity(haVpool,
param.getHighAvailability().getHaVirtualArrayVirtualPool().getVirtualPool(), false);
haVarrayVpoolId = param.getHighAvailability().getHaVirtualArrayVirtualPool().getVirtualPool()
.toString();
// Further validate that this VirtualPool is valid for the
// specified high availability varray.
StringSet haVpoolVarrays = haVpool.getVirtualArrays();
if ((haVpoolVarrays != null) && (!haVpoolVarrays.isEmpty()) && !haVpoolVarrays.contains(haVarrayId)) {
throw APIException.badRequests.invalidParameterVirtualPoolNotValidForArray(haVarrayVpoolId, haVarrayId);
}
} else {
_log.debug("HA varray VirtualPool map does not specify HA vpool");
haVarrayVpoolId = NullColumnValueGetter.getNullURI().toString();
}
// Not required, but this flag can be set to specify that the HA varray
// should be used as the RP source array for volume create.
if (param.getHighAvailability().getHaVirtualArrayVirtualPool().getActiveProtectionAtHASite() != null
&& param.getHighAvailability().getHaVirtualArrayVirtualPool().getActiveProtectionAtHASite()) {
vpool.setHaVarrayConnectedToRp(haVarrayId);
}
StringMap haVarrayVirtualPoolMap = new StringMap();
haVarrayVirtualPoolMap.put(haVarrayId, haVarrayVpoolId);
vpool.setHaVarrayVpoolMap(haVarrayVirtualPoolMap);
} else {
final VirtualArrayVirtualPoolMapEntry haVirtualArrayVirtualPool = param.getHighAvailability()
.getHaVirtualArrayVirtualPool();
if ((haVirtualArrayVirtualPool != null)
&& ((haVirtualArrayVirtualPool.getVirtualArray() != null) || (haVirtualArrayVirtualPool.getVirtualPool() != null))) {
throw APIException.badRequests.invalidParameterVirtualPoolAndVirtualArrayNotApplicableForHighAvailabilityType(param
.getHighAvailability().getType());
}
}
}
// Set expandable
if (param.getExpandable() != null) {
vpool.setExpandable(param.getExpandable());
}
if (param.getFastExpansion() != null) {
vpool.setFastExpansion(param.getFastExpansion());
}
if (null != param.getAutoTieringPolicyName() && !param.getAutoTieringPolicyName().isEmpty()) {
vpool.setAutoTierPolicyName(param.getAutoTieringPolicyName());
}
if (param.getCompressionEnabled() != null) {
vpool.setCompressionEnabled(param.getCompressionEnabled());
}
if (null != param.getDriveType()) {
vpool.setDriveType(param.getDriveType());
} else {
vpool.setDriveType(NONE);
}
// Set the min/max paths an paths per initiator
validateAndSetPathParams(vpool, param.getMaxPaths(), param.getMinPaths(), param.getPathsPerInitiator());
if (null != param.getUniquePolicyNames()) {
vpool.setUniquePolicyNames(param.getUniquePolicyNames());
}
// set limit for host bandwidth
if (param.getHostIOLimitBandwidth() != null) {
vpool.setHostIOLimitBandwidth(param.getHostIOLimitBandwidth());
}
// set limit for host i/o
if (param.getHostIOLimitIOPs() != null) {
vpool.setHostIOLimitIOPs(param.getHostIOLimitIOPs());
}
// set placement policy
if (param.getPlacementPolicy() != null) {
vpool.setPlacementPolicy(param.getPlacementPolicy());
}
// set dedup capable or not
if (null != param.getDedupCapable()) {
vpool.setDedupCapable(param.getDedupCapable());
}
return vpool;
}
}