/* * Copyright (c) 2015 EMC Corporation * All Rights Reserved */ package com.emc.storageos.networkcontroller.impl; import java.io.IOException; import java.net.URI; import java.text.MessageFormat; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; 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.ResourceBundle; import java.util.Set; import com.emc.storageos.db.client.model.StringMap; 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.URIUtil; import com.emc.storageos.db.client.constraint.AlternateIdConstraint; 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.DiscoveredDataObject.Type; import com.emc.storageos.db.client.model.FCEndpoint; import com.emc.storageos.db.client.model.Network; import com.emc.storageos.db.client.model.NetworkSystem; import com.emc.storageos.db.client.model.StorageProtocol.Transport; import com.emc.storageos.db.client.model.StringSet; import com.emc.storageos.db.client.util.DataObjectUtils; import com.emc.storageos.db.client.util.NullColumnValueGetter; import com.emc.storageos.db.exceptions.DatabaseException; import com.emc.storageos.networkcontroller.exceptions.NetworkDeviceControllerException; import com.emc.storageos.services.OperationTypeEnum; import com.emc.storageos.util.NetworkUtil; import com.emc.storageos.util.VersionChecker; import com.emc.storageos.volumecontroller.ControllerException; import com.emc.storageos.volumecontroller.impl.ControllerServiceImpl; import com.emc.storageos.volumecontroller.impl.ControllerUtils; 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; /** * This class handles the discovery tasks for NetworkSystem. It uses an instances * of {@link NetworkSystemDevice} for device-specific communication. * */ public class NetworkDiscoveryWorker { private NetworkSystemDevice _device; private DbClient dbClient; private CoordinatorClient _coordinator; private static final String BUNDLE_NAME = "networkdevice"; //$NON-NLS-1$ private static final String EVENT_SERVICE_TYPE = "Network Discovery"; private static final String EVENT_SERVICE_SOURCE = "NetworkDiscoveryWorker"; // Properties controllering expiration of FCEndpoints // Here Awol means "Absent Without Leave", i.e. since we last saw it in a sample. private Integer _minAwolSamples = 3; private Long _minAwolTime = 60000L; private RecordableEventManager _evtMgr; private static final Logger _log = LoggerFactory .getLogger(NetworkDiscoveryWorker.class); public NetworkDiscoveryWorker() { try { ResourceBundle resourceBundle = ResourceBundle .getBundle(BUNDLE_NAME); _minAwolSamples = Integer.valueOf(resourceBundle.getString("FCEndpoint.minAwolSamples")); _minAwolTime = Long.valueOf(resourceBundle.getString("FCEndpoint.minAwolTime")); } catch (Exception ex) { _log.error("Failed to get the values for _minAwolSamples and _minAwolTime from resource bundle " + ex.getMessage()); } } public NetworkDiscoveryWorker(NetworkSystemDevice device, DbClient dbClient) { this._device = device; this.dbClient = dbClient; RecordableEventManager evtMgr = new RecordableEventManager(); evtMgr.setDbClient(dbClient); this._evtMgr = evtMgr; } /** * Verify the firmware version for the NetworkSystem * * @param uri - Device URI * @throws ControllerException thrown if firmware version is not supported */ public void verifyVersion(URI uri) throws ControllerException { // Retrieve the storage device info from the database. NetworkSystem networkDev = getDeviceObject(uri); NetworkSystemDevice networkDevice = getDevice(); if (networkDevice == null) { throw NetworkDeviceControllerException.exceptions.verifyVersionFailedNull(uri.toString()); } String version = null; try { version = networkDevice.getVersion(networkDev); networkDev.setVersion(version); String minimumSupportedVersion = VersionChecker.getMinimumSupportedVersion(Type.valueOf(networkDev.getSystemType())); _log.info("Verifying version details : Minimum Supported Version {} - Discovered Firmware Version {}", minimumSupportedVersion, version); if (VersionChecker.verifyVersionDetails(minimumSupportedVersion, version) < 0) { networkDev.setCompatibilityStatus(DiscoveredDataObject.CompatibilityStatus.INCOMPATIBLE.name()); throw NetworkDeviceControllerException.exceptions.versionNotSupported(version, minimumSupportedVersion); } else { networkDev.setCompatibilityStatus(DiscoveredDataObject.CompatibilityStatus.COMPATIBLE.name()); } } catch (Exception ex) { Date date = new Date(); networkDev.setLastDiscoveryStatusMessage(ex.getMessage()); throw NetworkDeviceControllerException.exceptions.verifyVersionFailed(uri.toString(), date.toString(), ex); } finally { if (networkDev != null) { try { dbClient.persistObject(networkDev); } catch (DatabaseException ex) { _log.error("Error while persisting object to DB", ex); } } } } /** * Update the network system physical inventory and creates/updates the discovered FC transport * zones as needed. The physical inventory is primarily the FCEndpoints (FCPortConnections), * which contains a record for each endpoint logged into the Fiber Channel Nameserver database. * The endpoints per fabric (vsan) constitute an FC transport zone which get created/updated * based on the FCEndpoints discovered. * * @param uri - Device URI */ public void updatePhysicalInventory(URI uri) throws ControllerException { // Retrieve the storage device info from the database. long start = System.currentTimeMillis(); NetworkSystem networkDev = getDeviceObject(uri); String msg = "unknown status"; NetworkSystemDevice networkDevice = getDevice(); if (networkDevice == null) { throw NetworkDeviceControllerException.exceptions.updatePhysicalInventoryFailedNull( uri.toString(), networkDev.getSystemType()); } try { // === Reconcile the FCEndpoints of this device === List<FCEndpoint> currentConnections = new ArrayList<FCEndpoint>(); // IN/OUT parameter to get the routed endpoints map - Fabric-WWN-to-endpoints-WWN Map<String, Set<String>> routedEndpoints = new HashMap<String, Set<String>>(); try { currentConnections = networkDevice.getPortConnections(networkDev, routedEndpoints); msg = MessageFormat.format("Retrieved {0} connections from device {1} at {2}", new Integer(currentConnections.size()), uri, new Date()); _log.info(msg); } catch (Exception e) { msg = MessageFormat.format("Discovery failed getting port connections for Network System : {0}", uri.toString()); throw (e); } try { reconcileFCEndpoints(networkDev, currentConnections); } catch (Exception e) { msg = MessageFormat.format("Discovery failed reconciling FC endpoints for Network System : {0}", uri.toString()); throw (e); } // ==== Reconcile the discovered transport zones ====== try { reconcileTransportZones(networkDev, routedEndpoints); } catch (Exception e) { msg = MessageFormat.format("Discovery failed reconciling networks for Network System : {0}", uri.toString()); throw (e); } try { networkDev.setUptime(networkDevice.getUptime(networkDev)); } catch (Exception e) { msg = MessageFormat.format("Discovery failed setting version/uptime for Network System : {0}", uri.toString()); throw (e); } // discovery succeeds msg = MessageFormat.format("Discovery completed successfully for Network System : {0}", uri.toString()); } catch (Exception ex) { Date date = new Date(); throw NetworkDeviceControllerException.exceptions.updatePhysicalInventoryFailedExc( uri.toString(), date.toString(), ex); } finally { if (networkDev != null) { try { // set detailed message networkDev.setLastDiscoveryStatusMessage(msg); dbClient.persistObject(networkDev); _log.info("Discovery took {}", (System.currentTimeMillis() - start)); } catch (DatabaseException ex) { _log.error("Error while persisting object to DB", ex); } } } } /** * Reconciles the current set of a Device's endpoints with what is persisted. * Updates the database accordingly. * * @param dev * @param currentConnections * @throws IOException */ private void reconcileFCEndpoints(NetworkSystem dev, List<FCEndpoint> currentConnections) throws IOException { // First, read all the existing connections from the device, and put them into a map // keyed by remote wwpn. URIQueryResultList uriList = new URIQueryResultList(); dbClient.queryByConstraint(ContainmentConstraint.Factory .getNetworkSystemFCPortConnectionConstraint(dev.getId()), uriList); Map<String, FCEndpoint> existingEndpoints = new HashMap<String, FCEndpoint>(); for (URI uriold : uriList) { FCEndpoint connection = dbClient.queryObject(FCEndpoint.class, uriold); if (connection != null) { existingEndpoints.put(connection.getRemotePortName().toUpperCase(), connection); } } // Now, scan the new endpoints, looking for added or updated records by // comparing them with the existing endpoints. Keep track of what was processed // so can do deletions on anything not seen in the currentConnections. List<FCEndpoint> updated = new ArrayList<FCEndpoint>(); List<FCEndpoint> created = new ArrayList<FCEndpoint>(); Set<String> processedWwpns = new HashSet<String>(); int conflictingEndpoints = 0; for (FCEndpoint current : currentConnections) { String key = current.getRemotePortName().toUpperCase(); processedWwpns.add(key); FCEndpoint existing = existingEndpoints.get(key); if (existing == null) { current.setNetworkDevice(dev.getId()); current.setId(URIUtil.createId(FCEndpoint.class)); created.add(current); conflictingEndpoints += removeConflictingEndpoints(key, current.getFabricWwn(), dev.getId()); } else { boolean modified = checkUpdated(existing, current); if (existing.getAwolCount() > 0) { modified = true; existing.setAwolCount(0); existing.setAwolTime(null); } if (modified) { updated.add(existing); conflictingEndpoints += removeConflictingEndpoints(key, current.getFabricWwn(), dev.getId()); } } } // Determine those to be deleted. Remove all the processed records from the existing set. // What was left were not seen this time. for (String key : processedWwpns) { existingEndpoints.remove(key); } // The remaining existingEndpoints can be processed for removal. // They are removed after a minimum number of samples and minimum amount of time has transpired. Integer removedCount = 0; for (FCEndpoint entry : existingEndpoints.values()) { int count = entry.getAwolCount(); if (count == 0) { entry.setAwolTime(System.currentTimeMillis()); } entry.setAwolCount(++count); if (count >= _minAwolSamples && (System.currentTimeMillis() - entry.getAwolTime()) > _minAwolTime) { removedCount++; dbClient.removeObject(entry); } else { updated.add(entry); // update counters } } // Persist created, modified. dbClient.createObject(created); dbClient.updateAndReindexObject(updated); _log.info(MessageFormat.format("{0} new connections persisted", created.size()).toString()); _log.info(MessageFormat.format("{0} updated connections persisted", updated.size()).toString()); _log.info(MessageFormat.format("{0} missing connections", existingEndpoints.values().size()).toString()); _log.info(MessageFormat.format("{0} removed connections", removedCount.toString())); _log.info(MessageFormat.format("{0} conflicting connections (removed)", conflictingEndpoints)); } /** * Check that a single pair of attributes changed. * * @param existing * @param current * @return true if changed */ private boolean checkAttributeChanged(String existing, String current) { if (existing == null && current == null) { return false; // Both null, no change } if ((existing == null) ^ (current == null)) { return true; // One null, a change } return (!existing.equalsIgnoreCase(current)); } /** * Returns true if any fields of significance has been modified. * * @param existing * @param current * @return false if no updates, true if updated */ private boolean checkUpdated(FCEndpoint existing, FCEndpoint current) { boolean updated = false; if (existing.getInactive() == true) { existing.setInactive(false); updated = true; } if (checkAttributeChanged(existing.getRemotePortName(), current.getRemotePortName())) { existing.setRemotePortName(current.getRemotePortName()); updated = true; } if (checkAttributeChanged(existing.getRemoteNodeName(), current.getRemoteNodeName())) { existing.setRemoteNodeName(current.getRemoteNodeName()); updated = true; } if (checkAttributeChanged(existing.getRemotePortAlias(), current.getRemotePortAlias())) { existing.setRemotePortAlias(current.getRemotePortAlias() == null ? "" : current.getRemotePortAlias()); updated = true; } if (checkAttributeChanged(existing.getFabricId(), current.getFabricId())) { existing.setFabricId(current.getFabricId()); updated = true; } if (checkAttributeChanged(existing.getFcid(), current.getFcid())) { existing.setFcid(current.getFcid()); updated = true; } if (checkAttributeChanged(existing.getSwitchInterface(), current.getSwitchInterface())) { existing.setSwitchInterface(current.getSwitchInterface()); updated = true; } if (checkAttributeChanged(existing.getSwitchName(), current.getSwitchName())) { existing.setSwitchName(current.getSwitchName()); updated = true; } if (checkAttributeChanged(existing.getSwitchPortName(), current.getSwitchPortName())) { existing.setSwitchPortName(current.getSwitchPortName()); updated = true; } if (checkAttributeChanged(existing.getFabricWwn(), current.getFabricWwn())) { existing.setFabricWwn(current.getFabricWwn()); updated = true; } return updated; } /** * Search all the remote FCEndpoints, remove those having the same RemotePortName * that are in a different fabric from a different device. * * @param remoteWwpn RemotePortName of new entry * @param fabricWwn FabricWwn of new entry * @param deviceId Device that found the updated connections * @return count of those removed * @throws IOException */ private int removeConflictingEndpoints(String remoteWwpn, String fabricWwn, URI deviceId) throws IOException { int removedCount = 0; URIQueryResultList uriList = new URIQueryResultList(); dbClient.queryByConstraint(AlternateIdConstraint.Factory. getFCEndpointRemotePortNameConstraint(remoteWwpn), uriList); for (URI uri : uriList) { FCEndpoint ep = dbClient.queryObject(FCEndpoint.class, uri); if (ep == null) { continue; } if (ep.getNetworkDevice().equals(deviceId)) { continue; } if (ep.getRemotePortName().equals(remoteWwpn) == false) { continue; } if (ep.getFabricWwn().equals(fabricWwn)) { continue; } dbClient.removeObject(ep); removedCount++; } return removedCount; } /** * Given the updated list of end points for one network system, this function will update * the transport zones. * Require lock when reconciles vsan in fabrics that are linked through ISL. Without locking, multiple VSANs * could have same native gui id within the same fabric. * * @param networkSystem the network system * @param routedEndpoints IN/OUT parameter to get the routed endpoints map of Fabric-WWN-to-endpoints-WWN * @throws ControllerException */ private void reconcileTransportZones(NetworkSystem networkSystem, Map<String, Set<String>> routedEndpoints) throws ControllerException { _log.info("reconcileTransportZones for networkSystem {}", networkSystem.getId()); ControllerServiceImpl.Lock lock = ControllerServiceImpl.Lock.getLock(ControllerServiceImpl.DISCOVERY_RECONCILE_TZ); try { _log.debug("Acquiring lock to reconcile transport zone for networkSystem {}", networkSystem.getId()); lock.acquire(); _log.info("Acquired lock to reconcile transport zone for networkSystem {}", networkSystem.getId()); // get the network system's connections from the database Iterator<FCEndpoint> iNewEndPoints = getNetworkSystemEndPoints(networkSystem); // get all the transport zones we have in the DB List<Network> oldTransportZones = getCurrentTransportZones(); _log.info("Found {} existing transport zones", oldTransportZones.size()); // get the fabrics that exist on the network system Map<String, String> fabricIdsMap = getDevice().getFabricIdsMap(networkSystem); // get the list of fabrics added, removed, changed TransportZoneReconciler reconciler = new TransportZoneReconciler(); TransportZoneReconciler.Results results = reconciler.reconcile(networkSystem, iNewEndPoints, fabricIdsMap, oldTransportZones); String networkSystemUri = networkSystem.getId().toString(); for (Network tzone : results.getRemoved()) { List<String> removedEps = removeNetworkSystemTransportZone(tzone, networkSystemUri); _log.info("Removed network {} which removed discovered endpoints {}", tzone.getNativeGuid(), removedEps); } for (Network tzone : results.getAdded()) { handleEndpointsAdded(tzone, tzone.retrieveEndpoints()); saveTransportZone(tzone, true); } for (Network tzone : results.getModified()) { if (results.getRemovedEndPoints().get(tzone) != null) { NetworkAssociationHelper .handleEndpointsRemoved(tzone, results.getRemovedEndPoints().get(tzone), dbClient, _coordinator); } if (results.getAddedEndPoints().get(tzone) != null) { handleEndpointsAdded(tzone, results.getAddedEndPoints().get(tzone)); } saveTransportZone(tzone, false); } // update routed networks for routed and modified networks updateRoutedNetworks(networkSystem, results.getAddedAndModified(), routedEndpoints); } catch (Exception ex) { throw NetworkDeviceControllerException.exceptions.reconcileTransportZonesFailedExc( new Date().toString(), ex); } finally { try { _log.debug("Releasing reconcile transport zone lock for networkSystem {}", networkSystem.getId()); lock.release(); _log.info("Released reconcile transport zone lock for networkSystem {}", networkSystem.getId()); } catch (Exception e) { _log.error("Failed to release Lock while reconcile transport zone for network {} -->{}", networkSystem.getId(), e.getMessage()); } } } /** * Looks in the topology view for endpoints that accessible by routing * * @param networkSystem the network system being refreshed * @param updatedNetworks the networks that require updating * @param routedEndpoints the routed endpoints map of Fabric-WWN-to-endpoints-WWN * @throws Exception */ private void updateRoutedNetworks(NetworkSystem networkSystem, List<Network> updatedNetworks, Map<String, Set<String>> routedEndpoints) throws Exception { // for each network, get the list of routed ports and locate them in other networks StringSet routedNetworks = null; Network routedNetwork = null; if (!this.getDevice().isCapableOfRouting(networkSystem)) { _log.info("NetworkSystem {} does not support routing across VSANs, skipping routed networks update/discovery", networkSystem.getLabel()); //Clear out the routedNetworks entries, if filled, for non-IVR Cisco switches. This can happen in upgrade scenarios. URIQueryResultList networkSystemNetworkUriList = new URIQueryResultList(); dbClient.queryByConstraint(ContainmentConstraint.Factory. getNetworkSystemNetworkConstraint(networkSystem.getId()), networkSystemNetworkUriList); for (URI networkSystemNetworkUri : networkSystemNetworkUriList) { Network networkSystemNetwork = dbClient.queryObject(Network.class, networkSystemNetworkUri); _log.info("Updating routedNetwork to null for {}", networkSystemNetwork.getLabel()); networkSystemNetwork.setRoutedNetworks(null); dbClient.updateObject(networkSystemNetwork); } //Update the connected varray assignments _log.info("Updating connected network and varray assignments"); for (Network network : DataObjectUtils.toMap(getCurrentTransportZones()).values()) { NetworkAssociationHelper.setNetworkConnectedVirtualArrays(network, false, dbClient); } return; } // get the current networks from the database Map<URI, Network> allNetworks = DataObjectUtils.toMap(getCurrentTransportZones()); for (Network network : updatedNetworks) { // if this network has any routed endpoints Set<String> netRoutedEndpoints = routedEndpoints.get(NetworkUtil.getNetworkWwn(network)); if (netRoutedEndpoints == null || netRoutedEndpoints.isEmpty()) { _log.debug("No routed endpoint in network {}", network.getNativeGuid()); network.setRoutedNetworks(null); } else { _log.info("Found {} routed endpoint in network {}", netRoutedEndpoints, network.getNativeGuid()); routedNetworks = new StringSet(); for (String endpoint : netRoutedEndpoints) { // find the source network of the routed endpoint routedNetwork = findNetworkForDiscoveredEndPoint(allNetworks.values(), endpoint, network); if (routedNetwork != null) { // it is possible we did not discover the source routedNetworks.add(routedNetwork.getId().toString()); } } network.setRoutedNetworks(routedNetworks); } dbClient.updateAndReindexObject(network); _log.info("Updated routed networks for {} to {}", network.getNativeGuid(), routedNetworks); } // clean up transit networks from any one-way associations. // Transit networks will show any endpoints routed thru them // which may cause one-way associations in the routedNetworks. // For example if network A has ep1 and B has ep2 and there is // a routed zone between A and B, the transit network C will // reports ep1 and ep2 but there is not actual routing between // C and A or C and B, so we want to remove these associations. for (URI id : allNetworks.keySet()) { Network net = allNetworks.get(id); boolean updated = false; if (net.getRoutedNetworks() != null) { routedNetworks = new StringSet(net.getRoutedNetworks()); // for each network this network is pointing to for (String strUri : net.getRoutedNetworks()) { // get the opposite network Network opNet = allNetworks.get(URI.create(strUri)); if (opNet != null // it is possible this network is getting removed - the next discovery cleans up && opNet.getRoutedNetworks() != null // check for null in case the other network routed eps are not yet visible && !opNet.getRoutedNetworks().contains(net.getId().toString())) { // if the opposite network is not seeing this // one // remove this association because the opposite network is does not have the matching association routedNetworks.remove(opNet.getId().toString()); updated = true; } } if (updated) { _log.info("Reconciled routed networks for {} to {}", net.getNativeGuid(), routedNetworks); net.setRoutedNetworks(routedNetworks); dbClient.updateAndReindexObject(net); } } } for (Network network : allNetworks.values()) { NetworkAssociationHelper.setNetworkConnectedVirtualArrays(network, false, dbClient); } /* * COP-23266, COP-20698: Fix the problem that ViPR could not create IVR zone between VSANs routed by * transit VSAN(network), the "routedNetwork" field of "Network" data object should be updated based * on the transit network(s) if there is. */ this.updateTransitRoutedNetworks(networkSystem); } /** * Update routed networks according to transit network(s) if there is. * * @param networkSystem * @throws Exception */ private void updateTransitRoutedNetworks(NetworkSystem networkSystem) throws Exception { // 1. Get transit networks list and continue only when there is. List<Network> allNetworks = getCurrentTransportZones(); Set<String> transitNetworks = this.getTransitNetworks(networkSystem, allNetworks); if (transitNetworks.isEmpty()) { _log.info("No transit network is found and return directly."); return; } // 2. For each transit network, find its connected network systems and update each network of each connected // network system by adding routed networks. for (String transitNetwork : transitNetworks) { Set<String> connectedNetworkSystems = getConnectedNetworkSystems(transitNetwork, allNetworks); for (String connectedNetworkSystem : connectedNetworkSystems) { NetworkSystem currentNetworkSystem = getDeviceObject(new URI(connectedNetworkSystem)); this.updateRoutedNetworksForTransitNetwork(currentNetworkSystem, connectedNetworkSystems, allNetworks); } } } /** * Get the transit network set if there is, otherwise return empty set. * * @param networkSystem The current discovering network system. * @param allNetworks All the existing networks list. * @return The transit networks list. * @throws Exception The "getFabricIdsMap" may throw exception. */ private Set<String> getTransitNetworks(NetworkSystem networkSystem, List<Network> allNetworks) throws Exception { Map<String, String> networkWwnIdMap = getDevice().getFabricIdsMap(networkSystem); _log.info("getTransitNetworks.networkWwnIdMap = {}", networkWwnIdMap); Set<String> transitNetworks = new HashSet<String>(); for (Entry<String, String> entry : networkWwnIdMap.entrySet()) { String currentNetworkId = entry.getValue(); String currentNetworkWwn = entry.getKey(); Network currentNetwork = getNetworkByNativeId(allNetworks, currentNetworkId); // How to determine it's a transit network: 1. More than one network system have the same network. if (currentNetwork != null && currentNetwork.getNetworkSystems() != null && currentNetwork.getNetworkSystems().size() > 1) { _log.info("Network id={} is a transit VSAN", currentNetworkId); transitNetworks.add(currentNetworkId); } else { _log.info("Network id={} is NOT a transit VSAN", currentNetworkId); } } return transitNetworks; } /** * Get the connected network systems which are connected by the given transit network. * * @param transitNetwork * @param allNetworks * @return */ private Set<String> getConnectedNetworkSystems(String transitNetwork, List<Network> allNetworks) { Set<String> connectedNetworkSystems = new HashSet<String>(); for (Network network : allNetworks) { if (network.getNetworkSystems() != null && transitNetwork.equals(network.getNativeId())) { for (String networkSystem : network.getNetworkSystems()) { connectedNetworkSystems.add(networkSystem); } } } return connectedNetworkSystems; } /** * Update all the networks "routedNetworks" field by adding the routed networks list according to the given transit * network, including both "localNetworks" and "remoteRoutedNetworks". * * @param networkSystem * @param connectedNetworkSystems * @param allNetworks */ private void updateRoutedNetworksForTransitNetwork(NetworkSystem networkSystem, Set<String> connectedNetworkSystems, List<Network> allNetworks) { // 1. Get localNetworks, remoteRoutedNetworks and routedNetworks. With transit network, both the local // networks and the remote routed networks are routed. List<Network> localNetworks = getLocalNetworks(networkSystem, allNetworks); for (Network network : localNetworks) { dumpRoutedNetworks("localNetwork = ", network); } List<Network> remoteRoutedNetworks = this.getRemoteRoutedNetworks(networkSystem.getId().toString(), connectedNetworkSystems, allNetworks); for (Network network : remoteRoutedNetworks) { dumpRoutedNetworks("remoteRoutedNetworks = ", network); } List<Network> routedNetworks = new ArrayList<Network>(localNetworks); routedNetworks.addAll(remoteRoutedNetworks); for (Network network : routedNetworks) { dumpRoutedNetworks("routedNetworks = ", network); } // 2. Update each local network by setting the routed networks. for (Network currentNetwork : localNetworks) { boolean modified = false; StringSet networkSet = currentNetwork.getRoutedNetworks(); _log.info("NetworkDiscoveryWorker handling routed network, existing: {}", networkSet); if (networkSet == null) { networkSet = new StringSet(); } for (Network routedNetwork : routedNetworks) { if (!networkSet.contains(routedNetwork.getId().toString()) && !routedNetwork.getId().equals(currentNetwork.getId())) { networkSet.add(routedNetwork.getId().toString()); modified = true; } } _log.info("NetworkDiscoveryWorker handling routed network, updated: {}", networkSet); if (modified) { currentNetwork.setRoutedNetworks(networkSet); dumpRoutedNetworks("update network=", currentNetwork); dbClient.updateObject(currentNetwork); } } } /** * Generate logging output for given network details. * * @param prefix * @param network */ private void dumpRoutedNetworks(String prefix, Network network) { StringBuffer sb = new StringBuffer(); sb.append(prefix + ":"); sb.append("label = " + network.getLabel() + ", "); if (network.getRoutedNetworks() != null) { for (String str : network.getRoutedNetworks()) { sb.append(", routed = " + str); } } else { sb.append(", routed = null"); } _log.info(sb.toString()); } /** * Get the remote routed networks of the given network system. * * @param currentNetworkSystemId The current network system ID. * @param connectedNetworkSystems The connected network systems of the given transit network. * @param allNetworks All networks list. * @return remote routed networks list. */ private List<Network> getRemoteRoutedNetworks(String currentNetworkSystemId, Set<String> connectedNetworkSystems, List<Network> allNetworks) { List<Network> remoteRoutedNetworks = new ArrayList<Network>(); // 2. Find the remote routed networks of the current network system. for (Network network : allNetworks) { if (isRemoteRoutedNetwork(network, currentNetworkSystemId, connectedNetworkSystems)) { remoteRoutedNetworks.add(network); } } return remoteRoutedNetworks; } /** * Check if the given network is a remote routed network of the current network system. If it belongs to * a connected network system which is not the current network system, it is a remote routed network. * * @param network The given network. * @param currentNetworkSystemId The current network system ID. * @param connectedNetworkSystems The connected network systems list. * @return true/false */ private boolean isRemoteRoutedNetwork(Network network, String currentNetworkSystemId, Set<String> connectedNetworkSystems) { if (network.getNetworkSystems() != null) { for (String networkSystem : network.getNetworkSystems()) { if (networkSystem != currentNetworkSystemId && connectedNetworkSystems.contains(networkSystem)) { return true; } } } return false; } /** * Get all the networks belongs to the given network system. * * @param networkSystem * @param allNetworks * @return */ private List<Network> getLocalNetworks(NetworkSystem networkSystem, List<Network> allNetworks) { List<Network> realNetworks = new ArrayList<Network>(); for (Network network : allNetworks) { if (network.getNetworkSystems() != null && network.getNetworkSystems().contains(networkSystem.getId().toString())) { realNetworks.add(network); } } return realNetworks; } /** * Get the network by the given fabric ID. Return null if not found. * * @param networks * @param fabricId * @return */ private Network getNetworkByNativeId(List<Network> networks, String fabricId) { for (Network network : networks) { if (network != null && network.getNativeId() != null && network.getNativeId().equals(fabricId)) { return network; } } return null; } /** * Finds the network in the list that has this endpoint. * * @param networks a list of networks * @param endpoint the endpoint * @parame excludeNetwork - exclude this network from result if provided * @return the network that contains the endpoint if found, otherwise null. */ private Network findNetworkForDiscoveredEndPoint(Collection<Network> networks, String endpoint, Network excludeNetwork) { for (Network network : networks) { /* * if excludeNetwork not provided, look for first one. * Otherwise, ignore the provided network */ if (excludeNetwork == null || !network.getId().equals(excludeNetwork.getId())) { if (network.endpointIsDiscovered(endpoint)) { return network; } } } return null; } private void handleEndpointsAdded(Network tzone, Collection<String> endpoints) throws IOException { // find if the endpoints exit in some old transport zone Map<String, Network> transportZoneMap = NetworkAssociationHelper.getNetworksMap(endpoints, dbClient); if (!transportZoneMap.isEmpty()) { _log.info("Added endpoints {} to transport zone {}", endpoints.toArray(), tzone.getLabel()); // before we add the endpoints, they need to be removed from their old transport zones NetworkAssociationHelper.handleRemoveFromOldNetworks(transportZoneMap, tzone, dbClient, _coordinator); } // now, add the the endpoints NetworkAssociationHelper.handleEndpointsAdded(tzone, endpoints, dbClient, _coordinator); } private Iterator<FCEndpoint> getNetworkSystemEndPoints(NetworkSystem networkSystem) throws IOException { URIQueryResultList uriList = new URIQueryResultList(); dbClient.queryByConstraint(ContainmentConstraint.Factory .getNetworkSystemFCPortConnectionConstraint(networkSystem.getId()), uriList); List<URI> uris = new ArrayList<URI>(); while (uriList.iterator().hasNext()) { uris.add(uriList.iterator().next()); } return dbClient.queryIterativeObjects(FCEndpoint.class, uris); } /** * Get all the current networks in the database */ private List<Network> getCurrentTransportZones() throws Exception { List<Network> tzones = new ArrayList<Network>(); List<URI> uriTransportList = dbClient.queryByType(Network.class, true); Iterator<Network> iTZones = dbClient.queryIterativeObjects(Network.class, uriTransportList); while (iTZones.hasNext()) { Network transportZone = iTZones.next(); if (transportZone != null && Transport.FC.toString().equals(transportZone.getTransportType())) { tzones.add(transportZone); } } return tzones; } /** * Remove the transport zone for a given network system. This typically means * to dis-associated it unless this is the last network system associated with the * transport zone. In this case, the transport zone will be deleted if: * <ul> * <li>It was discovered</li> * <li>It does not have any user-created ports</li> * <li>It does not have any registered ports</li> * </ul> * * @param tzone * @param uri * @throws IOException */ public List<String> removeNetworkSystemTransportZone(Network tzone, String uri) throws IOException { tzone.removeNetworkSystems(Collections.singletonList(uri)); // dis-associate // list of end points getting deleted ArrayList<String> toRemove = new ArrayList<String>(); if (tzone.getNetworkSystems().isEmpty()) { // if this is the last network system List<String> userCreatedEndPoints = TransportZoneReconciler.getUserCreatedEndPoints(tzone); if (userCreatedEndPoints.isEmpty() && !tzone.assignedToVarray()) { // delete only if not modified by a user _log.info("Removing network {}", tzone.getLabel()); toRemove.addAll(tzone.retrieveEndpoints()); NetworkAssociationHelper.handleEndpointsRemoved(tzone, toRemove, dbClient, _coordinator); dbClient.markForDeletion(tzone); recordTransportZoneEvent(tzone, OperationTypeEnum.DELETE_NETWORK.getEvType(true), OperationTypeEnum.DELETE_NETWORK.getDescription()); } else { _log.info("Network {} is changed by the user and will " + "not be removed. Discovered end points will be removed.", tzone.getLabel()); for (String pt : tzone.retrieveEndpoints()) { if (!userCreatedEndPoints.contains(pt)) { toRemove.add(pt); } } tzone.removeEndpoints(toRemove); NetworkAssociationHelper.handleEndpointsRemoved(tzone, toRemove, dbClient, _coordinator); _log.info("Discovered endpoints removed {}", toRemove.toArray()); dbClient.persistObject(tzone); recordTransportZoneEvent(tzone, OperationTypeEnum.UPDATE_NETWORK.getEvType(true), OperationTypeEnum.UPDATE_NETWORK.getDescription()); } } else { _log.info("Removing network {} from network system {}", tzone.getLabel(), uri); dbClient.persistObject(tzone); recordTransportZoneEvent(tzone, OperationTypeEnum.UPDATE_NETWORK.getEvType(true), OperationTypeEnum.UPDATE_NETWORK.getDescription()); } return toRemove; } private void saveTransportZone(Network network, boolean newTransportZone) throws IOException { if (newTransportZone) { dbClient.createObject(network); _log.info("Added networks {}", network.getLabel()); recordTransportZoneEvent(network, OperationTypeEnum.CREATE_NETWORK.getEvType(true), OperationTypeEnum.CREATE_NETWORK.getDescription()); } else { dbClient.updateAndReindexObject(network); _log.info("Updated transport zone {}", network.getLabel()); recordTransportZoneEvent(network, OperationTypeEnum.UPDATE_NETWORK.getEvType(true), OperationTypeEnum.UPDATE_NETWORK.getDescription()); } } /** * 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.getDeviceObjectFailed( network.toString(), e); } // Verify non-null network device returned from the database client. if (networkDev == null) { throw NetworkDeviceControllerException.exceptions.getDeviceObjectFailedNull( network.toString()); } return networkDev; } private NetworkSystemDevice getDevice() { return _device; } public void setDevice(NetworkSystemDevice _device) { this._device = _device; } public void setDbClient(DbClient dbClient) { this.dbClient = dbClient; } public void setCoordinator(CoordinatorClient coordinator) { _coordinator = coordinator; } /** * Create a nice event based on the TransportZone * * @param tz Network for which the event is about * @param type Type of event such as updated, created, removed * @param description Description for the event if needed */ private void recordTransportZoneEvent(Network tz, String type, String description) { if (tz == null) { _log.error("Invalid Network event"); return; } // TODO fix the bogus user ID once we have AuthZ working RecordableBourneEvent event = ControllerUtils.convertToRecordableBourneEvent(tz, type, description, null, dbClient, EVENT_SERVICE_TYPE, RecordType.Event.name(), EVENT_SERVICE_SOURCE); try { _evtMgr.recordEvents(event); } catch (Exception ex) { _log.error("Failed to record event. Event description: {}. Error: {}.", description, ex); } } }