/*
* Copyright (c) 2016 EMC Corporation
* All Rights Reserved
*/
package com.emc.storageos.volumecontroller.impl.validators.vplex;
import java.net.URI;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.emc.storageos.db.client.DbClient;
import com.emc.storageos.db.client.model.BlockObject;
import com.emc.storageos.db.client.model.ExportMask;
import com.emc.storageos.db.client.model.Initiator;
import com.emc.storageos.db.client.model.StorageSystem;
import com.emc.storageos.db.client.util.NullColumnValueGetter;
import com.emc.storageos.exceptions.DeviceControllerException;
import com.emc.storageos.util.VPlexUtil;
import com.emc.storageos.volumecontroller.impl.utils.ExportMaskUtils;
import com.emc.storageos.volumecontroller.impl.validators.Validator;
import com.emc.storageos.volumecontroller.impl.validators.ValidatorConfig;
import com.emc.storageos.volumecontroller.impl.validators.ValidatorLogger;
import com.emc.storageos.vplex.api.VPlexApiClient;
import com.emc.storageos.vplex.api.VPlexApiFactory;
import com.emc.storageos.vplex.api.VPlexStorageViewInfo;
import com.emc.storageos.vplexcontroller.VPlexControllerUtils;
public class VplexExportMaskValidator extends AbstractVplexValidator implements Validator {
private Logger log = LoggerFactory.getLogger(VplexExportMaskValidator.class);
private StorageSystem vplex;
private ExportMask mask;
private Collection<URI> volumesToValidate = null;
private Collection<Initiator> initiatorsToValidate = null;
private VPlexApiClient client = null;
private String id = null; // identifying string for ExportMask
public VplexExportMaskValidator(DbClient dbClient, ValidatorConfig config, ValidatorLogger logger, StorageSystem vplex,
ExportMask mask) {
super(dbClient, config, logger);
this.vplex = vplex;
this.mask = mask;
id = String.format("%s (%s)(%s)", mask.getMaskName(), mask.getNativeId(), mask.getId().toString());
}
@Override
public boolean validate() throws Exception {
if (mask.getInactive()) {
log.info("Export mask inactive: " + id);
// no such export mask, ignore, but don't indicate pass
return false;
}
log.info("Initiating validation of Vplex ExportMask: " + id);
VPlexStorageViewInfo storageView = null;
try {
client = VPlexControllerUtils.getVPlexAPIClient(VPlexApiFactory.getInstance(), vplex, getDbClient());
// This will throw an exception if the cluster name cannot be determined
String vplexClusterName = VPlexUtil.getVplexClusterName(mask, vplex.getId(), client, getDbClient());
// This will throw an exception if the StorageView cannot be found
storageView = client.getStorageView(vplexClusterName, mask.getMaskName());
if (volumesToValidate != null) {
validateNoAdditionalVolumes(storageView);
}
if (initiatorsToValidate != null) {
validateNoAdditionalInitiators(storageView);
}
} catch (Exception ex) {
if (storageView == null) {
// Not finding the storage view is not an error, so delete can be idempotent
log.warn(String.format("Storage View %s cannot be located on VPLEX", id));
return false;
}
log.error("Unexpected exception validating ExportMask: " + ex.getMessage(), ex);
if (getValidatorConfig().isValidationEnabled()) {
throw DeviceControllerException.exceptions.unexpectedCondition(
"Unexpected exception validating ExportMask: " + ex.getMessage());
}
}
if (getValidatorLogger().hasErrors()) {
return false;
}
log.info("Vplex ExportMask validation complete: " + id);
return true;
}
/**
* Validates the hardware has no additional volumes than were passed in the volumesToValidate list.
* Uses the virtualVolumeWWNMap to retrieve the volume WWNs and match against hardware.
*
* @param storageView
* -- VPlexStorageViewInfo
*/
private void validateNoAdditionalVolumes(VPlexStorageViewInfo storageView) {
List<? extends BlockObject> bos = BlockObject.fetchAll(getDbClient(), volumesToValidate);
Set<String> storageViewWwns = storageView.getWwnToHluMap().keySet();
for (BlockObject bo : bos) {
if (bo == null || bo.getInactive()) {
continue;
}
String boWwn = bo.getWWN();
if (NullColumnValueGetter.isNotNullValue(boWwn)) {
if (storageViewWwns.contains(boWwn)) {
// Remove matched WWNs
storageViewWwns.remove(boWwn);
} else {
log.info(String.format("Database volume/snap %s (%s - %s) is not in StorageView [%s]",
bo.getId(), bo.getNativeGuid(), bo.getWWN(), storageView.getName()));
}
}
}
// Any remaining WWNs in storageViewWwns had no matching volume, and therefore are error
for (String wwn : storageViewWwns) {
String volumeId = String.format("%s (%s)", wwn, storageView.getVolumeNameForWWN(wwn));
getValidatorLogger().logDiff(id, "virtual-volume/snap WWN", ValidatorLogger.NO_MATCHING_ENTRY, volumeId);
}
}
/**
* Validate the hardware has no additional initiators than were passed in the initiatorsToValidate list.
* Uses the Initiator PWWNs in the Storage View to match against initiator port WWN.
*
* @param storageView
* -- VPlexStorageViewInfo
*/
private void validateNoAdditionalInitiators(VPlexStorageViewInfo storageView) {
Set<String> storageViewPwwns = new HashSet<String>(storageView.getInitiatorPwwns());
// Don't validate against RP masks
if (ExportMaskUtils.isBackendExportMask(getDbClient(), mask)) {
log.info("validation against RP masks is disabled.");
return;
}
for (Initiator initiator : initiatorsToValidate) {
if (initiator == null || initiator.getInactive()) {
continue;
}
String initiatorPwwn = Initiator.normalizePort(initiator.getInitiatorPort());
if (storageViewPwwns.contains(initiatorPwwn)) {
storageViewPwwns.remove(initiatorPwwn);
} else {
log.info(String.format("Database initiator %s (%s) not in StorageView", initiator.getId(), initiatorPwwn));
}
}
// Any remaining WWNs in storageViewPwwns had no matching initiator, and therefore are error
for (String wwn : storageViewPwwns) {
getValidatorLogger().logDiff(id, "initiator port WWN", ValidatorLogger.NO_MATCHING_ENTRY, wwn);
}
}
public StorageSystem getVplex() {
return vplex;
}
public void setVplex(StorageSystem vplex) {
this.vplex = vplex;
}
public ExportMask getMask() {
return mask;
}
public void setMask(ExportMask mask) {
this.mask = mask;
}
public Collection<URI> getVolumesToValidate() {
return volumesToValidate;
}
public void setVolumesToValidate(Collection<URI> volumesToValidate) {
this.volumesToValidate = volumesToValidate;
}
public Collection<Initiator> getInitiatorsToValidate() {
return initiatorsToValidate;
}
public void setInitiatorsToValidate(Collection<Initiator> initiatorsToValidate) {
this.initiatorsToValidate = initiatorsToValidate;
}
}