/*
* Copyright (c) 2015 EMC Corporation
* All Rights Reserved
*/
package com.emc.storageos.networkcontroller.impl.mds;
import java.net.URI;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Collection;
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 org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.emc.storageos.coordinator.client.service.CoordinatorClient;
import com.emc.storageos.db.client.DbClient;
import com.emc.storageos.db.client.model.FCEndpoint;
import com.emc.storageos.db.client.model.NetworkSystem;
import com.emc.storageos.networkcontroller.SSHSession;
import com.emc.storageos.networkcontroller.exceptions.NetworkDeviceControllerException;
import com.emc.storageos.networkcontroller.impl.NetworkSystemDevice;
import com.emc.storageos.networkcontroller.impl.NetworkSystemDeviceImpl;
import com.emc.storageos.svcs.errorhandling.model.ServiceError;
import com.emc.storageos.util.InvokeTestFailure;
import com.emc.storageos.util.NetworkLite;
import com.emc.storageos.util.NetworkUtil;
import com.emc.storageos.volumecontroller.ControllerException;
import com.emc.storageos.volumecontroller.impl.BiosCommandResult;
import com.emc.storageos.volumecontroller.impl.ControllerUtils;
import com.google.common.collect.Sets;
public class MdsNetworkSystemDevice extends NetworkSystemDeviceImpl implements NetworkSystemDevice {
private static final Logger _log = LoggerFactory.getLogger(MdsNetworkSystemDevice.class);
private static final String MDS_ROUTED_INDICATOR = "Virtual Device";
private final String wwnRegex = "([0-9A-Fa-f][0-9A-Fa-f]:){7}[0-9A-Fa-f][0-9A-Fa-f]";
private static volatile CoordinatorClient _coordinator;
private static volatile DbClient _dbClient;
/**
* Sets up a session. Gets session parameters from the NetworkSystem.
*
* @param network NetworkSystem
* @return MDSDialog representing the session
* @throws NetworkDeviceControllerException
*/
private MDSDialog setUpDialog(NetworkSystem network) throws NetworkDeviceControllerException {
try {
SSHSession session = new SSHSession();
session.connect(network.getIpAddress(), network.getPortNumber(), network.getUsername(), network.getPassword());
MDSDialog dialog = new MDSDialog(session, getDefaultTimeout());
dialog.initialize();
return dialog;
} catch (Exception ex) {
String exMsg = ex.getLocalizedMessage();
if (exMsg.equals("Auth fail")) {
exMsg = "Authorization Failed";
}
if (exMsg.equals("timeout: socket is not established")) {
exMsg = "Connection Failed";
}
String msg = MessageFormat.format("Could not connect to device {0}: {1}", network.getLabel(), exMsg);
_log.error(msg);
throw NetworkDeviceControllerException.exceptions.setUpDialogFailed(network.getLabel(), exMsg, ex);
}
}
/**
* Disconnect a session. Sends an "exit" command to log out.
*
* @param dialog
*/
private void disconnect(MDSDialog dialog) {
if (dialog != null) {
dialog.send("exit\n");
dialog.getSession().disconnect();
}
}
@Override
public BiosCommandResult doConnect(NetworkSystem network) {
BiosCommandResult result = null;
MDSDialog dialog = null;
try {
dialog = setUpDialog(network);
String[] versInfo = dialog.showVersion();
result = BiosCommandResult.createSuccessfulResult();
if (versInfo[0].startsWith("MDS") == false && versInfo[0].startsWith("Nexus") == false) {
ServiceError svcError = NetworkDeviceControllerException.errors.doConnectFailedNotMds(
network.getLabel());
result = BiosCommandResult.createErrorResult(svcError);
}
} catch (Exception ex) {
ServiceError svcError = NetworkDeviceControllerException.errors.doConnectFailed(
network.getLabel());
result = BiosCommandResult.createErrorResult(svcError);
} finally {
disconnect(dialog);
}
_log.info("Connetwork to NetworkSystem " + (result.isCommandSuccess() ? "successful " : "failed ") + result.getMessage());
return result;
}
@Override
public BiosCommandResult doDisconnect(NetworkSystem network) {
// TODO Auto-generated method stub
return null;
}
@Override
public List<FCEndpoint> getPortConnections(NetworkSystem network, Map<String, Set<String>> routedEndpoints) throws Exception {
MDSDialog dialog = null;
try {
dialog = setUpDialog(network);
List<FCEndpoint> connections = dialog.showFcnsDatabase(null);
// removing all the endpoints that are associated by routing
Iterator<FCEndpoint> itr = connections.iterator();
FCEndpoint ep = null;
while (itr.hasNext()) {
ep = itr.next();
if (MDS_ROUTED_INDICATOR.equalsIgnoreCase(ep.getSwitchInterface()) && ep.getFabricWwn() != null) {
Set<String> netRoutedEndpoints = routedEndpoints.get(ep.getFabricWwn());
if (netRoutedEndpoints == null) {
netRoutedEndpoints = new HashSet<String>();
routedEndpoints.put(ep.getFabricWwn(), netRoutedEndpoints);
}
netRoutedEndpoints.add(ep.getRemotePortName());
itr.remove();
}
}
dialog.populateConnectionByIvrZone(routedEndpoints);
return connections;
} catch (Exception ex) {
_log.error("Cannot read FCNS database from device: " + network.getLabel() + ": " + ex.getLocalizedMessage());
throw ex;
} finally {
disconnect(dialog);
}
}
@Override
public List<String> getFabricIds(NetworkSystem network) throws Exception {
MDSDialog dialog = null;
try {
dialog = setUpDialog(network);
Map<Integer, Vsan> vsanMap = dialog.showVsan(false);
List<String> fabricIds = new ArrayList<String>();
for (Integer vsanId : vsanMap.keySet()) {
fabricIds.add(vsanId.toString());
}
return fabricIds;
} catch (Exception ex) {
_log.error("Cannot read fabric ids: " + ex.getLocalizedMessage());
throw ex;
} finally {
disconnect(dialog);
}
}
@Override
public Map<String, String> getFabricIdsMap(NetworkSystem network) throws Exception {
Map<String, String> fabricIdsMap = new HashMap<String, String>();
MDSDialog dialog = null;
try {
dialog = setUpDialog(network);
Map<Integer, String> vsanWwnMap = dialog.getVsanWwns(null);
for (Integer v : vsanWwnMap.keySet()) {
fabricIdsMap.put(vsanWwnMap.get(v).toUpperCase(), String.valueOf(v));
}
} catch (Exception ex) {
_log.error("Cannot get fabric ids map for network device "
+ network.getLabel() + ": " + ex.getLocalizedMessage());
throw ex;
} finally {
disconnect(dialog);
}
return fabricIdsMap;
}
@Override
public List<Zoneset> getZonesets(NetworkSystem network, String fabricId, String fabricWwn, String zoneName, boolean excludeMembers,
boolean excludeAliases) throws Exception {
MDSDialog dialog = null;
try {
dialog = setUpDialog(network);
Integer vsanId = checkVsanFabric(dialog, fabricId, fabricWwn);
List<Zoneset> zonesets = dialog.showZoneset(vsanId, true, zoneName, excludeMembers, excludeAliases);
return zonesets;
} catch (Exception ex) {
_log.error("Cannot get zones: " + ex.getLocalizedMessage());
throw ex;
} finally {
disconnect(dialog);
}
}
/**
* Checks to see if the fabricId matches the fabricWwn. If a fabricWwn is supplied,
* and it can be matched to a vsan, that fabricId will be used.
*
* @param fabricId - Normally the VSAN id as a string
* @param fabricWwn - optional fabric WWN
* @return vsanId
*/
private Integer checkVsanFabric(MDSDialog dialog, String fabricId, String fabricWwn)
throws NetworkDeviceControllerException {
if (fabricWwn != null && fabricWwn.matches(wwnRegex)) {
Map<Integer, String> vsanWwnMap = null;
try {
// Optimal case:
// Both fabricId and fabricWwn supplied; fabricId matches vsan containing WWN
Integer vsanId = new Integer(fabricId);
vsanWwnMap = dialog.getVsanWwns(new Integer(fabricId));
String vsanwwn = vsanWwnMap.get(vsanId);
if (null != vsanwwn && vsanwwn.equals(fabricWwn)) {
return vsanId;
}
} catch (Exception ex) {
_log.warn("Exception while getting vsan wwns for {}", fabricId, ex);
}
// fabricId mal-formated (i.e. not a Vsan number) or doesn't match WWN
vsanWwnMap = dialog.getVsanWwns(null);
for (Integer v : vsanWwnMap.keySet()) {
if (fabricWwn.equalsIgnoreCase(vsanWwnMap.get(v))) {
return v;
}
}
throw NetworkDeviceControllerException.exceptions.checkVsanFabricFailedNotFound(
fabricId, fabricWwn);
}
try {
return new Integer(fabricId);
} catch (NumberFormatException ex) {
throw NetworkDeviceControllerException.exceptions.checkVsanFabricFailed(fabricId, ex);
}
}
@Override
public BiosCommandResult addZones(NetworkSystem network, List<Zone> zones, String fabricId, String fabricWwn,
boolean activateZones) throws NetworkDeviceControllerException {
BiosCommandResult result = null;
MDSDialog dialog = null;
Map<String, String> addedZoneNames = new HashMap<String, String>();
try {
dialog = setUpDialog(network);
Integer vsanId = checkVsanFabric(dialog, fabricId, fabricWwn);
List<IvrZone> addingIvrZones = new ArrayList<IvrZone>();
List<Zone> addingZones = new ArrayList<Zone>();
for (Zone zone : zones) {
IvrZone routedZone = getRoutedZone(dialog, zone, network);
// if zone is routed, handle it as routed network. Otherwise, handle it
// as normal zone
if (routedZone != null) {
addingIvrZones.add(routedZone);
} else {
addingZones.add(zone);
}
}
if (!addingZones.isEmpty()) {
addedZoneNames.putAll(addZonesStrategy(dialog, addingZones, vsanId, activateZones));
}
if (!addingIvrZones.isEmpty()) {
addedZoneNames.putAll(addIvrZonesStrategy(dialog, addingIvrZones));
}
_log.info("Add SAN zones results: " + toMessage(addedZoneNames));
String msg = "Vsan: " + fabricId + ": Successfully added zones: " + addedZoneNames.toString();
if (addedZoneNames.size() == 0) {
msg = "Vsan: " + fabricId + ": No zones were added";
}
_log.info(msg);
result = getBiosCommandResult(addedZoneNames);
} catch (Exception ex) {
_log.error("Cannot add zones: " + (ex.getCause() != null ? ex.getCause().getMessage() : ex.getLocalizedMessage()));
throw ex;
} finally {
disconnect(dialog);
}
return result;
}
@Override
public BiosCommandResult removeZones(NetworkSystem network, List<Zone> zones, String fabricId, String fabricWwn,
boolean activateZones) throws NetworkDeviceControllerException {
BiosCommandResult result = null;
MDSDialog dialog = null;
Map<String, String> removedZoneNames = new HashMap<String, String>();
try {
dialog = setUpDialog(network);
Integer vsanId = checkVsanFabric(dialog, fabricId, fabricWwn);
List<IvrZone> removingIvrZones = new ArrayList<IvrZone>();
List<Zone> removingZones = new ArrayList<Zone>();
for (Zone zone : zones) {
IvrZone routedZone = getRoutedZone(dialog, zone, network);
// if zone is routed, handle it as routed network. Otherwise, handle it
// as normal zone
if (routedZone != null) {
removingIvrZones.add(routedZone);
} else {
removingZones.add(zone);
}
}
//Throw artificial exception here to simulate FOD for MDS same as creating alias
InvokeTestFailure.internalOnlyInvokeTestFailure(InvokeTestFailure.ARTIFICIAL_FAILURE_057);
if (!removingZones.isEmpty()) {
removedZoneNames.putAll(removeZonesStrategy(dialog, removingZones, vsanId, activateZones));
}
if (!removingIvrZones.isEmpty()) {
removedZoneNames.putAll(removeIvrZonesStrategy(dialog, removingIvrZones));
}
_log.info("Remove VSAN zone results: " + toMessage(removedZoneNames));
result = getBiosCommandResult(removedZoneNames);
} catch (Exception ex) {
_log.error("Cannot remove zones: " + (ex.getCause() != null ? ex.getCause().getMessage() : ex.getLocalizedMessage()));
throw ex;
} finally {
disconnect(dialog);
}
return result;
}
/**
* Given a dialog, add one or more zones to the active zoneset of the specified vsan.
* This method is callable from with Bourne or from MDSDialogTest for stand-alone testing.
* For now the only type of zone members supported are pwwn.
*
* @param dialog - An MDSDialog, containing dialog state to the device
* @param zones - List of zones to be created. Zone names will be overwritten.
* @param vsanId - Integer vsanId
* @param activateZones - activate active zoneset after specified zones are added
* @return a map that contains the outcome for each zone keyed by zone name
* @throws ControllerException
*/
public Map<String, String> addZonesStrategy(MDSDialog dialog, List<Zone> zones, Integer vsanId, boolean activateZones)
throws NetworkDeviceControllerException {
waitForSession(dialog, vsanId);
Long time = System.currentTimeMillis();
// a zone-name-to-result map to hold the results for each zone
Map<String, String> addedZoneNames = new HashMap<String, String>();
// First determine if there is an active zoneset.
Zoneset activeZoneset = getActiveZoneset(dialog, vsanId);
// There is no active zone set. So we'll create one. TBD
if (activeZoneset == null) {
_log.info("No active zoneset vsan: " + vsanId);
throw NetworkDeviceControllerException.exceptions.noActiveZonesetForFabric(vsanId.toString());
}
List<Zone> fabricZones = dialog.showFabricZones(vsanId);
try {
// Go into config mode. This allows us to change the configuration.
dialog.config();
boolean doZonesetClone = false;
for (Zone zone : zones) {
try {
if (createZone(dialog, zone, vsanId, fabricZones, activeZoneset)) {
addedZoneNames.put(zone.getName(), SUCCESS);
doZonesetClone = true;
} else {
addedZoneNames.put(zone.getName(), NO_CHANGE);
}
} catch (Exception ex) {
addedZoneNames.put(zone.getName(), ERROR + ": " + ex.getMessage());
handleZonesStrategyException(ex, activateZones);
}
}
//If there was any changes to the zones, do a clone of the zoneset.
if (doZonesetClone) {
zonesetClone(dialog, vsanId, activeZoneset);
}
// if there were normal zones created, commit them
if (hasResult(addedZoneNames, SUCCESS)) {
// Now add all the zones to the active zoneset.
dialog.zonesetNameVsan(activeZoneset.getName(), vsanId, false);
for (String zoneName : addedZoneNames.keySet()) {
if (SUCCESS.equals(addedZoneNames.get(zoneName))) {
dialog.zonesetMember(zoneName, false);
}
}
dialog.exitToConfig();
commitZones(dialog, vsanId, activateZones ? activeZoneset : null);
dialog.copyRunningConfigToStartupFabric();
}
dialog.endConfig();
time = System.currentTimeMillis() - time;
_log.info("Zone add time (msec): " + time.toString());
return addedZoneNames;
} catch (Exception ex) {
throw NetworkDeviceControllerException.exceptions.addZonesStrategyFailed(ex);
} finally {
safeExitSession(dialog, vsanId);
}
}
/**
* Add one or more ivr zones to the active zoneset of the specified vsan.
* For now the only type of zone members supported are pwwn.
*
* @param ivrZones - List of zones to be created. Zone names will be overwritten.
* @param vsanId - Integer vsanId
* @return a map that contains the outcome for each zone keyed by zone name
* @throws ControllerException
*/
private Map<String, String> addIvrZonesStrategy(MDSDialog dialog, List<IvrZone> ivrZones) throws NetworkDeviceControllerException {
// list to hold ivr zone names which are added to fabric
Map<String, String> addedIvrZoneNames = new HashMap<String, String>();
Long time = System.currentTimeMillis();
for (IvrZone ivrZone : ivrZones) {
if (addIvrZone(dialog, ivrZone)) {
addedIvrZoneNames.put(ivrZone.getName(), SUCCESS);
} else {
addedIvrZoneNames.put(ivrZone.getName(), NO_CHANGE);
}
}
time = System.currentTimeMillis() - time;
_log.info("Ivr Zone add time (msec): " + time.toString());
return addedIvrZoneNames;
}
protected String getDefaultZonesetName(String vsanId) {
return "Zoneset_" + vsanId;
}
private Zoneset createActiveZoneset(MDSDialog dialog, Integer vsanId) {
String zonesetName = getDefaultZonesetName(vsanId.toString());
_log.info("Attempting to create zoneset: " + zonesetName + " vsan: " + vsanId);
try {
dialog.config();
dialog.zonesetNameVsan(zonesetName, vsanId, false);
dialog.exitToConfig();
if (dialog.isInSession()) {
dialog.zoneCommit(vsanId);
dialog.waitForZoneCommit(vsanId);
}
dialog.endConfig();
return new Zoneset(zonesetName);
} catch (NetworkDeviceControllerException ex) {
_log.info("Unable to create zoneset: " + zonesetName);
throw NetworkDeviceControllerException.exceptions.addZonesStrategyFailedNotFound(
vsanId.toString(), ex);
} finally {
safeExitSession(dialog, vsanId);
}
}
/**
* Creates the zone in the fabric's active zoneset.
* Checks if the zone with the same name already exists before the zone is created. The
* rules for creating a zone are:
* If an active zone with the same name exists, ensure that all the desired members
* are in the zone. If this is true, consider the zone created. If not, error because
* the application is not going to modify an existing zone.
* If an inactive zone with the same name exists, delete the inactive zone and then
* create the new one.
*
* @param dialog an MDSDialog, containing dialog state to the device
* @param zone the zone to be created
* @param vsanId vsan Id
* @param zonesInFabric a list containing all the zones in the fabric,
* both active an inactive
* @param activeZoneset the active zoneset in which the zone will be created
* @throws NetworkDeviceControllerException
*/
private boolean createZone(MDSDialog dialog, Zone zone, Integer vsanId,
List<Zone> zonesInFabric, Zoneset activeZoneset)
throws NetworkDeviceControllerException {
_log.info("Creating zone: " + zone.getName() + " vsan: " + vsanId);
boolean added = false;
// check if an active zone with the same name exists
Zone zoneInFabric = getZoneInFabric(zone.getName(), activeZoneset.getZones());
if (zoneInFabric != null) {
// if an active zone is found, but have different member size, throw exception
if (!sameMembers(zoneInFabric, zone)) {
throw NetworkDeviceControllerException.exceptions.activeZoneWithSameNameExists(zone.getName());
}
_log.info("Found existing active zone with the name " + zone.getName() + ". No create necessary");
} else {
// check if an inactive zone with the same name exists
zoneInFabric = getZoneInFabric(zone.getName(), zonesInFabric);
if (zoneInFabric != null) {
// delete the zone
_log.info("Found an inactive zone with the name " + zone.getName());
dialog.zoneNameVsan(zoneInFabric.getName(), vsanId, true);
_log.info("Deleted inactive zone with the name " + zone.getName());
}
// create the new zone
dialog.zoneNameVsan(zone.getName(), vsanId, false);
try {
for (ZoneMember member : zone.getMembers()) {
if (!StringUtils.isEmpty(member.getAlias())) {
dialog.zoneMemberAlias(member.getAlias());
} else {
dialog.zoneMemberPwwn(member.getAddress());
}
}
} finally {
// be sure to exit add zone member mode
dialog.exitToConfig();
}
added = true;
}
return added;
}
/**
* Searches the collection of zone for a zone that matches name and active state.
*
* @param name the zone name
* @param zonesInFabric the list of zones to search
* @return a zone that is matched by name. Null if a zone was not found.
*/
private Zone getZoneInFabric(String name, List<Zone> zonesInFabric) {
for (Zone zone : zonesInFabric) {
if (zone.getName().equals(name)) {
return zone;
}
}
return null;
}
/**
* Given an MDS dialog, removes one or more zones from the active zoneset in the specified vsan.
* This method is callable from with Bourne or from MDSDialogTest for stand-alone testing.
* For now the only type of zone members supported are pwwn.
*
* @param dialog
* @param zones List<Zone> - name is ignored, members are checked against existing zones
* @param vsanId
* @return a map that contains the outcome for each zone keyed by zone name
* @throws NetworkControllerException
*/
public Map<String, String> removeZonesStrategy(MDSDialog dialog, List<Zone> zones, Integer vsanId, boolean activateZones)
throws NetworkDeviceControllerException {
waitForSession(dialog, vsanId);
Long time = System.currentTimeMillis();
// a zone-name-to-result map to hold the results for each zone
Map<String, String> removedZoneNames = new HashMap<String, String>();
// First determine if there is an active zone.
Zoneset activeZoneset = getActiveZoneset(dialog, vsanId);
if (activeZoneset == null) {
// if no active or default zoneset presents, consider none is removed
String defaultZonesetName = getDefaultZonesetName(vsanId.toString());
_log.warn("No active/default zoneset found: " + defaultZonesetName);
throw NetworkDeviceControllerException.exceptions.noActiveZonesetForFabric(vsanId.toString());
}
// Find the set of zones to be actually deleted.
// We don't attempt to delete zones that are already gone.
// And we don't delete zones that Bourne didn't create.
Integer[] remainingZones = new Integer[1];
List<Zone> zonesToBeDeleted = getZonesToBeDeleted(zones, activeZoneset.getZones(), remainingZones, removedZoneNames);
// If all zones already deleted, return.
if (zonesToBeDeleted.isEmpty()) {
return removedZoneNames;
}
try {
dialog.config();
zonesetClone(dialog, vsanId, activeZoneset);
dialog.zonesetNameVsan(activeZoneset.getName(), vsanId, false);
for (Zone zone : zonesToBeDeleted) {
String zoneName = zone.getName();
_log.info("Removing zone: " + zoneName + "zoneset: " + activeZoneset.getName() + "vsan: " + vsanId);
try {
dialog.zonesetMember(zone.getName(), true);
removedZoneNames.put(zoneName, SUCCESS);
} catch (Exception ex) {
removedZoneNames.put(zoneName, ERROR + " : " + ex.getMessage());
handleZonesStrategyException(ex, activateZones);
}
}
_log.info("going back to config prompt");
dialog.exitToConfig();
if (activateZones) {
dialog.zonesetActivate(activeZoneset.getName(), vsanId, ((remainingZones[0] == 0) ? true : false));
}
if (dialog.isInSession()) {
dialog.zoneCommit(vsanId);
dialog.waitForZoneCommit(vsanId);
}
dialog.copyRunningConfigToStartupFabric();
dialog.endConfig();
time = System.currentTimeMillis() - time;
_log.info("Zone remove time (msec): " + time.toString());
return removedZoneNames;
} catch (Exception ex) {
throw NetworkDeviceControllerException.exceptions.removeZonesStrategyFailed(ex);
} finally {
safeExitSession(dialog, vsanId);
}
}
/**
*
*
* @param removingIvrZones
* @param vsanId
* @return a map that contains the outcome for each zone keyed by zone name
*/
private Map<String, String> removeIvrZonesStrategy(MDSDialog dialog, List<IvrZone> removingIvrZones) {
Long time = System.currentTimeMillis();
// a zone-name-to-result map to hold the results for each zone
Map<String, String> removedZoneNames = new HashMap<String, String>();
for (IvrZone ivrZone : removingIvrZones) {
if (removeIvrZone(dialog, ivrZone)) {
removedZoneNames.put(ivrZone.getName(), SUCCESS);
} else {
removedZoneNames.put(ivrZone.getName(), NO_CHANGE);
}
}
time = System.currentTimeMillis() - time;
_log.info("Ivr Zone remove time (msec): " + time.toString());
return removedZoneNames;
}
/**
* Verify whether an ivrZone is in any of ivr zonesets
*
* @param removingIvrZones
* @param ivrZonesetInFabric
* @return list of ivr zone to be deleted
*/
private boolean isInZonesets(IvrZone ivrZone, List<IvrZoneset> ivrZonesetInFabric) {
boolean inZoneset = false;
for (IvrZoneset ivrZoneset : ivrZonesetInFabric) {
inZoneset = ivrZoneset.getZones().contains(ivrZone);
if (inZoneset) {
break;
}
}
return inZoneset;
}
/**
* Check to see if the Vsan is already in a session. If so wait until it might be cleared,
* or throw an NetworkControllerException. We don't want to assume control of a pre-existing
* session.
*
* @param dialog
* @param fabricId
* @throws NetworkDeviceControllerException
*/
private void waitForSession(MDSDialog dialog, Integer vsanId)
throws NetworkDeviceControllerException {
boolean isInSession = dialog.isSessionInProgress(vsanId);
/*
* compute retry attempts based on the configured timeout.
* will retry in every SLEEP_TIME_PER_RETRY until exceeded the timeout value
* Add one more attempt to ensure timeout value is reached
*/
int retryAttempts = getDefaultTimeout() / MDSDialogProperties.SLEEP_TIME_PER_RETRY + 1;
for (int retrys = 0; isInSession == true && retrys < retryAttempts; retrys++) {
try {
Thread.sleep(MDSDialogProperties.SLEEP_TIME_PER_RETRY);
} catch (InterruptedException ex) {
_log.warn(ex.getLocalizedMessage());
}
isInSession = dialog.isSessionInProgress(vsanId);
}
if (isInSession) {
throw NetworkDeviceControllerException.exceptions.waitForSessionFailedTimeout(vsanId.toString());
}
}
@Override
public String getVersion(NetworkSystem network) throws NetworkDeviceControllerException {
MDSDialog dialog = null;
String[] versInfo = null;
try {
dialog = setUpDialog(network);
versInfo = dialog.showVersion();
} catch (Exception ex) {
throw NetworkDeviceControllerException.exceptions.getVersionFailed(
ex.getCause() != null ? ex.getCause().getMessage() : ex.getMessage(), ex);
} finally {
disconnect(dialog);
}
return versInfo[1];
}
@Override
public String getUptime(NetworkSystem network) throws NetworkDeviceControllerException {
MDSDialog dialog = null;
String systemUptime = null;
try {
dialog = setUpDialog(network);
systemUptime = dialog.showSystemUptime();
} catch (Exception ex) {
throw NetworkDeviceControllerException.exceptions.getUptimeFailed(
ex.getCause() != null ? ex.getCause().getMessage() : ex.getMessage(), ex);
} finally {
disconnect(dialog);
}
return systemUptime;
}
@Override
public Set<String> getRoutedEndpoints(NetworkSystem networkSystem,
String fabricId, String fabricWwn) throws Exception {
MDSDialog dialog = null;
Set<String> routedEndpoints = Sets.newHashSet();
try {
dialog = setUpDialog(networkSystem);
List<FCEndpoint> fcEndpoints = dialog.showFcnsDatabase(Integer.valueOf(fabricId));
if (fcEndpoints != null) {
for (FCEndpoint fcEndpoint : fcEndpoints) {
if (MDS_ROUTED_INDICATOR.equalsIgnoreCase(fcEndpoint.getSwitchInterface())) {
routedEndpoints.add(fcEndpoint.getRemotePortName());
}
}
}
} finally {
if (dialog != null) {
disconnect(dialog);
}
}
return routedEndpoints;
}
/**
* Creates the ivr zone in the fabric's active zoneset.
* Checks if the zone with the same name already exists before the zone is created. The
* rules for creating a zone are:
* If an active ivr zone with the same name exists, ensure that all the desired members
* are in the ivr zone. If this is true, consider the ivr zone created. If not, error because
* the application is not going to modify an existing zone.
* If an inactive ivr zone with the same name exists, delete the inactive zone and then
* create the new one.
*
* @param dialog an MDSDialog, containing dialog state to the device
* @param ivrZone the zone to be created
* @param vsanId vsan Id
* @param ivrZonesInFabric a list containing all the zones in the fabric,
* both active an inactive
* @param activeIvrZoneset the active ivr zoneset in which the zone will be created
* @throws NetworkDeviceControllerException
*/
private boolean createIvrZone(MDSDialog dialog, IvrZone ivrZone, List<IvrZone> ivrZonesInFabric, IvrZoneset activeIvrZoneset)
throws NetworkDeviceControllerException {
_log.info("Creating ivr zone: " + ivrZone.getName());
boolean added = false;
// check if an active zone with the same name exists
IvrZone activeIvrZone = getIvrZoneInFabric(ivrZone.getName(), activeIvrZoneset.getZones());
if (activeIvrZone != null) {
_log.info("Found an active ivr zone with the name " + activeIvrZone.getName());
// if an active zone is found, get its members
if (activeIvrZone.getMembers().size() != ivrZone.getMembers().size()) {
throw NetworkDeviceControllerException.exceptions.activeZoneWithSameNameExists(activeIvrZone.getName());
}
for (IvrZoneMember member : ivrZone.getMembers()) {
if (!activeIvrZone.contains(member)) {
// I have a active zone with the same name but it does not have all the members we
// need - at this time we're not modifying zones that already exist
// and this one cannot be used without adding the missing members, so error
_log.info("Zone member pwwn/vsanId: " + member.getPwwn() + "/" + member.getVsanId()
+ " was not found in the active zone.");
throw NetworkDeviceControllerException.exceptions.activeZoneWithSameNameExists(activeIvrZone.getName());
}
}
} else {
// check if an inactive zone with the same name exists
IvrZone ivrZoneInFabric = getIvrZoneInFabric(ivrZone.getName(), ivrZonesInFabric);
if (ivrZoneInFabric != null) {
// delete the zone
_log.info("Found an inactive zone with the name " + ivrZoneInFabric.getName());
dialog.ivrZoneName(ivrZoneInFabric.getName(), true);
_log.info("Deleted inactive zone with the name " + ivrZoneInFabric.getName());
}
// create the new zone
dialog.ivrZoneName(ivrZone.getName(), false);
for (IvrZoneMember member : ivrZone.getMembers()) {
dialog.ivrZoneMember(member.getPwwn(), member.getVsanId(), false);
}
dialog.exitToConfig();
added = true;
}
return added;
}
/**
* Zones were previously added into fabric, thus need to add them into appropriate vsan as memebers
* then activate the vsan.
*
* @param dialog
* @param zoneNames list of zone to add to vsan
* @param vsanId
* @param activeZoneset
*/
private void commitZones(MDSDialog dialog, Integer vsanId, Zoneset activeZoneset) {
// Activate the active zoneset.
if (activeZoneset != null) {
dialog.zonesetActivate(activeZoneset.getName(), vsanId, false);
}
// dialog.exitToConfig(); -- no need for exitToConfig, because activate zoneset would exit
// If enhanced zoning is enabled, we will be in a session, and we must commit.
if (dialog.isInSession()) {
dialog.zoneCommit(vsanId);
dialog.waitForZoneCommit(vsanId);
}
}
/**
* Perform a "zoneset clone" of the zoneset in the zoning operation
* @param dialog
* @param vsanId
* @param activeZoneset
*/
private void zonesetClone(MDSDialog dialog, Integer vsanId, Zoneset activeZoneset) {
boolean doZonesetClone = true;
boolean allowZonesIfZonesetCloneFails = true;
try {
doZonesetClone = Boolean.valueOf(ControllerUtils.getPropertyValueFromCoordinator(_coordinator,
"controller_mds_clone_zoneset")) ;
allowZonesIfZonesetCloneFails = Boolean.valueOf(ControllerUtils.getPropertyValueFromCoordinator(_coordinator,
"controller_mds_allow_zoneset_commit")) ;
} catch (Exception e) {
_log.warn("Zoneset clone properties not set");
}
if (doZonesetClone) {
_log.info(String.format("Cloning zoneset %s", activeZoneset.getName()));
try {
dialog.zonesetClone(vsanId, activeZoneset.getName());
} catch (NetworkDeviceControllerException nde) {
_log.info("Failed to create zoneset clone. Reason : ", nde.getMessage());
if (!allowZonesIfZonesetCloneFails) {
throw nde;
}
}
} else {
_log.info(String.format("controller_mds_clone_zoneset is false, NOT Cloning zoneset %s", activeZoneset.getName()));
}
}
/**
* Ivr zones were previously added to fabric, add them to active zone set, then activate it.
*
* @param dialog
* @param activeIvrZoneset
*/
private void commitIvrZones(MDSDialog dialog, IvrZoneset activeIvrZoneset) {
// activate zone set
dialog.ivrZonesetName(activeIvrZoneset.getName(), true);
dialog.ivrCommit();
dialog.waitForIvrZonesetActivate();
}
/**
* Create an ivr zone set with embed switch's wwn, and activate it.
*
* @param dialog
* @return
*/
private IvrZoneset createActiveIvrZoneset(MDSDialog dialog) {
String zonesetName = "IVR_Zoneset_" + dialog.showSwitchWwn().replace(':', '_');
_log.info("Attempting to create active ivr zoneset: " + zonesetName);
try {
dialog.config();
dialog.ivrZonesetName(zonesetName, true);
dialog.exitToConfig();
if (dialog.isInSession()) {
dialog.ivrCommit();
}
dialog.endConfig();
return new IvrZoneset(zonesetName);
} catch (NetworkDeviceControllerException ex) {
_log.info("Unable to create zoneset: " + zonesetName);
throw NetworkDeviceControllerException.exceptions.addZonesStrategyFailedNotFound(
zonesetName, ex);
} finally {
if (dialog.isInSession()) {
dialog.endConfig();
dialog.config();
dialog.ivrAbort();
dialog.endConfig();
}
}
}
/**
* Check if given ivr zone name is existed in list of ivr zone
*
* @param name the zone name
* @param ivrZones the list of zones to search
* @return an ivr zone that is matched by name. Null if a zone was not found.
*/
private IvrZone getIvrZoneInFabric(String name, List<IvrZone> ivrZones) {
for (IvrZone zone : ivrZones) {
if (zone.getName().equals(name)) {
return zone;
}
}
return null;
}
/**
* Get corresponded ivr switch for the given network
*
* @param dialog - dialog session for borderNetworkSystem
* @param borderNetworkSystem - switch which has a dialog session attached
* @param networkLite
* @return
*/
private NetworkSystem getIvrNetworkSystem(MDSDialog dialog, NetworkSystem borderNetworkSystem, NetworkLite networkLite) {
NetworkSystem ivrNetworkSystem = null;
if (networkLite != null && networkLite.getNetworkSystems() != null) {
for (String networkSystemId : networkLite.getNetworkSystems()) {
ivrNetworkSystem = _dbClient.queryObject(NetworkSystem.class, URI.create(networkSystemId));
// if potential ivrNetworkSystem is the same as borderNetworksystem, then use the attached dialog session to
// verified whether is is an IVR or not. This check is to avoid open a new dialog session of the borderNetworkSystem.
boolean isIvrEnabled = StringUtils.equals(borderNetworkSystem.getId().toString(), ivrNetworkSystem.getId().toString()) ?
dialog.isIvrEnabled() : isIvrEnabled(ivrNetworkSystem);
if (isIvrEnabled) {
break;
} else {
ivrNetworkSystem = null;
}
}
}
return ivrNetworkSystem;
}
/**
* Verify if vsan in the list are the same
*
* @param ivrZoneMembers
* @return true if all vsan in the list are the same, false otherwise
*/
private boolean areIvrZoneMembersInSameNetwork(List<IvrZoneMember> ivrZoneMembers) {
boolean sameNetwork = true;
if (ivrZoneMembers != null && ivrZoneMembers.size() > 1) {
/*
* check if 2 ports are routed if they are in different networks
*/
Integer baseVsan = ivrZoneMembers.get(0).getVsanId();
for (IvrZoneMember ivrZoneMember : ivrZoneMembers) {
if (!baseVsan.equals(ivrZoneMember.getVsanId())) {
sameNetwork = false;
break;
}
}
}
return sameNetwork;
}
/**
* Construct an in memory ivr zone, if applied.
*
* @param dialog dialog session to borderNetworkSystem
* @param zone
* @param borderNetworkSystem ivr border network which has a dialog session open
* @return an ivr zone
*/
private IvrZone getRoutedZone(MDSDialog dialog, Zone zone, NetworkSystem borderNetworkSystem) {
IvrZone ivrZone = null;
Set<String> vsanIds = Sets.newHashSet(); // member vsan in ivr zone
if (zone != null && zone.getMembers() != null && zone.getMembers().size() == 2) {
ivrZone = new IvrZone(zone.getName());
List<IvrZoneMember> ivrZoneMembers = ivrZone.getMembers();
// Map<String, String> aliasDatabase = dialog.showDeviceAliasDatabase();
// map zone member address to corresponded network system
Map<String, NetworkLite> networkLiteMap = new HashMap<String, NetworkLite>();
// traverse each pwwn in given zone. If pwwn 's network is routed, added pwwn and its network
// to list of ivr zone member.
for (ZoneMember zoneMember : zone.getMembers()) {
// if zone member pwwn was not set, resolve it. If not resolvable,
// skip it
// TODO - need to revisit if ViPR decides to support alias for IVR
if (StringUtils.isEmpty(zoneMember.getAddress())) {
break;
}
NetworkLite networkLite = NetworkUtil.getEndpointNetworkLite(zoneMember.getAddress().trim(), _dbClient, vsanIds);
if (networkLite != null) {
// cached networklite for later reference
networkLiteMap.put(zoneMember.getAddress(), networkLite);
// if pwwn 's parent network is a routed network, construct an ivr zone member
Set<String> routedNetworks = networkLite.getRoutedNetworks();
if (routedNetworks != null && !routedNetworks.isEmpty()) {
Integer networkVsanId = Integer.valueOf(networkLite.getNativeId());
ivrZoneMembers.add(new IvrZoneMember(zoneMember.getAddress(), networkVsanId));
vsanIds.add(networkVsanId.toString());
}
}
}
/*
* if ivr zone members are in the same network, then they are not routed.
* Then, null out ivr zone.
*/
if (areIvrZoneMembersInSameNetwork(ivrZoneMembers) || ivrZoneMembers.size() <= 1) {
ivrZoneMembers.clear();
ivrZone = null;
} else {
// if it is an ivr zone,
// loop through to get ivr networksystem to later perform ivr cli on
for (IvrZoneMember ivrZoneMember : ivrZoneMembers) {
// if no ivr network system was set for ivr zone, find and set it
if (ivrZone.getIvrNetworkSystem() == null) {
NetworkLite networkLite = networkLiteMap.get(ivrZoneMember.getPwwn());
ivrZone.setIvrNetworkSystem(getIvrNetworkSystem(dialog, borderNetworkSystem, networkLite));
}
// if an ivr network system is set, exit loop
if (ivrZone.getIvrNetworkSystem() != null) {
break;
}
}
}
}
return ivrZone;
}
/**
* Check the given vsan is contained in ivr topology
*
* @param dialog
* @param vsanId
* @return
*/
private boolean isIvrVsan(MDSDialog dialog, Integer vsanId) {
boolean isIvrVsan = false;
List<IvrVsanConfiguration> ivrVsansList = dialog.showIvrVsanTopology();
for (IvrVsanConfiguration ivrVsans : ivrVsansList) {
if (ivrVsans.isIvrVsan(vsanId)) {
isIvrVsan = true;
break;
}
}
return isIvrVsan;
}
/**
* Verify if given network system is ivr enabled
*
* @param networkSystem
* @return
* @throws NetworkDeviceControllerException
*/
private boolean isIvrEnabled(NetworkSystem networkSystem) throws NetworkDeviceControllerException {
MDSDialog dialog = null;
try {
dialog = setUpDialog(networkSystem);
return dialog.isIvrEnabled();
} catch (NetworkDeviceControllerException ex) {
_log.error("Cannot remove zones: " + (ex.getCause() != null ? ex.getCause().getMessage() : ex.getLocalizedMessage()));
throw ex;
} finally {
disconnect(dialog);
}
}
/**
* remove an ivrZone for vsanId. If is being removed, return true. If edge switch and
* ivr switch is the same, use existing dialog session. Otherwise create new session on
* ivr switch to process ivr zone
*
* @param edgeDialog - created dialog session from edge switch
* @param ivrZone
* @param vsanId
* @return
*/
private boolean removeIvrZone(MDSDialog edgeDialog, IvrZone ivrZone) {
// check if edge and ivr switch are the same switch
boolean isSameHost = ivrZone.getIvrNetworkSystem().getIpAddress().equals(edgeDialog.getSession().getSession().getHost());
// use edgeDialog if edge and ivr are the same host
MDSDialog dialog = isSameHost ? edgeDialog : null;
try {
if (dialog == null) {
dialog = setUpDialog(ivrZone.getIvrNetworkSystem());
}
List<IvrZoneset> ivrZonesets = dialog.showIvrZonesets(false);
if (ivrZonesets == null || !isInZonesets(ivrZone, ivrZonesets)) {
return false;
}
dialog.config();
String zoneName = ivrZone.getName();
_log.info("Removing ivr zone: " + zoneName);
// config ivr zone mode
dialog.ivrZoneName(zoneName, false);
// remove member from IvrZone
for (IvrZoneMember ivrZoneMember : ivrZone.getMembers()) {
dialog.ivrZoneMember(ivrZoneMember.getPwwn(), ivrZoneMember.getVsanId(), true);
}
// remove IvrZone from fabric
dialog.ivrZoneName(zoneName, true);
// zone set config mode
IvrZoneset activeIvrZoneset = dialog.showActiveIvrZoneset();
dialog.ivrZonesetName(activeIvrZoneset.getName(), false);
// remove zone member from zone set
dialog.ivrZonesetMember(zoneName, true);
// activate zone set
commitIvrZones(dialog, activeIvrZoneset);
dialog.copyRunningConfigToStartupFabric();
dialog.endConfig();
return true;
} catch (Exception ex) {
throw NetworkDeviceControllerException.exceptions.removeZonesStrategyFailed(ex);
} finally {
if (dialog.isInSession()) {
dialog.endConfig();
dialog.config();
dialog.ivrAbort();
dialog.endConfig();
}
// only disconnect dialog session if new session was created in this method
if (!isSameHost) {
disconnect(dialog);
}
}
}
/**
* add an ivrZone. If is being added, return true. If edge switch and
* ivr switch is the same, use existing dialog session. Otherwise create new session on
* ivr switch to process ivr zone
*
* @param edgeDialog - created dialog session from edge switch
* @param ivrZone
* @param vsanId
* @return
*/
private boolean addIvrZone(MDSDialog edgeDialog, IvrZone ivrZone) {
// check if edge and ivr switch are the same switch
boolean isSameHost = ivrZone.getIvrNetworkSystem().getIpAddress().equals(edgeDialog.getSession().getSession().getHost());
boolean added = false;
// use edgeDialog if edge and ivr are the same host
MDSDialog dialog = isSameHost ? edgeDialog : null;
try {
// initiate dialog if not same host
if (dialog == null) {
dialog = setUpDialog(ivrZone.getIvrNetworkSystem());
}
List<IvrZone> fabricIvrZones = dialog.showIvrZones(false);
// create zone only if its end points are ivr connected
IvrZoneset activeIvrZoneset = dialog.showActiveIvrZoneset();
if (activeIvrZoneset == null) {
_log.info("No active ivr zoneset...create one");
activeIvrZoneset = createActiveIvrZoneset(dialog);
}
// Go into config mode. This allows us to change the configuration.
dialog.config();
// create IVR Zone
added = createIvrZone(dialog, ivrZone, fabricIvrZones, activeIvrZoneset);
if (added) {
// zone set config mode
dialog.ivrZonesetName(activeIvrZoneset.getName(), false);
// add zone as member to
dialog.ivrZonesetMember(ivrZone.getName(), false);
commitIvrZones(dialog, activeIvrZoneset);
dialog.copyRunningConfigToStartupFabric();
}
;
dialog.endConfig();
} catch (Exception ex) {
throw NetworkDeviceControllerException.exceptions.addZonesStrategyFailed(ex);
} finally {
if (dialog.isInSession()) {
dialog.endConfig();
dialog.config();
dialog.ivrAbort();
dialog.endConfig();
}
// only disconnect dialog if it was created in this method
if (!isSameHost) {
disconnect(dialog);
}
}
return added;
}
/**
* Convenient method to get Cisco MDS default timeout value from system configuration file.
* The config value is in second, then convert to milliseconds
*
* @return timeout value in milliseconds
*/
private int getDefaultTimeout() {
int defaultTimeout = 300 * 1000; // default to 5 minutes
try {
defaultTimeout = Integer.valueOf(ControllerUtils.getPropertyValueFromCoordinator(_coordinator,
"controller_mds_communication_timeout")) * 1000;
} catch (Exception e) {
_log.warn(e.getMessage());
}
return defaultTimeout;
}
public void setDbClient(DbClient dbClient) {
if (_dbClient == null) {
_dbClient = dbClient;
}
}
public void setCoordinator(CoordinatorClient coordinator) {
if (_coordinator == null) {
_coordinator = coordinator;
}
}
@Override
public BiosCommandResult updateZones(NetworkSystem network, List<ZoneUpdate> updateZones,
String fabricId, String fabricWwn, boolean activateZones) throws NetworkDeviceControllerException {
MDSDialog dialog = null;
Map<String, String> updatedZoneNames = null;
try {
dialog = setUpDialog(network);
Integer vsanId = checkVsanFabric(dialog, fabricId, fabricWwn);
if (!updateZones.isEmpty()) {
updatedZoneNames = updateZonesStrategy(dialog, updateZones, vsanId, activateZones);
}
_log.info("Update zone results " + toMessage(updatedZoneNames));
} catch (Exception ex) {
_log.error("Cannot update zones: " + (ex.getCause() != null ? ex.getCause().getMessage() : ex.getLocalizedMessage()));
throw ex;
} finally {
disconnect(dialog);
}
return getBiosCommandResult(updatedZoneNames);
}
/**
* Given a dialog, update one or more zones of the specified vsan.
* This method is callable from with Bourne or from MDSDialogTest for stand-alone testing.
* For now the only type of zone members supported are pwwn.
*
* @param dialog - An MDSDialog, containing dialog state to the device
* @param updateZones - List of zones to be created. Zone names will be overwritten.
* @param vsanId - Integer vsanId
* @param activagteZone - true, activate current active zoneset
* @return a map that contains the outcome for each zone keyed by zone name
* @throws ControllerException
*/
private Map<String, String> updateZonesStrategy(MDSDialog dialog, List<ZoneUpdate> updateZones, Integer vsanId, boolean activateZones)
throws NetworkDeviceControllerException {
waitForSession(dialog, vsanId);
Long time = System.currentTimeMillis();
// a zone-name-to-result map to hold the results for each zone
Map<String, String> updatedZoneNames = new HashMap<String, String>();
// First determine if there is an active zoneset.
Zoneset activeZoneset = getActiveZoneset(dialog, vsanId);
List<Zone> fabricZones = dialog.showFabricZones(vsanId);
try {
// Go into config mode. This allows us to change the configuration.
dialog.config();
zonesetClone(dialog, vsanId, activeZoneset);
for (ZoneUpdate zone : updateZones) {
try {
if (updateZone(dialog, zone, vsanId, fabricZones, activateZones)) {
updatedZoneNames.put(zone.getName(), SUCCESS);
} else {
updatedZoneNames.put(zone.getName(), NO_CHANGE);
}
} catch (Exception ex) {
updatedZoneNames.put(zone.getName(), ERROR + " : " + ex.getMessage());
handleZonesStrategyException(ex, activateZones);
}
}
if (!updatedZoneNames.isEmpty()) {
commitZones(dialog, vsanId, activateZones ? activeZoneset : null);
dialog.copyRunningConfigToStartupFabric();
}
dialog.endConfig();
time = System.currentTimeMillis() - time;
_log.info("Zone update time (msec): " + time.toString());
return updatedZoneNames;
} catch (Exception ex) {
throw NetworkDeviceControllerException.exceptions.updateZonesStrategyFailed(ex);
} finally {
safeExitSession(dialog, vsanId);
}
}
/**
* Update the zone of the given fabric
* Checks if the zone with the same name already exists before update. The
* rules for updating a zone are:
* It must be exist in fabric
*
* @param dialog an MDSDialog, containing dialog state to the device
* @param updateZone the zone to be update
* @param vsanId vsan Id
* @param zonesInFabric a list containing all the zones in the fabric,
* both active an inactive
* @throws NetworkDeviceControllerException
*/
private boolean updateZone(MDSDialog dialog, ZoneUpdate updateZone, Integer vsanId,
List<Zone> zonesInFabric, boolean activateZones)
throws NetworkDeviceControllerException {
_log.info("Updating zone: " + updateZone.getName() + " vsan: " + vsanId);
boolean updated = false;
// check if an active zone with the same name exists
Zone zoneInFabric = getZoneInFabric(updateZone.getName(), zonesInFabric);
if (zoneInFabric != null) {
dialog.zoneNameVsan(updateZone.getName(), vsanId, false);
try {
Collection<String> memberAddresses = getWwnsInZone(zoneInFabric);
Collection<String> memberAliases = getAliasesInZone(zoneInFabric);
// remove zone members
for (ZoneMember zoneMember : updateZone.getRemoveZones()) {
if (!StringUtils.isEmpty(zoneMember.getAddress()) && memberAddresses.contains(zoneMember.getAddress())) {
dialog.zoneMemberPwwn(zoneMember.getAddress(), true);
_log.info("Zone member : " + zoneMember.getAddress() + " was removed.");
} else if (!StringUtils.isEmpty(zoneMember.getAlias()) && memberAliases.contains(zoneMember.getAlias())) {
dialog.zoneMemberAlias(zoneMember.getAlias(), true);
_log.info("Zone member of type alias : " + zoneMember.getAlias() + " was removed.");
}
}
// add zone members
for (ZoneMember zoneMember : updateZone.getAddZones()) {
if (!StringUtils.isEmpty(zoneMember.getAddress())) {
dialog.zoneMemberPwwn(zoneMember.getAddress());
_log.info("Zone member : " + zoneMember.getAddress() + " was added.");
} else if (!StringUtils.isEmpty(zoneMember.getAlias())) {
dialog.zoneMemberAlias(zoneMember.getAlias());
_log.info("Zone member of type alias : " + zoneMember.getAlias() + " was added.");
}
}
_log.info("Updated zone: " + updateZone.getName() + " vsan: " + vsanId);
updated = true;
} finally {
dialog.exitToConfig(); // exit zone config mode, since zoneNameVsan() got in to it
}
} else {
throw NetworkDeviceControllerException.exceptions.zoneNotFoundInFabric(updateZone.getName(), vsanId.toString());
}
return updated;
}
@Override
public BiosCommandResult activateZones(NetworkSystem network, String fabricId, String fabricWwn)
throws NetworkDeviceControllerException {
BiosCommandResult result = null;
MDSDialog dialog = null;
try {
dialog = setUpDialog(network);
Integer vsanId = checkVsanFabric(dialog, fabricId, fabricWwn);
String activatedZonesetName = activateZonesStrategy(dialog, vsanId);
String msg = "";
if (activatedZonesetName == null) {
msg = "Vsan: " + fabricId + ": No zoneset was activated";
} else {
msg = "Vsan: " + fabricId + ": Successfully activated zoneset: " + activatedZonesetName;
}
_log.info(msg);
result = BiosCommandResult.createSuccessfulResult();
} catch (NetworkDeviceControllerException ex) {
_log.error("Cannot activate zoneset: " + (ex.getCause() != null ? ex.getCause().getMessage() : ex.getLocalizedMessage()));
throw ex;
} finally {
disconnect(dialog);
}
return result;
}
/**
* Given a dialog, activate current active zoneset of the specified vsan. Error out if vsan have
* have a empty zoneset.
*
* @param dialog - An MDSDialog, containing dialog state to the device
* @param vsanId - Integer vsanId
* @return list of zone names that were activated
* @throws ControllerException
*/
private String activateZonesStrategy(MDSDialog dialog, Integer vsanId) throws NetworkDeviceControllerException {
waitForSession(dialog, vsanId);
Long time = System.currentTimeMillis();
// First determine if there is an active zoneset.
Zoneset activeZoneset = getActiveZoneset(dialog, vsanId);
// There is no non-empty active zone set. Throw exception
if (activeZoneset == null || activeZoneset.getZones().isEmpty()) {
_log.error("Activate zone requires vsan: " + vsanId + " to have a non-empty active zoneset.");
throw NetworkDeviceControllerException.exceptions.noActiveZonesetForFabric(vsanId.toString());
}
try {
// Go into config mode. This allows us to change the configuration.
dialog.config();
zonesetClone(dialog, vsanId, activeZoneset);
commitZones(dialog, vsanId, activeZoneset);
dialog.copyRunningConfigToStartupFabric();
dialog.endConfig();
time = System.currentTimeMillis() - time;
_log.info("Zoneset: " + activeZoneset.getName() + " activate time (msec): " + time.toString());
return activeZoneset.getName();
} catch (Exception ex) {
throw NetworkDeviceControllerException.exceptions.activateZonesStrategyFailed(ex);
} finally {
safeExitSession(dialog, vsanId);
}
}
private Zoneset getActiveZoneset(MDSDialog dialog, Integer vsanId) {
// First determine if there is an active zoneset.
return dialog.showActiveZoneset(vsanId);
}
private void safeExitSession(MDSDialog dialog, Integer vsanId) {
if (dialog != null && dialog.isInSession()) {
dialog.endConfig();
dialog.config();
dialog.noZoneCommit(vsanId);
dialog.endConfig();
}
}
@Override
public List<ZoneWwnAlias> getAliases(NetworkSystem network, String fabricId, String fabricWwn) throws Exception {
// First determine if there is an active zoneset.
MDSDialog dialog = null;
List<ZoneWwnAlias> aliases = new ArrayList<>();
try {
dialog = setUpDialog(network);
Map<String, String> aliasMap = dialog.showDeviceAliasDatabase();
for (Map.Entry<String, String> aliasEntry : aliasMap.entrySet()) {
aliases.add(new ZoneWwnAlias(aliasEntry.getKey(), aliasEntry.getValue()));
}
return aliases;
} catch (Exception ex) {
_log.error("Cannot get aliases: " + ex.getLocalizedMessage());
throw ex;
} finally {
disconnect(dialog);
}
}
@Override
public BiosCommandResult addAliases(NetworkSystem network,
List<ZoneWwnAlias> addingAliases, String fabricId, String fabricWwn)
throws NetworkDeviceControllerException {
BiosCommandResult result = null;
MDSDialog dialog = null;
Map<String, String> addedAliasesName = new HashMap<String, String>();
try {
dialog = setUpDialog(network);
if (!addingAliases.isEmpty()) {
addedAliasesName.putAll(addAliasesStrategy(dialog, addingAliases));
}
String msg = "Successfully added aliases: " + addedAliasesName.toString();
if (!hasResult(addedAliasesName, SUCCESS)) {
msg = "Network System: " + network.getLabel() + ": No aliases were added";
}
_log.info(msg);
result = getBiosCommandResult(addedAliasesName);
} catch (Exception ex) {
_log.error("Cannot add aliases: " + (ex.getCause() != null ? ex.getCause().getMessage() : ex.getLocalizedMessage()));
throw ex;
} finally {
disconnect(dialog);
}
return result;
}
/**
* Given a dialog, add one or more aliases to the network system.
*
* @param dialog - An MDSDialog, containing dialog state to the device
* @param addingAliases - List of aliases to be added.
* @return a map that contains the outcome for each alias keyed by alias name
* @throws ControllerException
*/
private Map<String, String> addAliasesStrategy(MDSDialog dialog, List<ZoneWwnAlias> addingAliases)
throws NetworkDeviceControllerException {
Long time = System.currentTimeMillis();
// a alias-name-to-result map to hold the results for each alias
Map<String, String> addedAliasesName = new HashMap<String, String>();
try {
// Go into config mode. This allows us to change the configuration.
dialog.config();
dialog.deviceAliasConfig();
Map<String, String> aliasMap = dialog.showDeviceAliasDatabase();
for (ZoneWwnAlias wwnAlias : addingAliases) {
String name = wwnAlias.getName();
String wwn = wwnAlias.getAddress();
_log.info("Starting create alias with name " + name);
// get aliass wwn address from database
String currentWwn = aliasMap.get(name);
try {
// if alias address is null or already in system, considered as added
if (StringUtils.isEmpty(wwn) || StringUtils.isEmpty(name) || StringUtils.equalsIgnoreCase(wwn, currentWwn)) {
if (!StringUtils.isEmpty(currentWwn)) {
// alias already exists - this is not an error unless it is for different member
_log.info("The existing alias {} is found with the same WWN {}. Nothing to do.", name, currentWwn);
}
addedAliasesName.put(name, NO_CHANGE);
continue;
} else if (aliasMap.containsKey(name)) {
// if alias already exists with different address, throw exception
throw NetworkDeviceControllerException.exceptions.aliasWithSameNameExists(name, currentWwn, wwn);
} else if (aliasMap.containsValue(wwn)) {
// if wwn is already assigned to another alias, throw exception
throw NetworkDeviceControllerException.exceptions.wwnAssignedToAnotherAlias(wwn, name,
getAliasForWwn(aliasMap, wwn));
}
// add alias to device
dialog.deviceAliasName(name, wwn, false);
addedAliasesName.put(name, SUCCESS);
aliasMap.put(name, wwn);
} catch (Exception ex) {
addedAliasesName.put(name, ERROR + ": " + ex.getMessage());
_log.warn("Exception was encountered but will try the rest of the batch. " +
"Error message: " + ex.getMessage());
}
}
// if there was any alias added, commit them
if (!addedAliasesName.isEmpty()) {
dialog.deviceAliasCommit();
dialog.copyRunningConfigToStartupFabric();
} else {
dialog.exitToConfig();
}
time = System.currentTimeMillis() - time;
_log.info("Aliases add time (msec): " + time.toString());
return addedAliasesName;
} catch (Exception ex) {
dialog.deviceAliasAbort();
throw NetworkDeviceControllerException.exceptions.addAliasesStrategyFailed(ex);
} finally {
dialog.endConfig();
}
}
@Override
public BiosCommandResult removeAliases(NetworkSystem network,
List<ZoneWwnAlias> removingAliases, String fabricId, String fabricWwn)
throws NetworkDeviceControllerException {
BiosCommandResult result = null;
MDSDialog dialog = null;
Map<String, String> removedAliasesName = new HashMap<String, String>();
try {
dialog = setUpDialog(network);
if (!removingAliases.isEmpty()) {
removedAliasesName.putAll(removeAliasesStrategy(dialog, removingAliases));
}
String msg = "Successfully removed aliases: " + removedAliasesName.toString();
if (!hasResult(removedAliasesName, SUCCESS)) {
msg = "Network System: " + network.getLabel() + ": No aliases were removed";
}
_log.info(msg);
result = getBiosCommandResult(removedAliasesName);
} catch (Exception ex) {
_log.error("Cannot remove aliases: " + (ex.getCause() != null ? ex.getCause().getMessage() : ex.getLocalizedMessage()));
throw ex;
} finally {
disconnect(dialog);
}
return result;
}
/**
* Given a dialog, remove one or more aliases from the network system.
*
* @param dialog - An MDSDialog, containing dialog state to the device
* @param removingAliases - List of aliases to be removed.
* @return a map that contains the outcome for each alias keyed by alias name
* @throws ControllerException
*/
private Map<String, String> removeAliasesStrategy(MDSDialog dialog, List<ZoneWwnAlias> removingAliases)
throws NetworkDeviceControllerException {
Long time = System.currentTimeMillis();
// a alias-name-to-result map to hold the results for each alias
Map<String, String> removedAliasesName = new HashMap<String, String>();
try {
// Go into config mode. This allows us to change the configuration.
dialog.config();
dialog.deviceAliasConfig();
Map<String, String> aliasMap = dialog.showDeviceAliasDatabase();
for (ZoneWwnAlias wwnAlias : removingAliases) {
String name = wwnAlias.getName();
String wwn = wwnAlias.getAddress();
_log.info("Starting remove alias with name {}", name);
// get aliass wwn address from database
String currentWwn = aliasMap.get(name);
try {
// if address not found in device, implied that alias is already removed or not
// exist in system, ignore it and continue on
if (StringUtils.isEmpty(currentWwn)) {
_log.info("Did not find alias {}. Nothing to do.", name);
removedAliasesName.put(name, NO_CHANGE);
continue;
} else if (StringUtils.isEmpty(wwn) || StringUtils.equalsIgnoreCase(wwn, currentWwn)) {
_log.info("Found alias {}. The alias will be removed.", name);
// if address is not specified, or it is specified and the same as
// the current alias' wwn, remove it.
dialog.deviceAliasName(name, currentWwn, true);
removedAliasesName.put(name, SUCCESS);
// remove alias from the map, if it was delete successfully
aliasMap.remove(name);
} else {
// if removing alias and the provided address do not match in system, throw exception
_log.info("The existing alias {} has a WWN other than the expected {}. It will not be removed.", name, wwn);
throw NetworkDeviceControllerException.exceptions.aliasWithDifferentWwnExists(name, currentWwn, wwn);
}
} catch (Exception ex) {
removedAliasesName.put(name, ERROR + " : " + ex.getMessage());
_log.warn("Exception was encountered but will try the rest of the batch. " +
"Error message: " + ex.getMessage());
}
}
// if there was any alias added, commit them
if (!removedAliasesName.isEmpty()) {
dialog.deviceAliasCommit();
dialog.copyRunningConfigToStartupFabric();
} else {
dialog.exitToConfig();
}
time = System.currentTimeMillis() - time;
_log.info("Aliases remove time (msec): " + time.toString());
return removedAliasesName;
} catch (Exception ex) {
dialog.deviceAliasAbort();
throw NetworkDeviceControllerException.exceptions.removeAliasesStrategyFailed(ex);
} finally {
dialog.endConfig();
}
}
@Override
public BiosCommandResult updateAliases(NetworkSystem network,
List<ZoneWwnAliasUpdate> updatingAliases, String fabricId, String fabricWwn)
throws NetworkDeviceControllerException {
BiosCommandResult result = null;
MDSDialog dialog = null;
Map<String, String> updatedAliasesName = new HashMap<String, String>();
try {
dialog = setUpDialog(network);
if (!updatingAliases.isEmpty()) {
updatedAliasesName.putAll(updateAliasesStrategy(dialog, updatingAliases));
}
String msg = "Successfully updated aliases: " + updatedAliasesName.toString();
if (!hasResult(updatedAliasesName, SUCCESS)) {
msg = "Network System: " + network.getLabel() + ": No aliases were updated";
}
_log.info(msg);
result = getBiosCommandResult(updatedAliasesName);
} catch (Exception ex) {
_log.error("Cannot updated aliases: " + (ex.getCause() != null ? ex.getCause().getMessage() : ex.getLocalizedMessage()));
throw ex;
} finally {
disconnect(dialog);
}
return result;
}
/**
* Given a dialog, update one or more aliases from the network system.
*
* @param dialog - An MDSDialog, containing dialog state to the device
* @param updatingAliases - List of aliases to be updated.
* @return a map that contains the outcome for each alias keyed by alias name
* @throws ControllerException
*/
private Map<String, String> updateAliasesStrategy(MDSDialog dialog, List<ZoneWwnAliasUpdate> updatingAliases)
throws NetworkDeviceControllerException {
Long time = System.currentTimeMillis();
// a alias-name-to-result map to hold the results for each alias
Map<String, String> updatedAliasesName = new HashMap<String, String>();
try {
// Go into config mode. This allows us to change the configuration.
dialog.config();
dialog.deviceAliasConfig();
Map<String, String> aliasMap = dialog.showDeviceAliasDatabase();
for (ZoneWwnAliasUpdate updatingAlias : updatingAliases) {
String name = updatingAlias.getName();
String newName = updatingAlias.getNewName();
String oldWwn = updatingAlias.getAddress();
String newWwn = updatingAlias.getNewAddress();
_log.info("Starting update alias {}", name);
// get aliass wwn address from database
String currentWwn = aliasMap.get(name);
String updateStatus = NO_CHANGE;
try {
// if address not found in device, implied that alias is already removed or not
// exist in system, ignore it and continue on
if (StringUtils.isEmpty(newWwn) && StringUtils.isEmpty(newName)) {
_log.info("No new alias or WWN were specified. Nothing to update");
} else if (StringUtils.equals(name, newName) && !StringUtils.isEmpty(newWwn)
&& StringUtils.equalsIgnoreCase(oldWwn, newWwn)) {
_log.info("Old and new name {} are the same. Nothing to do.", name, currentWwn);
_log.info("Old and new WWN {} are the same. Nothing to do.", newWwn, currentWwn);
} else if (StringUtils.isEmpty(currentWwn)) {
_log.info("Alias {} was not found. Nothing to do", name);
} else if (StringUtils.isEmpty(newName) && StringUtils.equalsIgnoreCase(currentWwn, newWwn)) {
// new alias was not specified, implies wants to change wwn. But new is the same as in system, ignore
_log.info("The existing alias {} already has the desired WWN {}. Nothing to do.", name, currentWwn);
} else if (aliasMap.containsValue(newWwn)) {
// if new wwn is already assigned to another alias, throw exception
throw NetworkDeviceControllerException.exceptions.wwnAssignedToAnotherAlias(newWwn, name,
getAliasForWwn(aliasMap, newWwn));
} else if (!StringUtils.isEmpty(oldWwn) && !StringUtils.equalsIgnoreCase(oldWwn, currentWwn)) {
// if old wwn of alias and the provided updating address do not match in system, throw exception
throw NetworkDeviceControllerException.exceptions.aliasWithDifferentWwnExists(name, currentWwn, oldWwn);
} else if (!StringUtils.isEmpty(newName) && aliasMap.containsKey(newName)) {
// updating alias is already in the system, error
throw NetworkDeviceControllerException.exceptions.aliasAlreadyInNetworkSystem(newName, "");
} else if (!StringUtils.isEmpty(newName) || !StringUtils.isEmpty(newWwn)) {
// update wwn
boolean wwnUpdated = false;
if (!StringUtils.isEmpty(newWwn) &&
(StringUtils.isEmpty(oldWwn) || StringUtils.equalsIgnoreCase(oldWwn, currentWwn))) {
// if oldWwn is not specified, or it is specified and the same as
// the current alias' wwn, update it to newWwn.
_log.info("The existing alias {} 's WWN is updated to {}", name, newWwn);
// update by remove the matching old, then re-add the new wwn
dialog.deviceAliasName(name, currentWwn, true);
dialog.deviceAliasName(name, newWwn, false);
aliasMap.put(name, newWwn); // update map with new value
wwnUpdated = true;
}
// update alias
if (!StringUtils.isEmpty(newName) && !StringUtils.equals(name, newName)) {
// if newName is specified, and it is different from the old one, rename it
_log.info("Renaming alias {} to {}", name, newName);
// update by remove the matching old, then re-add the new wwn
try {
dialog.deviceAliasRename(name, newName);
} catch (Exception ex) {
if (wwnUpdated) {
// roll back
_log.info("Failed to rename. Rollback update wwn");
dialog.deviceAliasName(name, newWwn, true);
dialog.deviceAliasName(name, currentWwn, false);
aliasMap.put(name, currentWwn); // update map with new value
}
throw ex;
}
aliasMap.put(newName, aliasMap.get(name)); // update map with new value
aliasMap.remove(name);
}
updateStatus = SUCCESS;
}
} catch (Exception ex) {
_log.warn("Exception was encountered but will try the rest of the batch. " +
"Error message: " + ex.getMessage());
updateStatus = ERROR;
}
updatedAliasesName.put(name, updateStatus);
}
// if there was any alias added, commit them
if (!updatedAliasesName.isEmpty()) {
dialog.deviceAliasCommit();
dialog.copyRunningConfigToStartupFabric();
} else {
dialog.exitToConfig();
}
time = System.currentTimeMillis() - time;
_log.info("Aliases update time (msec): " + time.toString());
return updatedAliasesName;
} catch (Exception ex) {
dialog.deviceAliasAbort();
throw NetworkDeviceControllerException.exceptions.updateAliasesStrategyFailed(ex);
} finally {
dialog.endConfig();
}
}
private String getAliasForWwn(Map<String, String> aliasMap, String wwn) {
String foundAlias = null;
for (Entry<String, String> aliasEntry : aliasMap.entrySet()) {
if (StringUtils.equalsIgnoreCase(wwn, aliasEntry.getValue())) {
foundAlias = aliasEntry.getKey();
}
}
return foundAlias;
}
@Override
public Map<String, List<Zone>> getEndpointsZones(
NetworkSystem networkSystem, String fabricWwn,
String nativeId, Collection<String> endpointsWwn)
throws NetworkDeviceControllerException {
MDSDialog dialog = null;
Map<String, List<Zone>> zoneMap = new HashMap<String, List<Zone>>();
try {
Integer vsanId = Integer.valueOf(nativeId);
dialog = setUpDialog(networkSystem);
for (String endpointWwn : endpointsWwn) {
Collection<String> zoneNames = dialog.showZoneNamesForPwwn(endpointWwn, vsanId, true);
List<Zone> zones = dialog.showZones(zoneNames, true);
zoneMap.put(endpointWwn, zones);
}
return zoneMap;
} catch (Exception ex) {
_log.error("Cannot read zones from device: " + networkSystem.getLabel() + ": " + ex.getLocalizedMessage());
throw ex;
} finally {
disconnect(dialog);
}
}
@Override
public boolean isCapableOfRouting(NetworkSystem networkSystem) {
return this.isIvrEnabled(networkSystem);
}
}