/*
* Copyright (c) 2015 EMC Corporation
* All Rights Reserved
*/
package com.emc.storageos.networkcontroller.impl;
import java.net.URI;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.UUID;
import org.apache.curator.framework.recipes.locks.InterProcessLock;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.CollectionUtils;
import com.emc.storageos.coordinator.client.service.CoordinatorClient;
import com.emc.storageos.customconfigcontroller.CustomConfigConstants;
import com.emc.storageos.customconfigcontroller.DataSourceFactory;
import com.emc.storageos.customconfigcontroller.impl.CustomConfigHandler;
import com.emc.storageos.db.client.DbClient;
import com.emc.storageos.db.client.DbModelClient;
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.DiscoveredDataObject;
import com.emc.storageos.db.client.model.ExportGroup;
import com.emc.storageos.db.client.model.ExportMask;
import com.emc.storageos.db.client.model.FCEndpoint;
import com.emc.storageos.db.client.model.FCZoneReference;
import com.emc.storageos.db.client.model.HostInterface;
import com.emc.storageos.db.client.model.Initiator;
import com.emc.storageos.db.client.model.Network;
import com.emc.storageos.db.client.model.NetworkSystem;
import com.emc.storageos.db.client.model.Operation;
import com.emc.storageos.db.client.model.StoragePort;
import com.emc.storageos.db.client.model.StorageProtocol.Transport;
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.Volume;
import com.emc.storageos.db.client.model.ZoneInfo;
import com.emc.storageos.db.client.model.ZoneInfoMap;
import com.emc.storageos.db.client.util.CommonTransformerFunctions;
import com.emc.storageos.db.client.util.DataObjectUtils;
import com.emc.storageos.db.client.util.NullColumnValueGetter;
import com.emc.storageos.db.client.util.StringMapUtil;
import com.emc.storageos.db.client.util.StringSetUtil;
import com.emc.storageos.db.client.util.WWNUtility;
import com.emc.storageos.db.exceptions.DatabaseException;
import com.emc.storageos.db.joiner.Joiner;
import com.emc.storageos.exceptions.DeviceControllerException;
import com.emc.storageos.model.ResourceOperationTypeEnum;
import com.emc.storageos.networkcontroller.NetworkController;
import com.emc.storageos.networkcontroller.NetworkFCContext;
import com.emc.storageos.networkcontroller.NetworkFCZoneInfo;
import com.emc.storageos.networkcontroller.exceptions.NetworkDeviceControllerException;
import com.emc.storageos.networkcontroller.impl.mds.Zone;
import com.emc.storageos.networkcontroller.impl.mds.ZoneMember;
import com.emc.storageos.networkcontroller.impl.mds.ZoneMember.ConnectivityMemberType;
import com.emc.storageos.networkcontroller.impl.mds.ZoneUpdate;
import com.emc.storageos.networkcontroller.impl.mds.ZoneWwnAlias;
import com.emc.storageos.networkcontroller.impl.mds.ZoneWwnAliasUpdate;
import com.emc.storageos.networkcontroller.impl.mds.Zoneset;
import com.emc.storageos.security.audit.AuditLogManager;
import com.emc.storageos.services.OperationTypeEnum;
import com.emc.storageos.svcs.errorhandling.model.ServiceCoded;
import com.emc.storageos.svcs.errorhandling.model.ServiceError;
import com.emc.storageos.util.ExportUtils;
import com.emc.storageos.util.InvokeTestFailure;
import com.emc.storageos.util.NetworkLite;
import com.emc.storageos.util.NetworkUtil;
import com.emc.storageos.volumecontroller.AsyncTask;
import com.emc.storageos.volumecontroller.ControllerException;
import com.emc.storageos.volumecontroller.TaskCompleter;
import com.emc.storageos.volumecontroller.impl.BiosCommandResult;
import com.emc.storageos.volumecontroller.impl.ControllerUtils;
import com.emc.storageos.volumecontroller.impl.block.taskcompleter.ZoneReferencesRemoveCompleter;
import com.emc.storageos.volumecontroller.impl.monitoring.RecordableBourneEvent;
import com.emc.storageos.volumecontroller.impl.monitoring.RecordableEventManager;
import com.emc.storageos.volumecontroller.impl.monitoring.cim.enums.RecordType;
import com.emc.storageos.volumecontroller.impl.utils.ExportMaskUtils;
import com.emc.storageos.workflow.Workflow;
import com.emc.storageos.workflow.WorkflowService;
import com.emc.storageos.workflow.WorkflowStepCompleter;
import com.google.common.collect.Collections2;
public class NetworkDeviceController implements NetworkController {
private DbClient _dbClient;
private CoordinatorClient _coordinator;
private static final Logger _log = LoggerFactory.getLogger(NetworkDeviceController.class);
private Map<String, NetworkSystemDevice> _devices;
private NetworkScheduler _networkScheduler;
private static final String EVENT_SERVICE_TYPE = "network";
private static final String EVENT_SERVICE_SOURCE = "NetworkDeviceController";
public static final String ZONESET_QUERY_FILTER = "filter:";
@Autowired
private AuditLogManager _auditMgr;
@Autowired
private DbModelClient dbModelClient;
@Autowired
private DataSourceFactory dataSourceFactory;
@Autowired
private CustomConfigHandler customConfigHandler;
private RecordableEventManager _eventManager;
public void setEventManager(RecordableEventManager eventManager) {
_eventManager = eventManager;
}
public void setDbClient(DbClient dbc) {
_dbClient = dbc;
}
public void setCoordinator(CoordinatorClient coordinator) {
_coordinator = coordinator;
}
public void setDevices(Map<String, NetworkSystemDevice> deviceInterfaces) {
_devices = deviceInterfaces;
}
private NetworkSystemDevice getDevice(String deviceType) {
return _devices.get(deviceType);
}
public void setNetworkScheduler(NetworkScheduler networkScheduler) {
_networkScheduler = networkScheduler;
}
public NetworkScheduler getNetworkScheduler() {
return _networkScheduler;
}
/**
* Returns the NetworkDevice from the db
*
* @param network device URI
* @return NetworkDevice
* @throws ControllerException
*/
private NetworkSystem getDeviceObject(URI network) throws ControllerException {
NetworkSystem networkDev = null;
try {
networkDev = _dbClient.queryObject(NetworkSystem.class, network);
} catch (Exception e) {
throw NetworkDeviceControllerException.exceptions.getDeviceObjectFailedNotFound(
network.toString(), e);
}
// Verify non-null network device returned from the database client.
if (networkDev == null) {
throw NetworkDeviceControllerException.exceptions.getDeviceObjectFailedNull(
network.toString());
}
return networkDev;
}
/**
* Save the NetworkSystem object after updates.
*
* @param networkSystem
* @throws ControllerException
*/
private void saveDeviceObject(NetworkSystem networkSystem) throws ControllerException {
try {
_dbClient.persistObject(networkSystem);
} catch (DatabaseException ex) {
throw NetworkDeviceControllerException.exceptions.saveDeviceObjectFailed(
networkSystem.getId().toString(), ex);
}
}
@Override
public void connectNetwork(URI network) throws ControllerException {
BiosCommandResult result = doConnect(network);
boolean failed = false;
if (!result.isCommandSuccess()) {
_log.error("Connect failed to {}", network);
failed = true;
// To Do - mark device inactive or take in status to set failure.
} else {
String msg = MessageFormat.format("Connected to Network Device {0} at {1}", result.getMessage(), new Date());
_log.info(msg);
}
// Update status on the NetworkSystem
NetworkSystem networkObj = getDeviceObject(network);
networkObj.setCompatibilityStatus(failed ?
DiscoveredDataObject.CompatibilityStatus.INCOMPATIBLE.name()
: DiscoveredDataObject.CompatibilityStatus.COMPATIBLE.name());
saveDeviceObject(networkObj);
}
private BiosCommandResult doConnect(URI network) throws ControllerException {
// Retrieve the storage device info from the database.
NetworkSystem networkObj = getDeviceObject(network);
// Verify non-null network device returned from the database client.
// Will not return null
// Get the file device reference for the type of file device managed
// by the controller.
NetworkSystemDevice networkDevice = getDevice(networkObj.getSystemType());
if (networkDevice == null) {
throw NetworkDeviceControllerException.exceptions.doConnectFailed(
network.toString(), networkObj.getSystemType());
}
return networkDevice.doConnect(networkObj);
}
@Override
public void disconnectNetwork(URI network) throws ControllerException {
// Nothing to do
}
@Override
public void discoverNetworkSystems(AsyncTask[] tasks)
throws ControllerException {
// this is now handled by the discovery framework
throw new UnsupportedOperationException();
}
@Override
public void testCommunication(URI network, String task)
throws ControllerException {
try {
BiosCommandResult result = doConnect(network);
if (result.isCommandSuccess()) {
Operation op = new Operation();
op.setMessage(result.getMessage());
_dbClient.ready(NetworkSystem.class, network, task);
} else {
String opName = ResourceOperationTypeEnum.UPDATE_NETWORK.getName();
ServiceError serviceError = NetworkDeviceControllerException.errors.testCommunicationFailed(opName,
network.toString());
_dbClient.error(NetworkSystem.class, network, task, serviceError);
}
} catch (Exception e) {
_log.error("Exception while trying update task status");
try {
String opName = ResourceOperationTypeEnum.UPDATE_NETWORK.getName();
ServiceError serviceError = NetworkDeviceControllerException.errors.testCommunicationFailedExc(opName,
network.toString(), e);
_dbClient.error(NetworkSystem.class, network, task, serviceError);
} catch (DatabaseException ioe) {
_log.error(ioe.getMessage());
}
}
}
@Override
public List<String> getFabricIds(URI uri) throws ControllerException {
NetworkSystem device = getDeviceObject(uri);
// Get the file device reference for the type of file device managed
// by the controller.
NetworkSystemDevice networkDevice = getDevice(device.getSystemType());
if (networkDevice == null) {
throw NetworkDeviceControllerException.exceptions.getFabricIdsFailedNull(device.getSystemType());
}
try {
List<String> fabricIds = networkDevice.getFabricIds(device);
return fabricIds;
} catch (Exception ex) {
Date date = new Date();
throw NetworkDeviceControllerException.exceptions.getFabricIdsFailedExc(
uri.toString(), date.toString(), ex);
}
}
@Override
public List<Zoneset> getZonesets(URI uri, String fabricId, String fabricWwn, String zoneName, boolean excludeMembers,
boolean excludeAliases) throws ControllerException {
NetworkSystem device = getDeviceObject(uri);
// Get the file device reference for the type of file device managed
// by the controller.
NetworkSystemDevice networkDevice = getDevice(device.getSystemType());
if (networkDevice == null) {
throw NetworkDeviceControllerException.exceptions.getZonesetsFailedNull(device.getSystemType());
}
try {
List<Zoneset> zonesets = networkDevice.getZonesets(device, fabricId, fabricWwn, zoneName, excludeMembers, excludeAliases);
// NOTE! The RMI infrastructure doesn't know how to deal with CIMObjectPaths, even if they are in
// Object pointers, so remove them here!
for (Zoneset zs : zonesets) {
zs.setCimObjectPath(null);
for (Zone zo : zs.getZones()) {
zo.setCimObjectPath(null);
for (ZoneMember zm : zo.getMembers()) {
zm.setCimObjectPath(null);
}
}
}
return zonesets;
} catch (Exception ex) {
Date date = new Date();
throw NetworkDeviceControllerException.exceptions.getZonesetsFailedExc(uri.toString(), date.toString(), ex);
}
}
@Override
public void addSanZones(URI uri, String fabricId, String fabricWwn, List<Zone> zones, boolean activateZones,
String taskId) throws ControllerException {
NetworkSystem device = getDeviceObject(uri);
// Lock to prevent concurrent operations on the same VSAN / FABRIC.
InterProcessLock fabricLock = NetworkFabricLocker.lockFabric(fabricId, _coordinator);
try {
// Get the file device reference for the type of file device managed
// by the controller.
NetworkSystemDevice networkDevice = getDevice(device.getSystemType());
if (networkDevice == null) {
throw NetworkDeviceControllerException.exceptions.addSanZonesFailedNull(device.getSystemType());
}
BiosCommandResult result = networkDevice.addZones(device, zones, fabricId, fabricWwn, activateZones);
setStatus(NetworkSystem.class, device.getId(), taskId, result.isCommandSuccess(), result.getServiceCoded());
_auditMgr.recordAuditLog(null, null, EVENT_SERVICE_TYPE,
OperationTypeEnum.ADD_SAN_ZONE, System.currentTimeMillis(),
AuditLogManager.AUDITLOG_SUCCESS, AuditLogManager.AUDITOP_END,
device.getId().toString(), device.getLabel(), device.getPortNumber(), device.getUsername(),
device.getSmisProviderIP(), device.getSmisPortNumber(), device.getSmisUserName(), device.getSmisUseSSL());
} catch (Exception ex) {
ServiceError serviceError = NetworkDeviceControllerException.errors.addSanZonesFailedExc(
device.getSystemType(), ex);
_dbClient.error(NetworkSystem.class, device.getId(), taskId, serviceError);
} finally {
NetworkFabricLocker.unlockFabric(fabricId, fabricLock);
}
}
/**
* Add/remove a group of zones as given by their NetworkFabricInfo structures.
* ALL fabricInfos must be using the same NetworkDevice, and the same fabricId. There is a higher level
* subroutine to split complex requests into sets of requests with the same NetworkDevice and fabricId.
*
* @param device NetworkDevice
* @param fabricId String
* @param exportGroupUri The ExportGroup URI. Used for reference counting.
* @param fabricInfos - Describe each zone.
* @param activateZones - activate active zoneset after zones change
* @param retryAltNetworkDevice - a boolean to indicate if re-try to be done.
* This is to stop this function from running again after the alternate
* system is retried once.
* @return BiosCommandResult
* @throws ControllerException
*/
private BiosCommandResult addRemoveZones(NetworkSystem device, String fabricId, String fabricWwn,
URI exportGroupUri, List<NetworkFCZoneInfo> fabricInfos, boolean doRemove,
boolean retryAltNetworkDevice)
throws ControllerException {
BiosCommandResult result = null;
String taskId = UUID.randomUUID().toString();
List<Zone> zones = new ArrayList<Zone>();
// Make the zone operations. Don't make the same zone more than once,
// as determined by its key. The same zone shows up multiple times because it
// must be recorded for each volume in the FCZoneReference table.
HashSet<String> keySet = new HashSet<String>();
for (NetworkFCZoneInfo fabricInfo : fabricInfos) {
String key = fabricInfo.makeEndpointsKey();
if (false == keySet.contains(key)) {
keySet.add(key);
// neither create nor delete zones found on the switch
if (fabricInfo.isExistingZone()) {
_log.info("Zone {} will not be created or removed on {}, as it is not vipr created. ", fabricInfo.getZoneName(),
fabricInfo.toString());
continue; // neither create nor delete zones found on the switch
}
// Don't actually remove the zone if it's not the last reference
if (doRemove && !fabricInfo._isLastReference) {
_log.info("Zone {} will not be removed on {}, as still the zone is used to expose other volumes in export groups ",
fabricInfo.getZoneName(), fabricInfo.toString());
continue;
}
Zone zone = new Zone(fabricInfo.getZoneName());
for (String address : fabricInfo.getEndPoints()) {
ZoneMember member = new ZoneMember(address, ConnectivityMemberType.WWPN);
zone.getMembers().add(member);
}
zones.add(zone);
}
}
// Get the network device reference for the type of network device managed
// by the controller.
NetworkSystemDevice networkDevice = getDevice(device.getSystemType());
if (networkDevice == null) {
throw NetworkDeviceControllerException.exceptions.addRemoveZonesFailedNull(
device.getSystemType());
}
// Lock to prevent concurrent operations on the same VSAN / FABRIC.
InterProcessLock fabricLock = NetworkFabricLocker.lockFabric(fabricId, _coordinator);
try {
if (doRemove) { /* Removing zones */
result = networkDevice.removeZones(device, zones, fabricId, fabricWwn, true);
if (result.isCommandSuccess()) {
String refKey = null;
try {
for (NetworkFCZoneInfo fabricInfo : fabricInfos) {
URI fcZoneReferenceId = fabricInfo.getFcZoneReferenceId();
if (NullColumnValueGetter.isNullURI(fcZoneReferenceId)) {
_log.info("fcZoneReferenceId corresponding to zone info {} is null. Nothing to remove.",
fabricInfo.toString());
continue;
}
FCZoneReference ref = _dbClient.queryObject(FCZoneReference.class, fcZoneReferenceId);
if (ref != null) {
refKey = ref.getPwwnKey();
_dbClient.markForDeletion(ref);
_log.info(String.format("Remove FCZoneReference key: %s volume %s id %s",
ref.getPwwnKey(), ref.getVolumeUri(), ref.getId().toString()));
if(!zones.isEmpty()){
recordZoneEvent(ref, OperationTypeEnum.REMOVE_SAN_ZONE.name(),
OperationTypeEnum.REMOVE_SAN_ZONE.getDescription());
}
}
}
} catch (DatabaseException ex) {
_log.error("Could not persist FCZoneReference: " + refKey);
}
}
} else { /* Adding zones */
result = networkDevice.addZones(device, zones, fabricId, fabricWwn, true);
if (result.isCommandSuccess()) {
String refKey = null;
try {
for (NetworkFCZoneInfo fabricInfo : fabricInfos) {
String[] newOrExisting = new String[1];
FCZoneReference ref = addZoneReference(exportGroupUri, fabricInfo, newOrExisting);
fabricInfo.setFcZoneReferenceId(ref.getId()); // this is needed for rollback
_log.info(String.format(
"%s FCZoneReference key: %s volume %s group %s",
newOrExisting[0], ref.getPwwnKey(), ref.getVolumeUri(), exportGroupUri));
if(!zones.isEmpty()){
recordZoneEvent(ref, OperationTypeEnum.ADD_SAN_ZONE.name(),
OperationTypeEnum.ADD_SAN_ZONE.getDescription());
}
}
} catch (DatabaseException ex) {
_log.error("Could not persist FCZoneReference: " + refKey);
}
}
}
// Update the FCZoneInfo structures if we changed device state for rollback.
Map<String, String> map = (Map<String, String>) result.getObjectList().get(0);
for (NetworkFCZoneInfo info : fabricInfos) {
if (NetworkSystemDevice.SUCCESS.equals(map.get(info.getZoneName()))) {
info.setCanBeRolledBack(true);
}
}
if (!result.isCommandSuccess()) {
ServiceError serviceError = NetworkDeviceControllerException.errors.addRemoveZonesFailed(
device.getSystemType());
setStatus(ExportGroup.class, exportGroupUri, taskId, false, serviceError);
} else {
setStatus(ExportGroup.class, exportGroupUri, taskId, true, null);
}
return result;
} catch (ControllerException ex) {
String operation = doRemove ? "Remove Zones" : "Add Zones";
_log.info(String.format("waiting for 2 min before retrying %s with alternate device", operation));
try {
Thread.sleep(1000 * 120);
} catch (InterruptedException e) {
_log.warn("Thread sleep interrupted. Allowing to continue without sleep");
}
NetworkFCZoneInfo fabricInfo = fabricInfos.get(0);
URI primaryUri = fabricInfo.getNetworkDeviceId();
URI altUri = fabricInfo.getAltNetworkDeviceId();
// If we took an error, attempt a retry with an alternate device if possible.
if (altUri != null && retryAltNetworkDevice) {
NetworkFabricLocker.unlockFabric(fabricId, fabricLock);
fabricLock = null;
_log.error("Zone operation failed using device: " + primaryUri + " retrying with alternate device: " + altUri);
fabricInfo.setNetworkDeviceId(altUri);
device = getDeviceObject(altUri);
return addRemoveZones(device, fabricId, fabricWwn, exportGroupUri, fabricInfos, doRemove, false);
} else {
if (result != null) {
if (!result.isCommandSuccess()) {
ServiceError serviceError = NetworkDeviceControllerException.errors.addRemoveZonesFailed(
device.getSystemType());
setStatus(ExportGroup.class, exportGroupUri, taskId, false, serviceError);
} else {
setStatus(ExportGroup.class, exportGroupUri, taskId, true, null);
}
}
throw ex;
}
} finally {
NetworkFabricLocker.unlockFabric(fabricId, fabricLock);
}
}
/**
* Adds/removes a bunch of zones based on their NetworkFCZoneInfo structures.
* They are split into groups and subgroups, first by the device used for zoning, and then by the fabricId to be zoned.
* Then each subgroup is processed separately.
*
* @param exportGroupUri
* @param fabricInfos
* @return BiosCommandResult
* @throws ControllerException
*/
public BiosCommandResult addRemoveZones(URI exportGroupUri, List<NetworkFCZoneInfo> fabricInfos, boolean doRemove)
throws ControllerException {
// Group the fabric infos together based on which devices should zone them.
Map<URI, NetworkSystem> deviceId2NetworkSystem = new HashMap<URI, NetworkSystem>();
Map<URI, List<NetworkFCZoneInfo>> deviceId2NetworkFabricInfos = new HashMap<URI, List<NetworkFCZoneInfo>>();
for (NetworkFCZoneInfo fabricInfo : fabricInfos) {
URI deviceId = fabricInfo.getNetworkDeviceId();
URI altDeviceId = fabricInfo.getAltNetworkDeviceId();
NetworkSystem device = null;
// Determine device. The device structures are cached in deviceId2NetworkSystem
device = deviceId2NetworkSystem.get(deviceId);
if (device == null) {
device = getDeviceObject(deviceId);
if (device != null && device.getInactive() == false) {
deviceId2NetworkSystem.put(deviceId, device);
} else if (altDeviceId != null) {
device = deviceId2NetworkSystem.get(altDeviceId);
if (device == null) {
device = getDeviceObject(altDeviceId);
if (device != null && device.getInactive() == false) {
deviceId2NetworkSystem.put(altDeviceId, device);
}
}
}
}
if (device == null) {
throw NetworkDeviceControllerException.exceptions.addRemoveZonesFailedNoDev(deviceId.toString());
}
List<NetworkFCZoneInfo> finfos = deviceId2NetworkFabricInfos.get(device.getId());
if (finfos == null) {
finfos = new ArrayList<NetworkFCZoneInfo>();
deviceId2NetworkFabricInfos.put(device.getId(), finfos);
}
finfos.add(fabricInfo);
}
// Now loop through each device, splitting the collection of fabric infos by fabric ID/WWN.
StringBuilder messageBuffer = new StringBuilder();
for (URI deviceId : deviceId2NetworkFabricInfos.keySet()) {
NetworkSystem device = deviceId2NetworkSystem.get(deviceId);
Map<String, List<NetworkFCZoneInfo>> fabric2FabricInfos = new HashMap<String, List<NetworkFCZoneInfo>>();
Map<String, NetworkLite> fabricId2Network = new HashMap<String, NetworkLite>();
List<NetworkFCZoneInfo> finfos = deviceId2NetworkFabricInfos.get(deviceId);
for (NetworkFCZoneInfo fabricInfo : finfos) {
String fabricId = fabricInfo.getFabricId();
String fabricWwn = fabricInfo.getFabricWwn();
String key = (fabricWwn != null) ? fabricWwn : fabricId;
updateAltDeviceid(fabricInfo, fabricId, fabricWwn, key, fabricId2Network);
List<NetworkFCZoneInfo> singleFabricInfos = fabric2FabricInfos.get(key);
if (singleFabricInfos == null) {
singleFabricInfos = new ArrayList<NetworkFCZoneInfo>();
fabric2FabricInfos.put(key, singleFabricInfos);
}
singleFabricInfos.add(fabricInfo);
}
// Now for each fabric, do the zoning.
for (String key : fabric2FabricInfos.keySet()) {
List<NetworkFCZoneInfo> singleFabricInfos = fabric2FabricInfos.get(key);
String fabricId = singleFabricInfos.get(0).getFabricId();
String fabricWwn = singleFabricInfos.get(0).getFabricWwn();
BiosCommandResult rslt = addRemoveZones(device, fabricId, fabricWwn, exportGroupUri, singleFabricInfos, doRemove, true);
if (messageBuffer.length() > 0) {
messageBuffer.append("; ");
}
messageBuffer.append(rslt.getMessage());
}
}
BiosCommandResult result = BiosCommandResult.createSuccessfulResult();
return result;
}
/**
* Ensures every fabricInfo has its altNetworkDevice that is not null when
* the calling code did not already supply a value for this field.
*
* @param fabricInfo the fabric info to be updated
* @param fabricId the fabric id of the network where zoning will be performed
* @param fabricWWN the WWN of the network where zoning will be performed
* @param key the key used to save already retrieved networks in the map
* this key is the fabric WWN unless it is null, then it is the fabric id
* @param fabricId2Network a map where retrieved networks are saved between calls
* into this function. This is done to avoid repeated db retrieves of same objects
*/
private void updateAltDeviceid(NetworkFCZoneInfo fabricInfo, String fabricId, String fabricWWN,
String key, Map<String, NetworkLite> fabricId2Network) {
if (fabricInfo != null && fabricInfo.getAltNetworkDeviceId() == null) {
if (fabricId2Network.get(key) == null) {
NetworkLite network = NetworkUtil.getNetworkLiteByFabricId(fabricId, fabricWWN, _dbClient);
if (network != null) {
fabricId2Network.put(key, network);
URI id = fabricInfo.getNetworkDeviceId();
for (String strUri : network.getNetworkSystems()) {
if (!strUri.equals(id.toString())) {
id = URI.create(strUri);
break;
}
}
fabricInfo.setAltNetworkDeviceId(id);
}
}
}
}
@Override
public void removeSanZones(URI uri, String fabricId, String fabricWwn, List<Zone> zones, boolean activateZones,
String taskId) throws ControllerException {
NetworkSystem device = getDeviceObject(uri);
// Lock to prevent concurrent operations on the same VSAN / FABRIC.
InterProcessLock fabricLock = NetworkFabricLocker.lockFabric(fabricId, _coordinator);
try {
// Get the file device reference for the type of file device managed
// by the controller.
NetworkSystemDevice networkDevice = getDevice(device.getSystemType());
if (networkDevice == null) {
throw NetworkDeviceControllerException.exceptions.removeSanZonesFailedNull(
device.getSystemType());
}
BiosCommandResult result = networkDevice.removeZones(device, zones, fabricId, fabricWwn, activateZones);
setStatus(NetworkSystem.class, device.getId(), taskId, result.isCommandSuccess(), result.getServiceCoded());
_auditMgr.recordAuditLog(null, null, EVENT_SERVICE_TYPE,
OperationTypeEnum.REMOVE_SAN_ZONE, System.currentTimeMillis(),
AuditLogManager.AUDITLOG_SUCCESS, AuditLogManager.AUDITOP_END,
device.getId().toString(), device.getLabel(), device.getPortNumber(), device.getUsername(),
device.getSmisProviderIP(), device.getSmisPortNumber(), device.getSmisUserName(), device.getSmisUseSSL());
} catch (Exception ex) {
ServiceError serviceError = NetworkDeviceControllerException.errors.removeSanZonesFailedExc(
device.getSystemType(), ex);
_dbClient.error(NetworkSystem.class, device.getId(), taskId, serviceError);
} finally {
NetworkFabricLocker.unlockFabric(fabricId, fabricLock);
}
}
/**
* Remove a zone.
*
* @param volUri URI of the Volume
* @param fabricInfo NetworkFabricInfo generated by NetworkScheduler
* @return BiosCommandResult
*/
public BiosCommandResult removeZone(URI volUri, NetworkFCZoneInfo fabricInfo, boolean activateZones) throws ControllerException {
ServiceError serviceError = NetworkDeviceControllerException.errors.zoningFailedArgs(
volUri.toString());
BiosCommandResult result = BiosCommandResult.createErrorResult(serviceError);
List<Zone> zones = new ArrayList<Zone>();
Zone zone = new Zone(fabricInfo.getZoneName());
zones.add(zone);
String taskId = UUID.randomUUID().toString();
for (String address : fabricInfo.getEndPoints()) {
ZoneMember member = new ZoneMember(address, ConnectivityMemberType.WWPN);
zone.getMembers().add(member);
}
// Lock to prevent concurrent operations on the same VSAN / FABRIC.
InterProcessLock fabricLock = NetworkFabricLocker.lockFabric(fabricInfo.getFabricId(), _coordinator);
try {
NetworkSystem device = getDeviceObject(fabricInfo.getNetworkDeviceId());
// Get the file device reference for the type of file device managed
// by the controller.
NetworkSystemDevice networkDevice = getDevice(device.getSystemType());
if (networkDevice == null) {
throw NetworkDeviceControllerException.exceptions.removeZoneFailedNull(device.getSystemType());
}
if (fabricInfo.isLastReference() == true && !fabricInfo.isExistingZone()) {
result = networkDevice.removeZones(device, zones, fabricInfo.getFabricId(), fabricInfo.getFabricWwn(), activateZones);
} else {
// This is not the last reference, just mark our FCZoneReference for deletion
result = BiosCommandResult.createSuccessfulResult();
}
if (result.isCommandSuccess()) {
if (fabricInfo.getFcZoneReferenceId() != null) {
try { // Mark our FcZoneReference object for removal
FCZoneReference reference = _dbClient.queryObject(FCZoneReference.class, fabricInfo.getFcZoneReferenceId());
if (reference != null) {
_dbClient.markForDeletion(reference);
recordZoneEvent(reference, OperationTypeEnum.REMOVE_SAN_ZONE.name(),
OperationTypeEnum.REMOVE_SAN_ZONE.getDescription());
}
} catch (Exception ex) {
_log.error("Can't mark object for removal: " + fabricInfo.getFcZoneReferenceId());
}
}
}
if (!result.isCommandSuccess()) {
ServiceError svcError = NetworkDeviceControllerException.errors.removeZoneFailed(
volUri.toString(), device.getSystemType());
setStatus(Volume.class, volUri, taskId, false, svcError);
} else {
setStatus(Volume.class, volUri, taskId, true, null);
}
} catch (ControllerException ex) {
_log.info("waiting for 2 min before retrying removeZone with alternate device");
try {
Thread.sleep(1000 * 120);
} catch (InterruptedException e) {
_log.warn("Thread sleep interrupted. Allowing to continue without sleep");
}
URI primaryUri = fabricInfo.getNetworkDeviceId();
URI altUri = fabricInfo.getAltNetworkDeviceId();
if (altUri != null && altUri != primaryUri) {
NetworkFabricLocker.unlockFabric(fabricInfo.getFabricId(), fabricLock);
fabricLock = null;
_log.error("Remove Zone failed using device: " + primaryUri + " retrying with alternate device: " + altUri);
fabricInfo.setNetworkDeviceId(altUri);
return removeZone(volUri, fabricInfo, activateZones);
} else {
ServiceError svcError = NetworkDeviceControllerException.errors.removeZoneFailedExc(
volUri.toString());
setStatus(Volume.class, volUri, taskId, false, svcError);
throw ex;
}
} finally {
NetworkFabricLocker.unlockFabric(fabricInfo.getFabricId(), fabricLock);
}
return result;
}
@Override
public void updateSanZones(URI uri, String fabricId, String fabricWwn, List<ZoneUpdate> zones, boolean activateZones,
String taskId) throws ControllerException {
NetworkSystem device = getDeviceObject(uri);
// Lock to prevent concurrent operations on the same VSAN / FABRIC.
InterProcessLock fabricLock = NetworkFabricLocker.lockFabric(fabricId, _coordinator);
try {
// Get the file device reference for the type of file device managed
// by the controller.
NetworkSystemDevice networkDevice = getDevice(device.getSystemType());
if (networkDevice == null) {
throw NetworkDeviceControllerException.exceptions.updateSanZonesFailedNull(device.getSystemType());
}
BiosCommandResult result = networkDevice.updateZones(device, zones, fabricId, fabricWwn, activateZones);
setStatus(NetworkSystem.class, device.getId(), taskId, result.isCommandSuccess(), result.getServiceCoded());
_auditMgr.recordAuditLog(null, null, EVENT_SERVICE_TYPE,
OperationTypeEnum.UPDATE_SAN_ZONE, System.currentTimeMillis(),
AuditLogManager.AUDITLOG_SUCCESS, AuditLogManager.AUDITOP_END,
device.getId().toString(), device.getLabel(), device.getPortNumber(), device.getUsername(),
device.getSmisProviderIP(), device.getSmisPortNumber(), device.getSmisUserName(), device.getSmisUseSSL());
} catch (Exception ex) {
ServiceError serviceError = NetworkDeviceControllerException.errors.updateSanZonesFailedExc(
device.getSystemType(), ex);
_dbClient.error(NetworkSystem.class, device.getId(), taskId, serviceError);
} finally {
NetworkFabricLocker.unlockFabric(fabricId, fabricLock);
}
}
@Override
public void activateSanZones(URI uri, String fabricId, String fabricWwn, String taskId) throws ControllerException {
NetworkSystem device = getDeviceObject(uri);
// Lock to prevent concurrent operations on the same VSAN / FABRIC.
InterProcessLock fabricLock = NetworkFabricLocker.lockFabric(fabricId, _coordinator);
try {
// Get the file device reference for the type of file device managed
// by the controller.
NetworkSystemDevice networkDevice = getDevice(device.getSystemType());
if (networkDevice == null) {
throw NetworkDeviceControllerException.exceptions.updateSanZonesFailedNull(device.getSystemType());
}
BiosCommandResult result = networkDevice.activateZones(device, fabricId, fabricWwn);
ServiceError serviceError = NetworkDeviceControllerException.errors.activateSanZonesFailed(
uri.toString(), device.getSystemType());
setStatus(NetworkSystem.class, device.getId(), taskId, result.isCommandSuccess(), serviceError);
_auditMgr.recordAuditLog(null, null, EVENT_SERVICE_TYPE,
OperationTypeEnum.ACTIVATE_SAN_ZONE, System.currentTimeMillis(),
AuditLogManager.AUDITLOG_SUCCESS, AuditLogManager.AUDITOP_END,
device.getId().toString(), device.getLabel(), device.getPortNumber(), device.getUsername(),
device.getSmisProviderIP(), device.getSmisPortNumber(), device.getSmisUserName(), device.getSmisUseSSL());
} catch (Exception ex) {
ServiceError serviceError = NetworkDeviceControllerException.errors.activateSanZonesFailedExc(
device.getSystemType(), ex);
_dbClient.error(NetworkSystem.class, device.getId(), taskId, serviceError);
} finally {
NetworkFabricLocker.unlockFabric(fabricId, fabricLock);
}
}
private void setStatus(Class clz, URI uri, String taskId, boolean success, ServiceCoded serviceCode) {
try {
if (success) {
_dbClient.ready(clz, uri, taskId);
} else {
_dbClient.error(clz, uri, taskId, serviceCode);
}
} catch (Exception ex) {
_log.error("Exception trying to setStatus: " + ex.getLocalizedMessage());
}
}
/**
* Make a String key from two URIs.
*
* @param uri1
* @param uri2
* @return
*/
private String make2UriKey(URI uri1, URI uri2) {
String part1 = "null";
String part2 = "null";
if (uri1 != null) {
part1 = uri1.toString();
}
if (uri2 != null) {
part2 = uri2.toString();
}
return part1 + "+" + part2;
}
@Override
public void deleteNetworkSystem(URI network, String taskId)
throws ControllerException {
try {
NetworkSystem networkDevice = getDeviceObject(network);
URIQueryResultList epUriList = new URIQueryResultList();
_dbClient.queryByConstraint(ContainmentConstraint.Factory
.getNetworkSystemFCPortConnectionConstraint(network), epUriList);
while (epUriList.iterator().hasNext()) {
FCEndpoint connection = _dbClient.queryObject(FCEndpoint.class, epUriList.iterator().next());
if (connection != null) {
_dbClient.removeObject(connection);
}
}
List<URI> tzUriList = _dbClient.queryByType(Network.class, true);
NetworkDiscoveryWorker worker =
new NetworkDiscoveryWorker(
getDevice(networkDevice.getSystemType()), _dbClient);
worker.setCoordinator(_coordinator);
for (URI tzUri : tzUriList) {
Network tz = _dbClient.queryObject(Network.class, tzUri);
if (tz != null && (tz.getNetworkSystems() != null
&& tz.getNetworkSystems().contains(network.toString()))) {
worker.removeNetworkSystemTransportZone(tz, network.toString());
}
}
if (taskId != null) {
_dbClient.ready(NetworkSystem.class, network, taskId);
_auditMgr.recordAuditLog(null, null, EVENT_SERVICE_TYPE,
OperationTypeEnum.DELETE_NETWORK_SYSTEM, System.currentTimeMillis(),
AuditLogManager.AUDITLOG_SUCCESS, AuditLogManager.AUDITOP_END,
networkDevice.getId().toString(), networkDevice.getLabel(), networkDevice.getPortNumber(),
networkDevice.getUsername(), networkDevice.getSmisProviderIP(), networkDevice.getSmisPortNumber(),
networkDevice.getSmisUserName(), networkDevice.getSmisUseSSL());
}
} catch (Exception ex) {
String msg = MessageFormat.format("Exception encountered while removing FC Port Connection for {0} because: {1}", network,
ex.getLocalizedMessage());
_log.error(msg);
if (taskId != null) {
try {
ServiceError serviceError = NetworkDeviceControllerException.errors.deleteNetworkSystemFailed(
network.toString(), ex);
_dbClient.error(NetworkSystem.class, network, taskId, serviceError);
} catch (DatabaseException e) {
_log.error(e.getMessage());
}
}
}
}
/**
* Returns true if zoning is required for the given VirtualArray
*
* @param varrayURI - The VirtualArray URI.
*/
private boolean isZoningRequired(URI varrayURI) {
VirtualArray virtualArray = _dbClient.queryObject(VirtualArray.class, varrayURI);
if (virtualArray == null) {
throw DeviceControllerException.exceptions.virtualArrayNotFound();
}
return NetworkScheduler.isZoningRequired(_dbClient, virtualArray);
}
/**
* Sets the completed Workflow state to read or error depending on the
* BiosCommandResult received from lower layers.
*
* @param completer task completer
* @param token String Workflow stepId
* @param result BiosCommandResult
* @param warningMessage - optional warning message if completed successfully (can be null)
*/
private void completeWorkflowState(TaskCompleter completer, String token, String operation, BiosCommandResult result,
String warningMessage) {
// Update the workflow state.
if (Operation.Status.valueOf(result.getCommandStatus()).equals(Operation.Status.ready)) {
if (warningMessage != null && !warningMessage.isEmpty()) {
WorkflowService.getInstance().postTaskWarningMessage(token, warningMessage);
if (completer != null) {
completer.ready(_dbClient);
} else {
WorkflowStepCompleter.stepSucceeded(token, warningMessage);
}
} else {
if (completer != null) {
completer.ready(_dbClient);
} else {
WorkflowStepCompleter.stepSucceded(token);
}
}
} else if (Operation.Status.valueOf(result.getCommandStatus()).equals(Operation.Status.error)) {
ServiceError svcError = NetworkDeviceControllerException.errors.zoneOperationFailed(
operation, result.getMessage());
if (completer != null) {
completer.error(_dbClient, svcError);
} else {
WorkflowStepCompleter.stepFailed(token, svcError);
}
}
}
/**
* Returns true if zoning required; sets Workflow Step status to
* executing or suceeded depending.
*
* @param token - Workflow step id.
* @param varrayURI URI of virtual array
* @return true if zoning required
*/
private boolean checkZoningRequired(String token, URI varrayURI) {
if (!isZoningRequired(varrayURI)) {
WorkflowStepCompleter.stepSucceded(token);
return false;
} else {
WorkflowStepCompleter.stepExecuting(token);
return true;
}
}
// ===========================================================================================================
// External Interfaces to BlockDeviceController
// ===========================================================================================================
/**
* Creates a Workflow Method for creating zones for a list of ExportMasks.
*
* @see zoneExportMasksCreate
* @param exportGroupURI -- ExportGroup URI
* @param exportMaskURIs -- A list of ExportMask URIs.
* @param volumeURIs -- A Collection of ExportMask URIs.
* @return - boolean true if successful, false if not
*/
public Workflow.Method zoneExportMasksCreateMethod(
URI exportGroupURI, List<URI> exportMaskURIs, Collection<URI> volumeURIs) {
return new Workflow.Method("zoneExportMasksCreate", exportGroupURI, exportMaskURIs, volumeURIs);
}
/**
* Creates zones for one or more newly created ExportMasks.
* Each ExportMask is assumed to have a zoningMap that indicates which
* initiators should be zoned to which ports.
* This routine will create zones for each of the ExportMasks
* according to their zoningMap. The zones will assume to be
* used to access the Volumes in the indicated collection.
* Note: these arguments (except token) must match zoneExportMasksCreateMethod above.
* This routine executes as a Workflow Step.
*
* @param URI exportGroupURI -- ExportGroup URI
* @param exportMaskURIs -- A list of ExportMask URIs to be zoned.
* @param volumeURIs -- A collection of Volume URIs to be zoned
* @param token -- The workflow step id.
* @return boolean true if succesful, false if not
*/
public boolean zoneExportMasksCreate(URI exportGroupURI,
List<URI> exportMaskURIs, Collection<URI> volumeURIs, String token) {
ExportGroup exportGroup = null;
try {
exportGroup = _dbClient
.queryObject(ExportGroup.class, exportGroupURI);
_log.info(String.format("Entering zoneExportMasksCreate for ExportGroup: %s (%s)",
exportGroup.getLabel(), exportGroup.getId()));
if (exportMaskURIs == null && exportGroup != null && exportGroup.getExportMasks() != null) {
// If the ExportMasks aren't specified, do all in the ExportGroup.
exportMaskURIs = new ArrayList<URI>(Collections2.transform(
exportGroup.getExportMasks(),
CommonTransformerFunctions.FCTN_STRING_TO_URI));
}
if (_log.isDebugEnabled()) {
for (URI maskURI : exportMaskURIs) {
ExportMask mask = _dbClient.queryObject(ExportMask.class,
maskURI);
if (mask != null) {
_log.debug(String.format("ExportMask %s (%s) storage %s",
mask.getMaskName(), mask.getId(), mask.getStorageDevice()));
}
}
}
} catch (Exception ex) {
_log.error("Exception zoning Export Masks", ex);
ServiceError svcError = NetworkDeviceControllerException.errors.zoneExportGroupCreateFailed(
ex.getMessage(), ex);
WorkflowStepCompleter.stepFailed(token, svcError);
}
return doZoneExportMasksCreate(exportGroup, exportMaskURIs, volumeURIs, token, true);
}
/**
* Handles ExportGroup / ExportMask create, as well as add volume
*
* @param exportGroup
* @param exportMaskURIs
* @param volumeURIs
* @param token
* @param checkZones Flag to enable or disable zoning check on a Network System
* @return
*/
private boolean doZoneExportMasksCreate(ExportGroup exportGroup,
List<URI> exportMaskURIs, Collection<URI> volumeURIs, String token, boolean checkZones) {
BiosCommandResult result = null;
NetworkFCContext context = new NetworkFCContext();
try {
if (!checkZoningRequired(token, exportGroup.getVirtualArray())) {
return true;
}
volumeURIs = removeDuplicateURIs(volumeURIs);
// In the case of export group create, we created this step before we even
// knew which ExportMask we need to update. There's a chance we don't have
// to update any at all, in which case we can success out.
if (exportMaskURIs == null || exportMaskURIs.isEmpty()) {
WorkflowStepCompleter.stepSucceded(token);
return true;
}
// Compute the zones for the ExportGroup
// [hala] make sure we do not rollback existing zones
Map<String, List<Zone>> zonesMap = new HashMap<String, List<Zone>>();
if (checkZones) {
zonesMap = getExistingZonesMap(exportMaskURIs, token);
}
List<NetworkFCZoneInfo> zones = _networkScheduler.
getZoningTargetsForExportMasks(exportGroup, exportMaskURIs, volumeURIs, zonesMap, checkZones, _dbClient);
context.getZoneInfos().addAll(zones);
logZones(zones);
// If there are no zones to do, we were successful.
if (!checkZones) {
if (!context.getZoneInfos().isEmpty()) {
String[] newOrExisting = new String[1];
for (NetworkFCZoneInfo zoneInfo : context.getZoneInfos()) {
addZoneReference(exportGroup.getId(), zoneInfo, newOrExisting);
}
}
result = BiosCommandResult.createSuccessfulResult();
} else {
// Now call addZones to add all the required zones.
InvokeTestFailure.internalOnlyInvokeTestFailure(InvokeTestFailure.ARTIFICIAL_FAILURE_047);
result = addRemoveZones(exportGroup.getId(),
context.getZoneInfos(), false);
InvokeTestFailure.internalOnlyInvokeTestFailure(InvokeTestFailure.ARTIFICIAL_FAILURE_048);
}
// Save our zone infos in case we want to rollback.
WorkflowService.getInstance().storeStepData(token, context);
// Update the workflow state.
completeWorkflowState(null, token, "zoneExportMaskCreate", result, null);
} catch (Exception ex) {
_log.error("Exception zoning Export Masks", ex);
// Save our zone infos in case we want to rollback.
WorkflowService.getInstance().storeStepData(token, context);
ServiceError svcError = NetworkDeviceControllerException.errors.zoneExportGroupCreateFailed(
ex.getMessage(), ex);
WorkflowStepCompleter.stepFailed(token, svcError);
}
return (result != null && result.isCommandSuccess());
}
/**
* This function wraps the logic for getting zones from the network system. The zones may have already
* been read in previous workflow steps during port assignment. To avoid another call into the network
* systems, these zones are stored in the workflow. In the case of "add initiators" workflow, only the
* new initiators's zones are read by port allocation. In this case, the new initiators zones are loaded
* from the workflow while the old initiators zones are retrieved from the network system.
*
* @param exportMaskUris -- the URI of the export mask being zones
* @param token -- the workflow step id
* @return a map of initiatorPort to the list if zones that already exist on the network system
*/
private Map<String, List<Zone>> getExistingZonesMap(Collection<URI> exportMaskUris, String token) {
// get existing zones from the switch, first check if the zones were retrieved by previous steps and cached in the workflow
Map<String, List<Zone>> zonesMap = (Map<String, List<Zone>>) WorkflowService.getInstance().loadWorkflowData(token, "zonemap");
// if the existing zones were not already retrieved and cached by other steps, retrieve them now
if (zonesMap == null) {
zonesMap = new HashMap<String, List<Zone>>();
} else {
_log.info("Existing zones were found in the workflow for initiators {}", zonesMap.keySet());
}
List<Initiator> exportInitiators = ExportUtils.getExportMasksInitiators(exportMaskUris, _dbClient);
Iterator<Initiator> itr = exportInitiators.iterator();
Initiator ini = null;
List<String> otherInitiators = new ArrayList<String>();
while (itr.hasNext()) {
ini = itr.next();
if (zonesMap.containsKey(ini.getInitiatorPort())) {
itr.remove();
} else {
otherInitiators.add(ini.getInitiatorPort());
}
}
if (!exportInitiators.isEmpty()) {
_log.info("Getting existing zones from network system for {} ", otherInitiators);
zonesMap.putAll(getInitiatorsZones(exportInitiators));
}
return zonesMap;
}
/**
* Get the Workflow.Method for zoneExportAddVolumesMethod.
*
* @param exportGroupURI
* @param exportMaskURIs
* @param volumeURIs
* @return Workflow.Method
*/
public Workflow.Method zoneExportAddVolumesMethod(URI exportGroupURI,
List<URI> exportMaskURIs, Collection<URI> volumeURIs) {
return new Workflow.Method("zoneExportAddVolumes", exportGroupURI, exportMaskURIs, volumeURIs);
}
/**
* Called when volumes are added to ExportMasks. This is important because we
* need to add FCZoneReferences for each of the new volumes so that in the
* future a zone will not be deleted until all volumes using the zone have
* been removed. References will only be added for zones with ports belonging
* to the array containing the volume.
* Note: these arguments (except token) must match zoneExportAddVolumesMethod above.
* This routine executes as a Workflow Step.
*
* @param exportGroupURI -- ExportGroup URI
* @param exportMaskURIs -- List of Export Mask URIs receiving the Volumes
* @param volumeURIs -- Collection of Volume URIs
* @param token -- Step ID
* @return
*/
public boolean zoneExportAddVolumes(URI exportGroupURI,
List<URI> exportMaskURIs, Collection<URI> volumeURIs, String token) {
ExportGroup exportGroup = _dbClient
.queryObject(ExportGroup.class, exportGroupURI);
_log.info(String.format
("Entering zoneExportAddVolumes for ExportGroup: %s (%s) Volumes: %s",
exportGroup.getLabel(), exportGroup.getId(), volumeURIs.toString()));
// Check if Zoning needs to be checked from system config
// call the doZoneExportMasksCreate to check/create/remove zones with the flag
String addZoneWhileAddingVolume = customConfigHandler.getComputedCustomConfigValue(
CustomConfigConstants.ZONE_ADD_VOLUME,
CustomConfigConstants.GLOBAL_KEY, null);
// Default behavior is we allow zoning checks against the Network System
Boolean addZoneOnDeviceOperation = true;
_log.info("zoneExportAddVolumes checking for custom config value {} to skip zoning checks : (Default) : {}",
addZoneWhileAddingVolume, addZoneOnDeviceOperation);
if (addZoneWhileAddingVolume != null) {
addZoneOnDeviceOperation = Boolean.valueOf(addZoneWhileAddingVolume);
_log.info("Boolean convereted of : {} : returned by Config handler as : {} ",
addZoneWhileAddingVolume, addZoneOnDeviceOperation);
} else {
_log.info("Config handler returned null for value so going by default value {}", addZoneOnDeviceOperation);
}
_log.info("zoneExportAddVolumes checking for custom config value {} to skip zoning checks : (Custom Config) : {}",
addZoneWhileAddingVolume, addZoneOnDeviceOperation);
return doZoneExportMasksCreate(exportGroup, exportMaskURIs, volumeURIs, token,
addZoneOnDeviceOperation);
}
/**
* Adds a specified list of Initiators to each of the specified ExportMasks.
*
* @param exportGroupURI
* @param exportMasksToInitiators - Map of ExportMap URI to list of Initiator URIs
* @param exportGroup
* @param exportMasksToInitiators
* @return
*/
public Workflow.Method zoneExportAddInitiatorsMethod(URI exportGroupURI,
Map<URI, List<URI>> exportMasksToInitiators) {
return new Workflow.Method("zoneExportAddInitiators", exportGroupURI, exportMasksToInitiators);
}
/**
* Handles zoning for ExportGroup.exportAddInitiator() call.
* This call adds zones for each of the initiators in the exportMasksToInitiators map.
* The ports are determined from the ExportMask:
* 1) if the ExportMask.zoningMap has an entry for an initiator, the ports are taken
* from there, otherwise
* 2) the ports are taken from ExportMask.storagePorts.
* Note: these arguments (except token) must match zoneExportAddInitiatorsMethod above.
* This routine executes as a Workflow Step.
*
* @param exportGroup -- Used for the zone references.
* @param exportMasksToInitiators - Map of ExportMap URI to list of Initiator URIs
* @param token Workflow step id
* @return true if success, false otherwise
* @throws ControllerException
*/
public boolean zoneExportAddInitiators(URI exportGroupURI,
Map<URI, List<URI>> exportMasksToInitiators,
String token) throws ControllerException {
NetworkFCContext context = new NetworkFCContext();
boolean status = false;
ExportGroup exportGroup = _dbClient
.queryObject(ExportGroup.class, exportGroupURI);
_log.info(String.format("Entering zoneExportAddInitiators for ExportGroup: %s (%s)",
exportGroup.getLabel(), exportGroup.getId()));
try {
if (!checkZoningRequired(token, exportGroup.getVirtualArray())) {
return true;
}
// get existing zones on the switch
Map<String, List<Zone>> zonesMap = getExistingZonesMap(exportMasksToInitiators.keySet(), token);
// Compute zones that are required.
List<NetworkFCZoneInfo> zoneInfos =
_networkScheduler.getZoningTargetsForInitiators(exportGroup, exportMasksToInitiators, zonesMap, _dbClient);
context.getZoneInfos().addAll(zoneInfos);
logZones(zoneInfos);
// If there are no zones to do, we were successful.
if (context.getZoneInfos().isEmpty()) {
WorkflowStepCompleter.stepSucceded(token);
return true;
}
// Now call addZones to add all the required zones.
InvokeTestFailure.internalOnlyInvokeTestFailure(InvokeTestFailure.ARTIFICIAL_FAILURE_058);
BiosCommandResult result = addRemoveZones(exportGroup.getId(),
context.getZoneInfos(), false);
InvokeTestFailure.internalOnlyInvokeTestFailure(InvokeTestFailure.ARTIFICIAL_FAILURE_059);
status = result.isCommandSuccess();
// Save our zone infos in case we want to rollback.
WorkflowService.getInstance().storeStepData(token, context);
// Update the workflow state.
completeWorkflowState(null, token, "zoneExportAddInitiators", result, null);
return status;
} catch (Exception ex) {
_log.error("Exception zoning add initiators", ex);
ServiceError svcError = NetworkDeviceControllerException.errors.zoneExportAddInitiatorsFailed(
ex.getMessage(), ex);
WorkflowStepCompleter.stepFailed(token, svcError);
return status;
}
}
/**
* Method called when deleting one or more ExportMasks
* @param zoningParams -- one or more NetworkZoningParam blocks corresponding to ExportMask
* @param volumeURIs -- list of Volumes that are affected
* @return status, true if SUCCESS
*/
public Workflow.Method zoneExportMasksDeleteMethod(
List<NetworkZoningParam> zoningParams, Collection<URI> volumeURIs) {
return new Workflow.Method("zoneExportMasksDelete", zoningParams, volumeURIs);
}
/**
* Deletes all the zones in the zoningMaps supplied in the zoningParams.
* @param zoningParams -- zoning parameter block corresponding to ExportMask
* @param volumeURIs -- list of Volumes that are affected
* @param stepId
* @return status, true if SUCCESS
*/
public boolean zoneExportMasksDelete(
List<NetworkZoningParam> zoningParams, Collection<URI> volumeURIs, String stepId) {
NetworkZoningParam zoningParam = zoningParams.get(0);
_log.info(String.format("Entering zoneExportMasksDelete for ExportGroup: %s",
zoningParam.getExportGroupDisplay()));
return doZoneExportMasksDelete(zoningParams, volumeURIs, stepId);
}
/**
* Remove ExportMasks or delete Volumes from ExportMasks.
*
* @param zoningParamss -- List of NetworkZoningParam block obtained from ExportMask
* @param volumeURIs -- Collection of all volumes being removed
* @param stepId - Step id.
* @return status -- true if success
*/
private boolean doZoneExportMasksDelete(List<NetworkZoningParam> zoningParams,
Collection<URI> volumeURIs, String stepId) {
NetworkFCContext context = new NetworkFCContext();
TaskCompleter taskCompleter = null;
boolean status = false;
try {
if (zoningParams.isEmpty()) {
_log.info("zoningParams is empty, returning");
WorkflowStepCompleter.stepSucceded(stepId);
return true;
}
URI exportGroupId = zoningParams.get(0).getExportGroupId();
URI virtualArray = zoningParams.get(0).getVirtualArray();
if (!checkZoningRequired(stepId, virtualArray)) {
return true;
}
volumeURIs = removeDuplicateURIs(volumeURIs);
// Compute the zones for the ExportGroup
context.setAddingZones(false);
List<NetworkFCZoneInfo> zones = _networkScheduler.
getZoningRemoveTargets(zoningParams, volumeURIs);
context.getZoneInfos().addAll(zones);
logZones(zones);
// If there are no zones to do, we were successful.
if (context.getZoneInfos().isEmpty()) {
_log.info("No zoning information provided.");
WorkflowStepCompleter.stepSucceded(stepId);
return true;
}
// Generate warning message if required.
String warningMessage = generateExistingZoneWarningMessages(context.getZoneInfos());
// Determine what needs to be rolled back.
List<NetworkFCZoneInfo> lastReferenceZoneInfo = new ArrayList<NetworkFCZoneInfo>();
List<NetworkFCZoneInfo> rollbackList = new ArrayList<NetworkFCZoneInfo>();
for (NetworkFCZoneInfo info : context.getZoneInfos()) {
if (info.canBeRolledBack()) {
// If we were adding zones, we set lastReference so it will be deleted.
if (context.isAddingZones()) {
info.setLastReference(true);
lastReferenceZoneInfo.add(info);
}
rollbackList.add(info);
}
}
// Create a local completer to handle DB cleanup in the case of failure.
taskCompleter = new ZoneReferencesRemoveCompleter(NetworkUtil.getFCZoneReferences(context.getZoneInfos()), true, stepId);
InvokeTestFailure.internalOnlyInvokeTestFailure(InvokeTestFailure.ARTIFICIAL_FAILURE_007);
// Now call removeZones to remove all the required zones.
BiosCommandResult result = addRemoveZones(exportGroupId, context.getZoneInfos(), true);
status = result.isCommandSuccess();
InvokeTestFailure.internalOnlyInvokeTestFailure(InvokeTestFailure.ARTIFICIAL_FAILURE_008);
List<URI> exportMaskURIs = new ArrayList<URI>();
for (NetworkZoningParam zoningParam : zoningParams) {
exportMaskURIs.add(zoningParam.getMaskId());
}
// This code isn't ideal, but its too risky to rework it here.
// It cleans up the zoningMap entries in the associated ExportMasks
// if they still exist.
if (status && !lastReferenceZoneInfo.isEmpty()) {
_log.info("There seems to be last reference zones that were removed, clean those zones from the ExportMask zoning map.");
updateZoningMap(lastReferenceZoneInfo, exportGroupId, exportMaskURIs);
}
// Update the workflow state.
completeWorkflowState(taskCompleter, stepId, "zoneExportMasksDelete", result, warningMessage.toString());
_log.info("Successfully removed zones for ExportGroup: {}", exportGroupId.toString());
} catch (Exception ex) {
_log.error("Exception zoning delete Export Masks", ex);
WorkflowService.getInstance().storeStepData(stepId, context);
ServiceError svcError = NetworkDeviceControllerException.errors
.zoneExportGroupDeleteFailed(ex.getMessage(), ex);
if (taskCompleter != null) {
taskCompleter.error(_dbClient, svcError);
} else {
WorkflowStepCompleter.stepFailed(stepId, svcError);
}
}
return status;
}
/**
* This method updates zoning map. This will mostly be called when volume(s) is/are removed
* from the ExportMask which is shared across two/more varrays and varrays do not have same
* storage ports which results in creating zoning based on the ports in the varray.
*
* lastReferenceZoneInfo contains the zones that were removed from the device,
* according to this if there is initiator in the zoningMap with just one storage port
* for which zone is removed then that entry is removed from the zoningMap.
* If initiator has more than one storage port in the zoningMap for the initiator then
* only storage port for which zone is removed is removed from the zoning map.
* ExportMasks with ImmutableZoningMap set are skipped.
*
* @param lastReferenceZoneInfo list of NetworkFCZoneInfo for the zones that are removed.
* @param exportGroupURI reference to exportGroup
* @param exportMaskURIs list of reference to exportMask
*/
private void updateZoningMap(List<NetworkFCZoneInfo> lastReferenceZoneInfo, URI exportGroupURI, List<URI> exportMaskURIs) {
List<URI> emURIs = new ArrayList<URI>();
if (exportMaskURIs == null || exportMaskURIs.isEmpty()) {
ExportGroup exportGroup = _dbClient.queryObject(ExportGroup.class, exportGroupURI);
List<ExportMask> exportMasks = ExportMaskUtils.getExportMasks(_dbClient, exportGroup);
if (exportGroup != null && !exportMasks.isEmpty()) {
for (ExportMask mask : exportMasks) {
emURIs.add(mask.getId());
}
}
} else {
emURIs.addAll(exportMaskURIs);
}
for (URI emURI : emURIs) {
ExportMask exportMask = _dbClient.queryObject(ExportMask.class, emURI);
if (exportMask != null && !exportMask.getInactive()
&& !exportMask.fetchDeviceDataMapEntry(
ExportMask.DeviceDataMapKeys.ImmutableZoningMap.name()).contains(Boolean.TRUE.toString())) {
for (NetworkFCZoneInfo zoneInfo : lastReferenceZoneInfo) {
StringSetMap existingZoningMap = exportMask.getZoningMap();
if (exportMask.getVolumes() == null) {
continue;
}
Set<String> exportMaskVolumes = exportMask.getVolumes().keySet();
if (existingZoningMap != null
&& zoneInfo.getVolumeId() != null
&& exportMaskVolumes.contains(zoneInfo.getVolumeId().toString())
&& zoneInfo.getEndPoints().size() == 2) {
Initiator initiator = NetworkUtil.findInitiatorInDB(zoneInfo.getEndPoints().get(0), _dbClient);
List<StoragePort> storagePorts = NetworkUtil.findStoragePortsInDB(zoneInfo.getEndPoints().get(1), _dbClient);
for (StoragePort storagePort : storagePorts) {
if (initiator != null && storagePort != null) {
for (String initiatorId : existingZoningMap.keySet()) {
if (initiator.getId().toString().equals(initiatorId)) {
StringSet ports = existingZoningMap.get(initiatorId);
if (ports != null) {
if (ports.contains(storagePort.getId().toString())) {
ports.remove(storagePort.getId().toString());
if (ports.isEmpty()) {
exportMask.removeZoningMapEntry(initiatorId);
_log.info("Removing zoning map entry for initiator {}, in exportmask {}",
initiatorId, emURI);
} else {
exportMask.addZoningMapEntry(initiatorId, ports);
_log.info("Removing storagePort " + storagePort.getId()
+ " from zoning map for initiator " + initiatorId
+ " in export mask " + emURI);
}
}
}
}
}
}
}
}
_dbClient.persistObject(exportMask);
}
}
}
}
/**
* Returns a Workflow Method for zoneExportRemoveVolumes
*
* Removes the indicated volumes from the zones given by the zoning parameters.
* If the FCZoneReferences for a zone are all removed (and there are no existing
* volumes in the associated export mask), the zones will be removed from switch.
* @param zoningParams -- List of NetworkZoningParam structure containing needed information
* derived from the ExportMasks
* @param volumeURIs -- List of volumes being removed from the masks
* @param stepId -- stepId from Workflow
* @return Workflow.Method
*/
public Workflow.Method zoneExportRemoveVolumesMethod(
List<NetworkZoningParam> zoningParams, Collection<URI> volumeURIs) {
return new Workflow.Method("zoneExportRemoveVolumes", zoningParams, volumeURIs);
}
/**
* Removes the indicated volumes from the zones given by the zoning parameters.
* If the FCZoneReferences for a zone are all removed (and there are no existing
* volumes in the associated export mask), the zones will be removed from switch.
* @param zoningParams -- List of NetworkZoningParam structure containing needed information
* derived from the ExportMasks
* @param volumeURIs -- List of volumes being removed from the masks
* @param stepId -- stepId from Workflow
* @return true if zones are removed
*/
public boolean zoneExportRemoveVolumes(
List<NetworkZoningParam> zoningParams, Collection<URI> volumeURIs, String stepId) {
NetworkZoningParam zoningParam = zoningParams.get(0);
_log.info(String.format(
"Entering zoneExportRemoveVolumes for ExportGroup: %s Volumes: %s",
zoningParam.getExportGroupDisplay(), volumeURIs.toString()));
return doZoneExportMasksDelete(zoningParams, volumeURIs, stepId);
}
/**
* Generate Workflow.Method for zoneExportRemoveInitiators
* @param zoningParam -- Zoning parameters list
* @return Workflow.Method for zoneExportRemoveInitiators
*/
public Workflow.Method zoneExportRemoveInitiatorsMethod(
List<NetworkZoningParam> zoningParam) {
return new Workflow.Method("zoneExportRemoveInitiators", zoningParam);
}
/**
* This will remove zones for all the initiators in the NetworkZoningParam zoneMap.
* Note: these arguments (except stepId) must match zoneExportRemoveInitiatorsMethod above.
* This routine executes as a Workflow Step.
*
* @param zoningParams -- List of NetworkZoningParam zoning parameter blocks corresponding to ExportMasks
* @param stepId -- step id in Workflow
* @return
* @throws ControllerException
*/
public boolean zoneExportRemoveInitiators(
List<NetworkZoningParam> zoningParams,
String stepId) throws ControllerException {
boolean isRollback = WorkflowService.getInstance().isStepInRollbackState(stepId);
boolean isOperationSuccessful = false;
TaskCompleter taskCompleter = null;
if (zoningParams.isEmpty()) {
_log.info("zoningParams is empty, returning");
WorkflowStepCompleter.stepSucceded(stepId);
return true;
}
NetworkFCContext context = new NetworkFCContext();
boolean status = false;
URI exportGroupId = zoningParams.get(0).getExportGroupId();
URI virtualArray = zoningParams.get(0).getVirtualArray();
_log.info(String.format("Entering zoneExportRemoveInitiators for ExportGroup: %s",
zoningParams.get(0).getExportGroupDisplay()));
try {
if (!checkZoningRequired(stepId, virtualArray)) {
return true;
}
context.setAddingZones(false);
// Get the zoning targets to be removed.
List<NetworkFCZoneInfo> zoneInfos = _networkScheduler.getZoningRemoveTargets(zoningParams, null);
context.getZoneInfos().addAll(zoneInfos);
logZones(zoneInfos);
// If there are no zones to do, we were successful.
if (context.getZoneInfos().isEmpty()) {
isOperationSuccessful = true;
WorkflowStepCompleter.stepSucceded(stepId);
return true;
}
// Create a local completer to handle DB cleanup in the case of failure.
taskCompleter = new ZoneReferencesRemoveCompleter(NetworkUtil.getFCZoneReferences(context.getZoneInfos()), true, stepId);
InvokeTestFailure.internalOnlyInvokeTestFailure(InvokeTestFailure.ARTIFICIAL_FAILURE_024);
// Now call removeZones to remove all the required zones.
BiosCommandResult result = addRemoveZones(exportGroupId, context.getZoneInfos(), true);
status = result.isCommandSuccess();
InvokeTestFailure.internalOnlyInvokeTestFailure(InvokeTestFailure.ARTIFICIAL_FAILURE_025);
// Update the workflow state.
completeWorkflowState(taskCompleter, stepId, "zoneExportRemoveInitiators", result, null);
if (result.isCommandSuccess()) {
isOperationSuccessful = true;
}
return status;
} catch (Exception ex) {
_log.error("Exception zoning remove initiators", ex);
ServiceError svcError = NetworkDeviceControllerException.errors.zoneExportRemoveInitiatorsFailed(
ex.getMessage(), ex);
taskCompleter.error(_dbClient, svcError);
WorkflowStepCompleter.stepFailed(stepId, svcError);
return status;
} finally {
// clean up the zoning map too if the result is success or a rollback
if (isOperationSuccessful || isRollback) {
removeInitiatorsFromZoningMap(zoningParams);
}
}
}
/**
* Returns the Workflow.Method for a zoneRollback operation.
*
* @param exportGroupURI - ExportGroup URI
* @param contextKey -- The context key which indicates what zones were configured on the device.
* This is the Step id of the zoning step.
* @return Workflow.Method
*/
public Workflow.Method zoneRollbackMethod(URI exportGroupURI, String contextKey) {
return new Workflow.Method("zoneRollback", exportGroupURI, contextKey);
}
/**
* Rollback any of the zoning operations.
*
* @param exportGroupURI
* -- The ExportGroup URI
* @param contextKey
* -- The context which indicates what zones were configured on the device.
* @param taskId
* -- String task identifier for WorkflowTaskCompleter.
* @return
* @throws DeviceControllerException
*/
public boolean zoneRollback(URI exportGroupURI, String contextKey, String taskId) throws DeviceControllerException {
TaskCompleter taskCompleter = null;
try {
NetworkFCContext context = (NetworkFCContext) WorkflowService.getInstance()
.loadStepData(contextKey);
if (context == null) {
_log.warn("No zone rollback information for Step: " + contextKey +
" , Export Group: " + exportGroupURI.toString() + ", and Task: " +
taskId + ". The zoning step either did not complete or encountered an error.");
WorkflowStepCompleter.stepSucceded(taskId);
return true;
}
logZones(context.getZoneInfos());
WorkflowStepCompleter.stepExecuting(taskId);
_log.info("Beginning zone rollback");
_log.info("context.isAddingZones -{}", context.isAddingZones());
// Determine what needs to be rolled back.
List<NetworkFCZoneInfo> lastReferenceZoneInfo = new ArrayList<NetworkFCZoneInfo>();
List<NetworkFCZoneInfo> rollbackList = new ArrayList<NetworkFCZoneInfo>();
for (NetworkFCZoneInfo info : context.getZoneInfos()) {
if (info.canBeRolledBack()) {
// If we were adding zones, we set lastReference so it will be deleted.
if (context.isAddingZones()) {
info.setLastReference(true);
lastReferenceZoneInfo.add(info);
}
rollbackList.add(info);
}
}
taskCompleter = new ZoneReferencesRemoveCompleter(NetworkUtil.getFCZoneReferences(rollbackList), context.isAddingZones(), taskId);
InvokeTestFailure.internalOnlyInvokeTestFailure(InvokeTestFailure.ARTIFICIAL_FAILURE_020);
BiosCommandResult result = addRemoveZones(exportGroupURI, rollbackList,
context.isAddingZones());
InvokeTestFailure.internalOnlyInvokeTestFailure(InvokeTestFailure.ARTIFICIAL_FAILURE_021);
if (result.isCommandSuccess() && !lastReferenceZoneInfo.isEmpty()) {
_log.info("There seems to be last reference zones that were removed, clean those zones from the zoning map.");
updateZoningMap(lastReferenceZoneInfo, exportGroupURI, null);
}
completeWorkflowState(taskCompleter, taskId, "ZoneRollback", result, null);
return result.isCommandSuccess();
} catch (Exception ex) {
_log.error("Exception occurred while doing zone rollback", ex);
ServiceError svcError = NetworkDeviceControllerException.errors.zoneRollbackFailedExc(
exportGroupURI.toString(), ex);
taskCompleter.error(_dbClient, svcError);
WorkflowStepCompleter.stepFailed(taskId, svcError);
return false;
}
}
/**
* Returns a Workflow.Method for zoneNullRollback in NetworkDeviceController
* @return Workflow.Method
*/
public Workflow.Method zoneNullRollbackMethod() {
return new Workflow.Method("zoneNullRollback");
}
/**
* Performs a null rollback if desired.
* @param stepId
*/
public void zoneNullRollback(String stepId) {
WorkflowStepCompleter.stepSucceded(stepId);
}
// ===========================================================================================================
// end of External Interfaces to BlockDeviceController
// ===========================================================================================================
/**
* Create a nice event based on the Zone
*
* @param ref FCZoneReference for which the event is about
* @param type Type of event such as modified, created, removed
* @param description Description for the event if needed
*/
private void recordZoneEvent(FCZoneReference ref, String type, String description) {
if (ref == null) {
_log.error("Invalid Zone event");
return;
}
// TODO fix the bogus user ID once we have AuthZ working
RecordableBourneEvent event = ControllerUtils.convertToRecordableBourneEvent(ref, type, description,
null, _dbClient, EVENT_SERVICE_TYPE, RecordType.Event.name(), EVENT_SERVICE_SOURCE);
try {
_eventManager.recordEvents(event);
} catch (Exception ex) {
_log.error("Failed to record event. Event description: {}. Error:", description, ex);
}
}
/**
* Add a zone reference for an ExportGroup-Zone combination.
* This method is careful not to duplicate existing FCZoneReferences matching the same
* ExportGroup and Volume. Whether a new reference is persisted or not,
* it returns the reference.
*
* @param exportGroupURI -- the URI of the export group
* @param zoneInfo -- the zoneInfo for which the FCZoneReference is being created
* @param newOrExisting - OUT param in String[0] puts "New" or "Existing" indicating
* whether a New FCZoneReference was persisted.
* @return an FCZoneReference for the zoneInfo-exportGroup combination
*/
private FCZoneReference addZoneReference(URI exportGroupURI, NetworkFCZoneInfo zoneInfo, String[] newOrExisting) {
String refKey = zoneInfo.makeEndpointsKey();
URI egURI = exportGroupURI;
// If ExportGroup specified in zone info, use it instead of the default of the order
if (zoneInfo.getExportGroup() != null) {
egURI = zoneInfo.getExportGroup();
}
FCZoneReference ref = addZoneReference(egURI, zoneInfo.getVolumeId(), refKey, zoneInfo.getFabricId(),
zoneInfo.getNetworkDeviceId(), zoneInfo.getZoneName(), zoneInfo.isExistingZone(), newOrExisting);
return ref;
}
/**
* A base function for creating an FCZoneReference from the parameters. This function
* ensures that duplicate FCZoneReference for the same refKey, volume and export group
* is not created.
*
* @param exportGroupURI -- the export group URI
* @param volumeURI -- the volume URI
* @param refKey -- the FCZoneReference key which is the concatenation of the initiator
* and storage port WWNs. Note that this key is formed by sorting the WWNs
* @param fabricId -- the name of the fabric or the is of the vsan
* @param NetworkSystemURI -- the network system used to add the zone
* @param zoneName -- the zone name
* @param existingZone -- an flag that indicates if the zone is created by the aplication
* or by the user, true means it was created by the user.
* @param newOrExisting - OUT param in String[0] puts "New" or "Existing" indicating
* whether a New FCZoneReference was persisted.
* @return The zone reference instance
*/
private FCZoneReference addZoneReference(URI exportGroupURI, URI volumeURI,
String refKey, String fabricId, URI NetworkSystemURI, String zoneName,
boolean existingZone, String[] newOrExisting) {
// Check to see that we don't add multiple references for same Volume/Export Group combination
FCZoneReference ref = findFCZoneReferenceForVolGroupKey(exportGroupURI, volumeURI, refKey, newOrExisting);
if (ref == null) {
ref = new FCZoneReference();
ref.setPwwnKey(refKey);
ref.setFabricId(fabricId);
ref.setNetworkSystemUri(NetworkSystemURI);
ref.setVolumeUri(volumeURI);
ref.setGroupUri(exportGroupURI);
ref.setZoneName(zoneName);
ref.setId(URIUtil.createId(FCZoneReference.class));
ref.setInactive(false);
ref.setLabel(FCZoneReference.makeLabel(ref.getPwwnKey(), volumeURI.toString()));
ref.setExistingZone(existingZone);
_dbClient.createObject(ref);
newOrExisting[0] = "New";
}
return ref;
}
/**
* Looks in the database for a zone for the same volume and export group and key
*
* @param exportGroupURI -- the export group URI
* @param volumeURI -- the volume URI
* @param refKey -- the FCZoneReference key which is the concatenation of the initiator
* and storage port WWNs. Note that this key is formed by sorting the WWNs
* @param newOrExisting - OUT param in String[0] puts "New" or "Existing" indicating
* whether a New FCZoneReference was persisted.
* @return The zone reference instance if found, null otherwise
*/
private FCZoneReference findFCZoneReferenceForVolGroupKey(URI exportGroupURI, URI volumeURI, String refKey, String[] newOrExisting) {
Map<String, FCZoneReference> volRefMap = _networkScheduler.makeExportToReferenceMap(refKey);
String volExportKey = make2UriKey(volumeURI, exportGroupURI);
if (volRefMap.containsKey(volExportKey)) {
FCZoneReference ref = volRefMap.get(volExportKey);
// If we have an active reference, don't make another
if (ref != null && ref.getInactive() == false) {
_log.info(String.format("Existing zone reference: vol %s group %s refkey %s",
volumeURI, exportGroupURI, refKey));
newOrExisting[0] = "Existing";
return ref;
}
}
return null;
}
private void logZones(List<NetworkFCZoneInfo> zones) {
if (CollectionUtils.isEmpty(zones)) {
_log.info("No zones to log in logZones");
return;
}
for (NetworkFCZoneInfo zone : zones) {
_log.info(String.format("zone %s endpoints %s vol %s last %s ref %s existing %s canBeRolledBack %s",
zone.getZoneName(), zone.getEndPoints(), zone.getVolumeId(),
zone.isLastReference(), zone.getFcZoneReferenceId(), zone.isExistingZone(), zone.canBeRolledBack()));
}
}
/**
* Removes duplicate URIs from a Collection.
*
* @param uris
* @return
*/
private Collection<URI> removeDuplicateURIs(Collection<URI> uris) {
HashSet<URI> set = new HashSet<URI>();
set.addAll(uris);
return set;
}
/**
* Processes the initiators that were removed and removes them from the
* ExportMask.zoningMap so the zoningMap will now be indicative of current state.
*
* @param exportMasksToInitiators
*/
private void removeInitiatorsFromZoningMap(List<NetworkZoningParam> zoningParams) {
for (NetworkZoningParam zoningParam : zoningParams) {
ExportMask mask = _dbClient.queryObject(ExportMask.class, zoningParam.getMaskId());
if (mask == null || mask.getInactive()) {
continue;
}
List<URI> initiators = StringSetUtil.stringSetToUriList(zoningParam.getZoningMap().keySet());
for (URI initiatorURI : initiators) {
if (mask.getZoningMap() != null) {
mask.removeZoningMapEntry(initiatorURI.toString());
}
}
_dbClient.persistObject(mask);
}
}
@Override
public List<ZoneWwnAlias> getAliases(URI uri, String fabricId, String fabricWwn) throws ControllerException {
NetworkSystem device = getDeviceObject(uri);
// Get the file device reference for the type of file device managed
// by the controller.
NetworkSystemDevice networkDevice = getDevice(device.getSystemType());
if (networkDevice == null) {
throw NetworkDeviceControllerException.exceptions.getAliasesFailedNull(device.getSystemType());
}
try {
List<ZoneWwnAlias> aliases = networkDevice.getAliases(device, fabricId, fabricWwn);
return aliases;
} catch (Exception ex) {
Date date = new Date();
throw NetworkDeviceControllerException.errors.getAliasesFailedExc(uri.toString(), date.toString(), ex);
}
}
@Override
public void addAliases(URI uri, String fabricId, String fabricWwn, List<ZoneWwnAlias> aliases, String taskId)
throws ControllerException {
NetworkSystem device = getDeviceObject(uri);
// Lock to prevent concurrent operations on the same VSAN / FABRIC.
InterProcessLock fabricLock = NetworkFabricLocker.lockFabric(fabricId, _coordinator);
try {
// Get the file device reference for the type of file device managed
// by the controller.
NetworkSystemDevice networkDevice = getDevice(device.getSystemType());
if (networkDevice == null) {
throw NetworkDeviceControllerException.exceptions.addAliasesFailedNull(device.getSystemType());
}
BiosCommandResult result = networkDevice.addAliases(device, aliases, fabricId, fabricWwn);
setStatus(NetworkSystem.class, device.getId(), taskId, result.isCommandSuccess(), result.getServiceCoded());
_auditMgr.recordAuditLog(null, null, EVENT_SERVICE_TYPE,
OperationTypeEnum.ADD_ALIAS, System.currentTimeMillis(),
AuditLogManager.AUDITLOG_SUCCESS, AuditLogManager.AUDITOP_END,
device.getId().toString(), device.getLabel(), device.getPortNumber(), device.getUsername(),
device.getSmisProviderIP(), device.getSmisPortNumber(), device.getSmisUserName(), device.getSmisUseSSL());
} catch (Exception ex) {
ServiceError serviceError = NetworkDeviceControllerException.errors.addAliasesFailedExc(
device.getSystemType(), ex);
_dbClient.error(NetworkSystem.class, device.getId(), taskId, serviceError);
} finally {
NetworkFabricLocker.unlockFabric(fabricId, fabricLock);
}
}
@Override
public void removeAliases(URI uri, String fabricId, String fabricWwn, List<ZoneWwnAlias> aliases, String taskId)
throws ControllerException {
NetworkSystem device = getDeviceObject(uri);
// Lock to prevent concurrent operations on the same VSAN / FABRIC.
InterProcessLock fabricLock = NetworkFabricLocker.lockFabric(fabricId, _coordinator);
try {
// Get the file device reference for the type of file device managed
// by the controller.
NetworkSystemDevice networkDevice = getDevice(device.getSystemType());
if (networkDevice == null) {
throw NetworkDeviceControllerException.exceptions.removeAliasesFailedNull(device.getSystemType());
}
BiosCommandResult result = networkDevice.removeAliases(device, aliases, fabricId, fabricWwn);
setStatus(NetworkSystem.class, device.getId(), taskId, result.isCommandSuccess(), result.getServiceCoded());
_auditMgr.recordAuditLog(null, null, EVENT_SERVICE_TYPE,
OperationTypeEnum.REMOVE_ALIAS, System.currentTimeMillis(),
AuditLogManager.AUDITLOG_SUCCESS, AuditLogManager.AUDITOP_END,
device.getId().toString(), device.getLabel(), device.getPortNumber(), device.getUsername(),
device.getSmisProviderIP(), device.getSmisPortNumber(), device.getSmisUserName(), device.getSmisUseSSL());
} catch (Exception ex) {
ServiceError serviceError = NetworkDeviceControllerException.errors.removeAliasesFailedExc(
device.getSystemType(), ex);
_dbClient.error(NetworkSystem.class, device.getId(), taskId, serviceError);
} finally {
NetworkFabricLocker.unlockFabric(fabricId, fabricLock);
}
}
@Override
public void updateAliases(URI uri, String fabricId, String fabricWwn, List<ZoneWwnAliasUpdate> updateAliases, String taskId)
throws ControllerException {
NetworkSystem device = getDeviceObject(uri);
// Lock to prevent concurrent operations on the same VSAN / FABRIC.
InterProcessLock fabricLock = NetworkFabricLocker.lockFabric(fabricId, _coordinator);
try {
// Get the file device reference for the type of file device managed
// by the controller.
NetworkSystemDevice networkDevice = getDevice(device.getSystemType());
if (networkDevice == null) {
throw NetworkDeviceControllerException.exceptions.updateAliasesFailedNull(device.getSystemType());
}
BiosCommandResult result = networkDevice.updateAliases(device, updateAliases, fabricId, fabricWwn);
setStatus(NetworkSystem.class, device.getId(), taskId, result.isCommandSuccess(), result.getServiceCoded());
_auditMgr.recordAuditLog(null, null, EVENT_SERVICE_TYPE,
OperationTypeEnum.UPDATE_ALIAS, System.currentTimeMillis(),
AuditLogManager.AUDITLOG_SUCCESS, AuditLogManager.AUDITOP_END,
device.getId().toString(), device.getLabel(), device.getPortNumber(), device.getUsername(),
device.getSmisProviderIP(), device.getSmisPortNumber(), device.getSmisUserName(), device.getSmisUseSSL());
} catch (Exception ex) {
ServiceError serviceError = NetworkDeviceControllerException.errors.updateAliasesFailedExc(
device.getSystemType(), ex);
_dbClient.error(NetworkSystem.class, device.getId(), taskId, serviceError);
} finally {
NetworkFabricLocker.unlockFabric(fabricId, fabricLock);
}
}
/**
* Given a list of initiators which are all in the network, for each
* initiator find all the zones on the network system where the initiator
* port WWN is a member. Returns the results as a map of zones grouped
* by the initiator port WWN.
*
* @param network the network of the initiators
* @param initiators the initiators
* @return a map of zones grouped by the initiator port WWN.
*/
private Map<String, List<Zone>> getInitiatorsInNetworkZones(
NetworkLite network, List<Initiator> initiators) {
Map<String, List<Zone>> wwnToZones = new HashMap<String, List<Zone>>();
fetchInitiatorsZones(network, initiators, wwnToZones);
return wwnToZones;
}
/**
* For the given network and initiators, which are in the network,
* use one of the network's network system to get the zones and populate
* wwnToZones map with the zones found. Return the network system
* used to get the zones.
* <p>
* This function is created because to create the ZoneInfoMap of {@link #getInitiatorsInNetworkZoneInfoMap(NetworkLite, List, Map)},
* both the network system used and the zones are needed, while for {@link #getInitiatorsInNetworkZones(NetworkLite, List)} only the
* zones are needed. This solution was to support both calling functions.
* <p>
* Note if a zone is found that has more than one of the initiator, the zone will be returned once for each initiator.
*
* @param network the network of the initiators
* @param initiators the initiators
* @param wwnToZones a IN/OUT parameters which is a map to be populated
* with the zone mappings found
* @return the network system used to get the zones.
*/
private NetworkSystem fetchInitiatorsZones(NetworkLite network,
List<Initiator> initiators,
Map<String, List<Zone>> wwnToZones) {
// Check some network systems are discovered.
if (!NetworkUtil.areNetworkSystemDiscovered(_dbClient)) {
return null;
}
if (!Transport.FC.toString().equals(network.getTransportType())) {
return null;
}
if (initiators == null || initiators.isEmpty()) {
return null;
}
// Select the network system to use
NetworkSystem networkSystem = null;
Map<String, Initiator> wwnToInitiatorMap = wwnToInitiatorMap(initiators);
List<NetworkSystem> zoningNetworkSystems = _networkScheduler.getZoningNetworkSystems(network, null);
Iterator<NetworkSystem> itr = zoningNetworkSystems.iterator();
while (itr.hasNext()) {
networkSystem = itr.next();
try {
if (networkSystem != null) {
_log.info("Trying network system {} for network {} to get initiator zones.",
networkSystem.getLabel(), network.getLabel());
wwnToZones.putAll(getDevice(networkSystem.getSystemType()).getEndpointsZones(networkSystem,
NetworkUtil.getNetworkWwn(network), network.getNativeId(), wwnToInitiatorMap.keySet()));
break; // if we get here, we were successful at getting the zones, do not try any more network systems
}
} catch (Exception ex) {
// if we hit and exception, log it and try the next network system;
wwnToZones.clear();
_log.error("Failed to get the zones for initiators {} in network {} " +
"using network system {}. Will try the other available network systems",
new Object[] { wwnToInitiatorMap.keySet(), network.getLabel(),
networkSystem == null ? "null" : networkSystem.getLabel() });
}
networkSystem = null;
}
if (networkSystem == null) {
_log.error("Failed to find a registered network system in good discovery status to discover the zones");
throw NetworkDeviceControllerException.exceptions
.failedToFindNetworkSystem(wwnToInitiatorMap.keySet(), network.getLabel());
}
return networkSystem;
}
/**
* For the given network and initiators, which are in the network,
* and a given list of storage ports, find all the zones on the network
* system for the initiators. Search the zones to find ones that have
* one or more of the ports and create the zoning map between the
* initiators and ports. Returns the results as {@link ZoneInfoMap} which is map
* of initiator port WWN and storage port WWN keyed by zone-key, where zone-key
* is the concatenation of the initiator port WWN and the storage port WWN.
* <p>
* Note that the map returned contains only the zones that were selected for use by ViPR. In the case of duplicate zones between an
* initiator-port pair, ViPR applies a selection criteria to choose one. See {@link #selectZonesForInitiatorsAndPorts}
* <p>
* Note that a zone in the network system can have more than one initiator and one storage port. For such zone, there can be multiple
* entries in the map, one for each initiator/port pairs.
* <p>
* If the initiator is not in a network or no zones could be found for the initiator, there will be no entries for this initiator in the
* map. An empty map will be returned if no zones could be found for any initiator.
*
* @param network the network of the initiators
* @param initiators the initiators for which the zones will be read
* @param initiatorPortsMap the storage ports of interest in the networks.
* @return a ZoneInfoMap a map of zones found that have at least one of initiators and one of the ports
*/
private ZoneInfoMap getInitiatorsInNetworkZoneInfoMap(NetworkLite network, List<Initiator> initiators,
Map<String, StoragePort> initiatorPortsMap) {
ZoneInfoMap map = new ZoneInfoMap();
fetchInitiatorsInNetworkZoneInfoMap(network, map, initiators, initiatorPortsMap);
return map;
}
/**
* For the given network and initiators, which are in the network,
* and a given list of storage ports, find all the zones on the network
* system for the initiators. Search the zones to find ones that have
* one or more of the ports and create the zoning map between the
* initiators and ports. Returns the results as {@link ZoneInfoMap} which is map
* of initiator port WWN and storage port WWN keyed by zone-key, where zone-key
* is the concatenation of the initiator port WWN and the storage port WWN.
* <p>
* Note that the map returned contains only the zones that were selected for use by ViPR. In the case of duplicate zones between an
* initiator-port pair, ViPR applies a selection criteria to choose one. See {@link #selectZonesForInitiatorsAndPorts}
* <p>
* Note that a zone in the network system can have more than one initiator and one storage port. For such zone, there can be multiple
* entries in the map, one for each initiator/port pairs.
* <p>
* If the initiator is not in a network or no zones could be found for the initiator, there will be no entries for this initiator in the
* map. An empty map will be returned if no zones could be found for any initiator.
*
* @param network the network of the initiators
* @param map an OUT parameter where ZoneInfoMap is stored
* @param initiators the initiators for which the zones will be read
* @param initiatorPortsMap the storage ports of interest in the networks.
* @return the network system used to read the zones
*/
private NetworkSystem fetchInitiatorsInNetworkZoneInfoMap(NetworkLite network, ZoneInfoMap map,
List<Initiator> initiators, Map<String, StoragePort> initiatorPortsMap) {
Map<String, Initiator> wwnToInitiatorMap = wwnToInitiatorMap(initiators);
// retrieve the zones
Map<String, List<Zone>> wwnToZones = new HashMap<String, List<Zone>>();
NetworkSystem networkSystem = fetchInitiatorsZones(network, initiators, wwnToZones);
wwnToZones = selectZonesForInitiatorsAndPorts(network, wwnToZones, initiatorPortsMap);
// if we successfully retrieved the zones
if (networkSystem != null && !wwnToZones.isEmpty()) {
ZoneInfo info = null;
Initiator initiator = null;
for (Map.Entry<String, List<Zone>> entry : wwnToZones.entrySet()) {
initiator = wwnToInitiatorMap.get(entry.getKey());
for (Zone zone : entry.getValue()) { // I need some logic here to make sure I select the best zone
for (ZoneMember member : zone.getMembers()) {
if (initiatorPortsMap.containsKey(member.getAddress())) { // double check WWN formatting
StoragePort port = initiatorPortsMap.get(member.getAddress());
info = new ZoneInfo();
info.setZoneName(zone.getName());
info.setInitiatorWwn(initiator.getInitiatorPort());
info.setInitiatorId(initiator.getId().toString());
info.setPortWwn(port.getPortNetworkId());
info.setPortId(port.getId().toString());
info.setNetworkId(network.getId().toString());
info.setNetworkWwn(NetworkUtil.getNetworkWwn(network));
info.setFabricId(network.getNativeId());
info.setNetworkSystemId(networkSystem.getId().toString());
info.setMemberCount(zone.getMembers().size());
map.put(info.getZoneReferenceKey(), info);
}
}
}
}
}
return networkSystem;
}
/**
* Given the map of all existing zones for a set on initiators and ports,
* this function selects the zones that should be used by ViPR.
*
* @see NetworkScheduler#selectExistingZoneForInitiatorPort(String, String, List)
*
* @param wwnToZones a map of existing zones
* @param initiatorPortsMap a map of port-wwn-to-storage-port
* @return a new map containing the selected zones.
*/
private Map<String, List<Zone>> selectZonesForInitiatorsAndPorts(NetworkLite network,
Map<String, List<Zone>> wwnToZones, Map<String, StoragePort> initiatorPortsMap) {
Map<String, List<Zone>> filteredMap = new HashMap<String, List<Zone>>();
Zone zone = null;
List<Zone> zones = null;
for (String initiatorWwn : wwnToZones.keySet()) {
for (String portWwn : initiatorPortsMap.keySet()) {
zone = _networkScheduler.selectExistingZoneForInitiatorPort(network, initiatorWwn, portWwn, wwnToZones.get(initiatorWwn));
if (zone != null) {
zones = filteredMap.get(initiatorWwn);
if (zones == null) {
zones = new ArrayList<>();
filteredMap.put(initiatorWwn, zones);
}
zones.add(zone);
}
}
}
return filteredMap;
}
/**
* Given a list of initiators, return a map of initiator-wwn-to-initiators
*
* @param initiators the list of initiators
* @return a map of initiator-wwn-to-initiators
*/
private Map<String, Initiator> wwnToInitiatorMap(List<Initiator> initiators) {
Map<String, Initiator> wwns = new HashMap<String, Initiator>();
for (Initiator initiator : initiators) {
if (HostInterface.Protocol.FC.toString().equals(initiator.getProtocol())) {
wwns.put(initiator.getInitiatorPort(), initiator);
}
}
return wwns;
}
/**
* Given a list of initiators, and a list of ports, for each initiator, find the
* zones on the network system where the initiator port WWN and the storage port
* WWN is a member. Returns the results as {@link ZoneInfoMap} which is map
* of initiator port WWN and storage port WWN keyed by zone-key, where zone-key
* is the concatenation of the initiator port WWN and the storage port WWN.
* <p>
* Note that a zone in the network system can have more than one initiator and one storage port. For such zone, there can be multiple
* entries in the map, one for each initiator/port pairs.
* <p>
* If the initiator is not in a network or no zones could be found for the initiator, there will be no entries for this initiator in the
* map. An empty map will be returned if no zones could be found for any initiator.
*
* @param initiators the list of initiators.
* @param storagePorts
* @return an instance of {@link ZoneInfoMap} which is which is map
* of initiator port WWN and storage port WWN keyed by zone-key, where zone-key
* is the concatenation of the initiator port WWN and the storage port WWN.
*/
public ZoneInfoMap getInitiatorsZoneInfoMap(List<Initiator> initiators, List<StoragePort> storagePorts) {
ZoneInfoMap zoningMap = new ZoneInfoMap();
Map<NetworkLite, List<Initiator>> initiatorsByNetworkMap = NetworkUtil.getInitiatorsByNetwork(initiators, _dbClient);
for (Map.Entry<NetworkLite, List<Initiator>> entry : initiatorsByNetworkMap.entrySet()) {
if (!Transport.FC.toString().equals(entry.getKey().getTransportType())) {
continue;
}
Map<String, StoragePort> initiatorPortsMap = NetworkUtil.getPortsInNetworkMap(entry.getKey(), storagePorts);
if (initiatorPortsMap.size() > 0) {
zoningMap.putAll(getInitiatorsInNetworkZoneInfoMap(entry.getKey(), entry.getValue(), initiatorPortsMap));
}
}
return zoningMap;
}
/**
* Given a list of initiators, for each initiator, find the zones on the network
* system where the initiator port WWN is a member. Returns the results as a map
* of zones grouped by the initiator port WWN.
*
* @param initiators the list of initiators.
* @return map of zones grouped the initiator port WWN
*/
public Map<String, List<Zone>> getInitiatorsZones(Collection<Initiator> initiators) {
Map<String, List<Zone>> zonesMap = new HashMap<String, List<Zone>>();
Map<NetworkLite, List<Initiator>> initiatorsByNetworkMap = NetworkUtil.getInitiatorsByNetwork(initiators, _dbClient);
for (Map.Entry<NetworkLite, List<Initiator>> entry : initiatorsByNetworkMap.entrySet()) {
if (!entry.getValue().isEmpty()) {
zonesMap.putAll(getInitiatorsInNetworkZones(entry.getKey(), entry.getValue()));
}
}
return zonesMap;
}
/**
* Finds all the zone paths that exists between a list of initiators and storage ports.
*
* @param initiators the list of initiators
* @param portsMap a map of storage port keyed by the port WWN
* @param initiatorWwnToZonesMap an OUT parameter used to store the zones retrieved mapped by initiator
* @param networkSystemURI - an OUT parameter indicating the NetworkSystem's URI that found the zones
*
* @return a zoning map of zones that exists on the network systems
*/
public StringSetMap getZoningMap(NetworkLite network, List<Initiator> initiators,
Map<String, StoragePort> portsMap, Map<String, List<Zone>> initiatorWwnToZonesMap,
URI[] networkSystemURI) {
StringSetMap map = new StringSetMap();
StringSet initiatorWwns = new StringSet();
for (Initiator initiator : initiators) {
initiatorWwns.add(initiator.getInitiatorPort());
}
_log.info(String.format("Looking for zones from iniiators %s to targets %s", initiatorWwns, portsMap.keySet()));
// find all the zones for the initiators as a map of initiator WWN to zones
if (initiatorWwnToZonesMap == null) {
initiatorWwnToZonesMap = new HashMap<String, List<Zone>>();
}
// of the zones retrieved from the network system, select the once
NetworkSystem networkSystem = fetchInitiatorsZones(network, initiators, initiatorWwnToZonesMap);
if (networkSystem != null && networkSystemURI != null) {
networkSystemURI[0] = networkSystem.getId();
}
initiatorWwnToZonesMap = selectZonesForInitiatorsAndPorts(network, initiatorWwnToZonesMap, portsMap);
// build the map object
for (Initiator initiator : initiators) {
StringSet set = new StringSet();
List<Zone> zones = initiatorWwnToZonesMap.get(initiator.getInitiatorPort());
if (zones != null) {
for (Zone zone : zones) {
_log.info(zone.getLogString());
for (ZoneMember member : zone.getMembers()) {
if (portsMap.containsKey(member.getAddress())) {
// There can be multiple zones with the same initiator and port
// for this function, we're just finding all the mappings
set.add(portsMap.get(member.getAddress()).getId().toString());
}
}
}
}
if (set != null && !set.isEmpty()) {
map.put(initiator.getId().toString(), set);
}
}
_log.info("Found the following zone mappings {} for initiators {} and ports {}",
new Object[] { map, initiatorWwnToZonesMap.keySet(), portsMap.keySet() });
return map;
}
/**
* Update the zoning map for a newly "accepted" export mask. This applies to
* brown field scenarios where a export mask was found on the storage array.
* This function finds the zones of the export mask initiators and
* existing ports and creates the zoning map between the two sets.
*
* @param exportGroup the masking view export group
* @param exportMask the export mask being updated.
* @param doPersist a boolean that indicates if the changes should be persisted in the db
*/
public void updateZoningMapForInitiators(ExportGroup exportGroup, ExportMask exportMask, boolean doPersist) {
if (exportMask.getZoningMap() == null || exportMask.getZoningMap().isEmpty()) {
Long start = System.currentTimeMillis();
// possibly the first time this export mask is processed, populate from existing zones
List<StoragePort> storagePorts = ExportUtils.getStoragePorts(exportMask, _dbClient);
Set<Initiator> initiators = ExportMaskUtils.getInitiatorsForExportMask(_dbClient, exportMask, Transport.FC);
Map<NetworkLite, List<Initiator>> initiatorsByNetworkMap = NetworkUtil.getInitiatorsByNetwork(initiators, _dbClient);
StringSetMap zoningMap = new StringSetMap();
for (NetworkLite network : initiatorsByNetworkMap.keySet()) {
if (!Transport.FC.toString().equals(network.getTransportType())) {
continue;
}
Map<String, StoragePort> initiatorPortsMap = NetworkUtil.getPortsInNetworkMap(network, storagePorts);
if (!initiatorPortsMap.isEmpty()) {
Map<String, List<Zone>> initiatorWwnToZonesMap = new HashMap<String, List<Zone>>();
URI[] networkSystemURIUsed = new URI[1];
zoningMap.putAll(
getZoningMap(network, initiatorsByNetworkMap.get(network), initiatorPortsMap,
initiatorWwnToZonesMap, networkSystemURIUsed));
createZoneReferences(network, networkSystemURIUsed[0], exportGroup, exportMask, initiatorWwnToZonesMap);
}
}
_log.info(String.format("Elapsed time to updateZoningMap %d seconds", ((System.currentTimeMillis()-start)/1000L)));
exportMask.setZoningMap(zoningMap);
if (doPersist) {
_dbClient.updateAndReindexObject(exportMask);
}
} else {
_log.info((String.format("Export mask %s (%s, args) already has a zoning map, not importing zones",
exportMask.getMaskName(), exportMask.getId())));
}
}
/**
* Create zone references when we discover zones off the switch.
* Normally this routine will not do anything, because the ExportMask is freshly created, and
* will therefore have no volumes in it.
* @param network -- Network Lite
* @param networkSystem -- URI of network system
* @param exportGroup -- ExportGroup
* @param exportMask -- ExportMask
* @param initiatorWwntoZonesMap -- Map of initiator WWN string to a list of corresponding Zones
*/
void createZoneReferences(NetworkLite network, URI networkSystem,
ExportGroup exportGroup, ExportMask exportMask, Map<String, List<Zone>> initiatorWwntoZonesMap) {
StringMap maskVolumes = exportMask.getVolumes();
if (maskVolumes == null || maskVolumes.isEmpty()) {
_log.info(String.format("No volumes in ExportMask %s %s so no zone references created",
exportMask.getMaskName(), exportMask.getId()));
return;
}
// Create zone reference for each volume and zone. In the case of zones with multiple targets,
// create a separate zone reference for each initiator - target pair.
for (String maskVolume : maskVolumes.keySet()) {
for (List<Zone> zones : initiatorWwntoZonesMap.values()) {
for (Zone zone : zones) {
// Find the initiatorWwn
String initiatorWwn = null;
for (ZoneMember member : zone.getMembers()) {
if (initiatorWwntoZonesMap.containsKey(member.getAddress())) {
initiatorWwn = member.getAddress();
break;
}
}
if (initiatorWwn == null) {
// Could not locate the initiator
_log.info("Could not locat the initiator: " + zone.getName());
continue;
}
for (ZoneMember member : zone.getMembers()) {
if (initiatorWwn.equals(member.getAddress())) {
continue;
}
String key = FCZoneReference.makeEndpointsKey(initiatorWwn, member.getAddress());
String[] newOrExisting = new String[1];
FCZoneReference ref = addZoneReference(exportGroup.getId(), URI.create(maskVolume), key, network.getNativeId(),
networkSystem, zone.getName(), true, newOrExisting);
_log.info(String.format("Created FCZoneReference for existing zone %s %s %s %s",
ref.getZoneName(), ref.getPwwnKey(), ref.getVolumeUri(), ref.getGroupUri()));
}
}
}
}
}
/**
* Update the zoning map for as export mask previously "accepted". This applies to
* brown field scenarios where a export mask was found on the storage array. For
* those export masks, changes outside of the application are expected and the
* application should get the latest state before making any changes. This
* function is called from ExportMaskOperations#refreshZoneMap after all
* updates to the initiators, ports and volumes were made into the export mask and
* the export group. The update steps are as follow:
* <ol>
* <li>Get the current zones for those initiators that were not added by ViPR and the storage ports that exist in the mask.</li>
* <li>Diff the current zones with those in the export mask and update the zoning map</li>
* <li>Update the FCZoneReferences to match the zone updates</li>
* </ol>
* Note that ViPR does not keep FCZoneReferences only for volumes created by ViPR. As those
* volumes are not updated by ExportMaskOperations#refreshZoneMap, no additional code
* is needed to remove FCZoneReferences for removed volumes.
*
* @param exportMask the export mask being updated.
* @param removedInitiators the list of initiators that were removed. This is needed because
* these were removed from the zoingMap by {@link ExportMask#removeInitiators(Collection)}
* @param removedPorts the set of storage ports that were removed
* @param maskUpdated a flag that indicates if an update was made to the mask that requires
* a zoning refresh
* @param persist a boolean that indicates if the changes should be persisted in the db
*/
public void refreshZoningMap(ExportMask exportMask, Collection<String> removedInitiators,
Collection<String> removedPorts, boolean maskUpdated, boolean persist) {
try {
// check if zoning is enabled for the mask
if (!zoningEnabled(exportMask)) {
_log.info("Zoning not enabled for export mask {}. Zoning refresh will not be done",
exportMask.getMaskName());
return;
}
if (!(maskUpdated || alwaysRefreshZone())) {
_log.info("The mask ports and initiators were not modified and alwaysRefreshZones is false" +
" Zoning refresh will not be done for mask {}",
exportMask.getMaskName());
return;
}
List<Initiator> initiators = ExportUtils.getExportMaskInitiators(exportMask, _dbClient);
_log.info("Refreshing zones for export mask {}. \n\tCurrent initiators " +
"in this mask are: {}. \n\tStorage ports in the mask are : {}. \n\tZoningMap is : {}. " +
"\n\tRemoved initiators: {}. \n\tRemoved ports: {}",
new Object[] { exportMask.getMaskName(), exportMask.getInitiators(),
exportMask.getStoragePorts(), exportMask.getZoningMap(), removedInitiators, removedPorts });
Long start = System.currentTimeMillis();
// get the current zones in the network system for initiators and ports
List<StoragePort> storagePorts = ExportUtils.getStoragePorts(exportMask, _dbClient);
ZoneInfoMap zoneInfoMap = getInitiatorsZoneInfoMap(initiators, storagePorts);
// Get the full sets of initiators and ports affected. They will be used to find the FCZoneReferences to refresh
// These sets include new initiators and ports, existing ones that did not change, as well as removed ones
List<StoragePort> allStoragePorts = DataObjectUtils.iteratorToList(_dbClient.queryIterativeObjects(StoragePort.class,
StringSetUtil.stringSetToUriList(removedPorts)));
allStoragePorts.addAll(storagePorts);
List<Initiator> allInitiators = DataObjectUtils.iteratorToList(_dbClient.queryIterativeObjects(Initiator.class,
StringSetUtil.stringSetToUriList(removedInitiators)));
allInitiators.addAll(initiators);
// Make a copy of the zoning mask - Zones have already been removed for removed initiators, put them back
// This zoning map will be used to do diff between old and new and to get zone references
StringSetMap allZonesMap = new StringSetMap();
StringSetMap tempMap = exportMask.getZoningMap() == null ? new StringSetMap() : exportMask.getZoningMap();
for (String key : tempMap.keySet()) {
// when the zoning map is removed prematurely, this ports set is empty but not null
if (removedInitiators.contains(key) && (tempMap.get(key) == null || tempMap.get(key).isEmpty())) {
// this was prematurely cleared, we will assume all ports
// were zoned to make sure we clean up all FCZoneReferences
allZonesMap.put(key, new StringSet(removedPorts));
if (exportMask.getStoragePorts() != null) {
allZonesMap.get(key).addAll(exportMask.getStoragePorts());
}
} else {
allZonesMap.put(key, new StringSet(tempMap.get(key)));
}
}
// get all the zone references that exist in the database for this export mask.
Map<String, List<FCZoneReference>> existingRefs = getZoneReferences(allZonesMap, allInitiators, allStoragePorts);
// initialize results collections
List<ZoneInfo> addedZoneInfos = new ArrayList<ZoneInfo>();
List<ZoneInfo> updatedZoneInfos = new ArrayList<ZoneInfo>();
List<String> removedZonesKeys = new ArrayList<String>();
// Compare old and new zones. Initialize some loop variables.
ZoneInfo zoneInfo = null;
String initId = null;
String portId = null;
if (exportMask.getZoningMap() == null) {
exportMask.setZoningMap(new StringSetMap());
}
for (Entry<String, ZoneInfo> entry : zoneInfoMap.entrySet()) {
zoneInfo = entry.getValue();
initId = zoneInfo.getInitiatorId();
portId = zoneInfo.getPortId();
if (exportMask.getZoningMap().containsKey(initId) &&
exportMask.getZoningMap().get(initId).contains(portId)) {
_log.debug("Zoning between initiator {} and port {} did not change",
zoneInfo.getInitiatorWwn(), zoneInfo.getPortWwn());
// This is accounted for, let's remove it from our diff map
allZonesMap.remove(initId, portId);
// add the zone info so that it can be updated for changes like zone name change
updatedZoneInfos.add(zoneInfo);
} else {
_log.info("New zone was found between initiator {} and port {} and will be added",
zoneInfo.getInitiatorWwn(), zoneInfo.getPortWwn());
// if this was ViPR allocation, do not add new zones
// sometimes zones have more than one initiator or port
if (exportMask.hasExistingInitiator(zoneInfo.getInitiatorWwn())) {
// This is a new entry, add it to the zoning map
exportMask.getZoningMap().put(initId, portId);
// add it to the results so that the appropriate FCZoneReferences are added
addedZoneInfos.add(zoneInfo);
}
// This zone is not expected to be in the diff map, but try anyway
allZonesMap.remove(initId, portId);
}
}
// If anything is remaining zones in the diff zoning map, these were removed in the network system
Initiator initiator = null;
StoragePort port = null;
for (String key : allZonesMap.keySet()) {
initiator = DataObjectUtils.findInCollection(allInitiators, key);
if (allZonesMap.get(key) != null && !allZonesMap.get(key).isEmpty()) {
for (String val : allZonesMap.get(key)) {
port = DataObjectUtils.findInCollection(allStoragePorts, val);
_log.info("Zone between initiator {} and port {} was removed from the network system" +
" or no longer belongs to this mask.", key, val);
if (port == null || initiator == null) {
// the port or initiator were removed at some point
exportMask.getZoningMap().remove(key, val);
_log.info("Removed zoningMap entry between initiator {} and port {} because " +
"the port and/or the initiator were removed from the mask", key, val);
} else if (removedInitiators.contains(key) || removedPorts.contains(val)) {
// the port or initiator were removed, remove the zone map entry
exportMask.getZoningMap().remove(key, val);
_log.info("Removed zoningMap entry between initiator {} and port {} because " +
"the port and/or the initiator were removed from the mask",
initiator.getInitiatorPort(), port.getPortNetworkId());
} else if (exportMask.hasExistingInitiator(
WWNUtility.getUpperWWNWithNoColons(initiator.getInitiatorPort()))) {
exportMask.getZoningMap().remove(key, val);
_log.info("Removed zoningMap entry between initiator {} and port {} because " +
"this was a brownfield zone for a brownfield initiator",
initiator.getInitiatorPort(), port.getPortNetworkId());
} else {
_log.info("The zone between initiator {} and port {} was removed from " +
" the network system but the zoningMap entry will be kept because it was" +
" a ViPR initiator-port assignment", initiator.getInitiatorPort(), port.getPortNetworkId());
}
if (port != null && initiator != null) {
removedZonesKeys.add(FCZoneReference.makeEndpointsKey(initiator.getInitiatorPort(), port.getPortNetworkId()));
}
}
}
}
// get all the existing zone references from the database, these are
refreshFCZoneReferences(exportMask,
existingRefs, addedZoneInfos, updatedZoneInfos, removedZonesKeys);
if (persist) {
_dbClient.updateAndReindexObject(exportMask);
}
_log.info("Changed zones for export mask {} to {}. \nRefreshing zones took {} ms",
new Object[] { exportMask.getMaskName(), exportMask.getZoningMap(),
(System.currentTimeMillis() - start) });
} catch (Exception ex) {
_log.error("An exception occurred while updating zoning map for export mask {} with message {}",
new Object[] { exportMask.getMaskName(), ex.getMessage() }, ex);
}
}
/**
* Checks if zoning is enabled for any of the mask export groups.
*
* @param exportMask the export mask
* @return true if zoning is enabled for any of the mask export groups.
*/
private boolean zoningEnabled(ExportMask exportMask) {
if (NetworkUtil.areNetworkSystemDiscovered(_dbClient)) {
List<ExportGroup> exportGroups = ExportUtils.getExportGroupsForMask(exportMask.getId(), _dbClient);
for (ExportGroup exportGroup : exportGroups) {
if (NetworkScheduler.isZoningRequired(_dbClient, exportGroup.getVirtualArray())) {
return true;
}
}
}
return false;
}
/**
* Given the list of zone changes, the list of existing zone references for the initiators and ports,
* update the FCZoneReference instances in the database. This function ensures that the zone
* references updated are those of the export mask volumes.
*
* @param exportMask the export mask being refreshed
* @param existingRefs a map of zone-reference-key-to-zone-references for each initiator-port pair
* of the mask, including those that were removed. This list may contain zone references for
* volumes not in the export mask.
* @param addedZoneInfos the ZoneInfo instances of zones that were added in this refresh operation
* @param updatedZoneInfos the ZoneInfo instances of zones that were updated in this refresh operation
* @param removedZonesKeys the keys of the zones that were removed.
*/
private void refreshFCZoneReferences(ExportMask exportMask, Map<String, List<FCZoneReference>> existingRefs,
List<ZoneInfo> addedZoneInfos, List<ZoneInfo> updatedZoneInfos, List<String> removedZonesKeys) {
// get the export mask volumes and export groups because FCZoneReference are kept for each
Map<URI, Integer> exportMaskVolumes = StringMapUtil.stringMapToVolumeMap(exportMask.getVolumes());
List<ExportGroup> exportGroups = ExportUtils.getExportGroupsForMask(exportMask.getId(), _dbClient);
// start with removing references
List<FCZoneReference> temp = null;
List<FCZoneReference> refs = new ArrayList<FCZoneReference>();
for (String refKey : removedZonesKeys) {
temp = existingRefs.get(refKey);
if (temp == null) {
continue;
}
for (FCZoneReference ref : temp) {
for (ExportGroup exportGroup : exportGroups) {
if (exportGroup.getId().equals(ref.getGroupUri()) &&
exportGroup.hasBlockObject(ref.getVolumeUri()) &&
exportMaskVolumes.containsKey(ref.getVolumeUri()) /*
* &&
* ref.getExistingZone()
*/) {
_log.info("FCZoneReference {} for volume {} and exportGroup {} will be deleted",
new Object[] { ref.getPwwnKey(), ref.getVolumeUri(), ref.getGroupUri() });
refs.add(ref);
}
}
}
}
_dbClient.markForDeletion(refs);
refs.clear();
// update zone references with new zone info in case the zone was renamed
for (ZoneInfo zoneInfo : updatedZoneInfos) {
String refKey = zoneInfo.getZoneReferenceKey();
temp = existingRefs.get(refKey);
if (temp == null) {
continue;
}
for (FCZoneReference ref : temp) {
for (ExportGroup exportGroup : exportGroups) {
if (exportGroup.getId().equals(ref.getGroupUri()) &&
exportGroup.hasBlockObject(ref.getVolumeUri()) &&
exportMaskVolumes.containsKey(ref.getVolumeUri())) {
// only update when there are changes to avoid unnecessary create/delete of indexes
if (zoneInfo.getZoneName() != null && !zoneInfo.getZoneName().equals(ref.getZoneName())) {
ref.setZoneName(zoneInfo.getZoneName());
ref.setExistingZone(true);
}
if (zoneInfo.getNetworkSystemId() != null &&
(ref.getNetworkSystemUri() == null ||
!zoneInfo.getNetworkSystemId().equals(ref.getNetworkSystemUri().toString()))) {
ref.setNetworkSystemUri(URI.create(zoneInfo.getNetworkSystemId()));
}
if (zoneInfo.getFabricId() != null && !zoneInfo.getFabricId().equals(ref.getFabricId())) {
ref.setFabricId(zoneInfo.getFabricId());
}
refs.add(ref);
}
}
}
}
_dbClient.updateAndReindexObject(refs);
refs.clear();
// Create zone references as needed, one per volume and export group
for (ZoneInfo zoneInfo : addedZoneInfos) {
for (URI volUri : exportMaskVolumes.keySet()) {
for (ExportGroup exportGroup : exportGroups) {
if (exportGroup.hasBlockObject(volUri)) {
refs.add(createFCZoneReference(zoneInfo, volUri, exportGroup)); // do I need to check duplicates?
_log.info("FCZoneReference {} for volume {} and exportGroup {} will be added",
new Object[] { zoneInfo.getZoneReferenceKey(), volUri, exportGroup.getId() });
}
}
}
}
_dbClient.createObject(refs);
}
/**
* Given the zoning map, find all the instances of FCZoneReference for each initiator-port pair.
*
* @param refreshMap the zoning map
* @param initiators the initiators
* @param ports the storage ports
* @return a map of zone key to a list of zone reference objects for the key.
*/
private Map<String, List<FCZoneReference>> getZoneReferences(StringSetMap refreshMap,
Collection<Initiator> initiators, Collection<StoragePort> ports) {
Map<String, List<FCZoneReference>> map = new HashMap<String, List<FCZoneReference>>();
Initiator initiator = null;
StoragePort port = null;
Set<String> portsKey = null;
for (String initKey : refreshMap.keySet()) {
portsKey = refreshMap.get(initKey);
initiator = DataObjectUtils.findInCollection(initiators, initKey);
if (initiator == null) {
continue;
}
if (portsKey == null || portsKey.isEmpty()) {
continue;
}
for (String portkey : portsKey) {
port = DataObjectUtils.findInCollection(ports, portkey);
if (port == null) {
continue;
}
String key = FCZoneReference.makeEndpointsKey(initiator.getInitiatorPort(), port.getPortNetworkId());
Joiner joiner = dbModelClient.join(FCZoneReference.class, "refs", "pwwnKey", key).go();
List<FCZoneReference> list = joiner.list("refs");
if (list != null && !list.isEmpty()) {
map.put(key, list);
}
}
}
return map;
}
/**
* Creates an instance of FCZoneReference
*
* @param info the zone info containing the zone, its network,
* its network system, ...
* @param initiator the zone initiator
* @param volume volume the FCZoneReference volume
* @param exportGroup the FCZoneReference export group
* @return an instance of FCZoneReference
*/
private static FCZoneReference createFCZoneReference(ZoneInfo info,
URI volumeURI, ExportGroup exportGroup) {
FCZoneReference ref = new FCZoneReference();
ref.setPwwnKey(info.getZoneReferenceKey());
ref.setFabricId(info.getFabricId());
ref.setNetworkSystemUri(URI.create(info.getNetworkSystemId()));
ref.setVolumeUri(volumeURI);
ref.setGroupUri(exportGroup.getId());
ref.setZoneName(info.getZoneName());
ref.setId(URIUtil.createId(FCZoneReference.class));
ref.setLabel(FCZoneReference.makeLabel(ref.getPwwnKey(), volumeURI.toString()));
ref.setExistingZone(true);
return ref;
}
/**
* Returns the flag settable by the user in the system config that indicates if zoned should
* be refreshed each time a mask is changed in viPR.
*
* @return true/false
*/
private boolean alwaysRefreshZone() {
boolean alwaysRefresh = false; // default to true
try {
alwaysRefresh = Boolean.valueOf(ControllerUtils.getPropertyValueFromCoordinator(
_coordinator, "controller_ns_zone_refresh_always"));
} catch (Exception ex) {
_log.warn("Failed to get the values for controller_ns_zone_refresh_always from resource bundle "
+ ex.getMessage());
}
return alwaysRefresh;
}
/**
* Method for the zoning for adding paths to mask.
* @param systemURI -- Storage system URI
* @param exportGroupURI -- Export Group URI
* @param exportMaskURIs -- List of ExportMask URIs
* @param newPaths -- Zoning map of the new paths to be added
* @param taskCompleter -- Task completer to be called
* @return true if successful
* @throws ControllerException
*/
public Workflow.Method zoneExportAddPathsMethod(URI systemURI, URI exportGroupURI, Collection<URI> exportMasks,
Map<URI, List<URI>> newPaths, TaskCompleter taskCompleter) {
return new Workflow.Method("zoneExportAddPaths", systemURI, exportGroupURI, exportMasks, newPaths, taskCompleter);
}
/**
* Handle zoning when adding paths to ExportMasks
* @param systemURI -- Storage system URI
* @param exportGroupURI -- Export Group URI
* @param exportMaskURIs -- List of ExportMask URIs
* @param newPaths -- Zoning map of the new paths to be added
* @param taskCompleter -- Task completer to be called
* @param token -- Step id
* @return true if successful
* @throws ControllerException
*/
public boolean zoneExportAddPaths(URI systemURI, URI exportGroupURI,
Collection<URI> exportMaskURIs,
Map<URI, List<URI>> newPaths,
TaskCompleter taskCompleter,
String token) throws ControllerException {
NetworkFCContext context = new NetworkFCContext();
boolean completedSucessfully = false;
ExportGroup exportGroup = _dbClient
.queryObject(ExportGroup.class, exportGroupURI);
_log.info(String.format("Entering zoneExportAddPaths for ExportGroup: %s (%s)",
exportGroup.getLabel(), exportGroup.getId()));
try {
if (!isZoningRequired(exportGroup.getVirtualArray())) {
_log.info("The zoning is not required.");
taskCompleter.ready(_dbClient);
return true;
}
// get existing zones on the switch
Map<String, List<Zone>> zonesMap = getExistingZonesMap(exportMaskURIs, token);
// Compute zones that are required.
List<NetworkFCZoneInfo> zoneInfos =
_networkScheduler.getZoningTargetsForPaths(systemURI, exportGroup, exportMaskURIs, newPaths, zonesMap, _dbClient);
context.getZoneInfos().addAll(zoneInfos);
logZones(zoneInfos);
// If there are no zones to do, we were successful.
if (context.getZoneInfos().isEmpty()) {
taskCompleter.ready(_dbClient);;
return true;
}
// Now call addRemoveZones to add all the required zones.
BiosCommandResult result = addRemoveZones(exportGroup.getId(),
context.getZoneInfos(), false);
completedSucessfully = result.isCommandSuccess();
if (completedSucessfully) {
taskCompleter.ready(_dbClient);
} else {
ServiceError svcError = NetworkDeviceControllerException.errors.zoneExportAddPathsError(
result.getMessage());
taskCompleter.error(_dbClient, svcError);
}
} catch (Exception ex) {
_log.error("Exception zoning add paths", ex);
ServiceError svcError = NetworkDeviceControllerException.errors.zoneExportAddPathsFailed(
ex.getMessage(), ex);
taskCompleter.error(_dbClient, svcError);
}
return completedSucessfully;
}
/**
* Method for handling zoning when removing paths
* @param zoningParam -- List of NetworkZoningParam
* @param taskCompleter -- TaskCompleter to be called
* @return true if successful
*/
public Workflow.Method zoneExportRemovePathsMethod(List<NetworkZoningParam> zoningParam, TaskCompleter taskCompleter) {
return new Workflow.Method("zoneExportRemovePaths", zoningParam, taskCompleter);
}
/**
* Handle zoning when removing paths from export masks
* @param zoningParams NetworkZoningParam list
* @param taskCompleter -- Task completer to be called
* @param token -- Step id
* @return true if successful
*/
public boolean zoneExportRemovePaths(List<NetworkZoningParam> zoningParams, TaskCompleter taskCompleter, String token) {
if (zoningParams.isEmpty()) {
_log.info("zoningParams is empty, returning");
taskCompleter.ready(_dbClient);
return true;
}
NetworkFCContext context = new NetworkFCContext();
boolean completedSuccessfully = false;
URI exportGroupId = zoningParams.get(0).getExportGroupId();
URI virtualArray = zoningParams.get(0).getVirtualArray();
_log.info(String.format("Entering zoneExportRemovePaths for ExportGroup: %s",
zoningParams.get(0).getExportGroupDisplay()));
try {
if(!isZoningRequired(virtualArray)) {
taskCompleter.ready(_dbClient);
return true;
}
context.setAddingZones(false);
// Get the zoning targets to be removed.
List<NetworkFCZoneInfo> zoneInfos = _networkScheduler.getZoningRemoveTargets(zoningParams, null);
context.getZoneInfos().addAll(zoneInfos);
logZones(zoneInfos);
// If there are no zones to do, we were successful.
if (context.getZoneInfos().isEmpty()) {
taskCompleter.ready(_dbClient);
return true;
}
// Now call addRemoveZones to remove all the required zones.
BiosCommandResult result = addRemoveZones(exportGroupId, context.getZoneInfos(), true);
completedSuccessfully = result.isCommandSuccess();
if (completedSuccessfully) {
taskCompleter.ready(_dbClient);
} else {
ServiceError svcError = NetworkDeviceControllerException.errors.zoneExportRemovePathsError(
result.getMessage());
taskCompleter.error(_dbClient, svcError);
}
} catch (Exception ex) {
_log.error("Exception zoning remove initiators", ex);
ServiceError svcError = NetworkDeviceControllerException.errors.zoneExportRemovePathsFailed(
ex.getMessage(), ex);
taskCompleter.error(_dbClient, svcError);
}
return completedSuccessfully;
}
/**
* Generating warning messages if there are any zoneInfos that won't be deleted because
* their existingZone value is true (and we are removing the last reference.)
* @param zoneInfos -- list of NetworkFCZoneInfo representingzones to be delete.
* @return -- Warning message text (may include information about multiple zones)
*/
private String generateExistingZoneWarningMessages(List<NetworkFCZoneInfo> zoneInfos) {
StringBuilder buf = new StringBuilder();
StringSet zoneNames = new StringSet();;
// Issue warning for zones that are lastRef but existing, as they won't be removed.
for (NetworkFCZoneInfo zoneInfo : zoneInfos) {
if (zoneInfo.isLastReference() && zoneInfo.isExistingZone()) {
zoneNames.add(zoneInfo.getZoneName());
}
}
if (!zoneNames.isEmpty()) {
buf.append("Zones which will not be deleted because they may be used externally: ");
buf.append(com.google.common.base.Joiner.on(' ').join(zoneNames));
}
return buf.toString();
}
}