package net.onrc.onos.apps.segmentrouting; import java.util.ArrayList; import java.util.HashMap; import java.util.LinkedList; import net.onrc.onos.core.intent.Path; import net.onrc.onos.core.topology.Link; import net.onrc.onos.core.topology.LinkData; import net.onrc.onos.core.topology.Switch; import net.onrc.onos.core.util.Dpid; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * This class creates bandwidth constrained breadth first tree and returns paths * from root switch to leaf switches which satisfies the bandwidth condition. If * bandwidth parameter is not specified, the normal breadth first tree will be * calculated. The paths are snapshot paths at the point of the class * instantiation. */ public class ECMPShortestPathGraph { LinkedList<Switch> switchQueue = new LinkedList<>(); LinkedList<Integer> distanceQueue = new LinkedList<>(); HashMap<Dpid, Integer> switchSearched = new HashMap<>(); HashMap<Dpid, ArrayList<LinkData>> upstreamLinks = new HashMap<>(); HashMap<Dpid, ArrayList<Path>> paths = new HashMap<>(); HashMap<Integer, ArrayList<Switch>> distanceSwitchMap = new HashMap<>(); Switch rootSwitch; private static final Logger log = LoggerFactory .getLogger(SegmentRoutingManager.class); /** * Constructor * * @param rootSwitch root of the BFS tree */ public ECMPShortestPathGraph(Switch rootSwitch) { this.rootSwitch = rootSwitch; calcECMPShortestPathGraph(); } /** * Calculates the BFS tree using any provided constraints and Intents. */ private void calcECMPShortestPathGraph() { switchQueue.add(rootSwitch); int currDistance = 0; distanceQueue.add(currDistance); switchSearched.put(rootSwitch.getDpid(), currDistance); while (!switchQueue.isEmpty()) { Switch sw = switchQueue.poll(); Switch prevSw = null; currDistance = distanceQueue.poll(); for (Link link : sw.getOutgoingLinks()) { Switch reachedSwitch = link.getDstPort().getSwitch(); if ((prevSw != null) && (prevSw.getDpid().equals(reachedSwitch.getDpid()))) { /* Ignore LAG links between the same set of switches */ continue; } else { prevSw = reachedSwitch; } Integer distance = switchSearched.get(reachedSwitch.getDpid()); if ((distance != null) && (distance.intValue() < (currDistance + 1))) { continue; } if (distance == null) { /* First time visiting this switch node */ switchQueue.add(reachedSwitch); distanceQueue.add(currDistance + 1); switchSearched.put(reachedSwitch.getDpid(), currDistance + 1); ArrayList<Switch> distanceSwArray = distanceSwitchMap .get(currDistance + 1); if (distanceSwArray == null) { distanceSwArray = new ArrayList<Switch>(); distanceSwArray.add(reachedSwitch); distanceSwitchMap.put(currDistance + 1, distanceSwArray); } else distanceSwArray.add(reachedSwitch); } ArrayList<LinkData> upstreamLinkArray = upstreamLinks.get(reachedSwitch.getDpid()); if (upstreamLinkArray == null) { upstreamLinkArray = new ArrayList<LinkData>(); upstreamLinkArray.add(new LinkData(link)); upstreamLinks.put(reachedSwitch.getDpid(), upstreamLinkArray); } else /* ECMP links */ upstreamLinkArray.add(new LinkData(link)); } } /* log.debug("ECMPShortestPathGraph:switchSearched for switch {} is {}", HexString.toHexString(rootSwitch.getDpid().value()), switchSearched); log.debug("ECMPShortestPathGraph:upstreamLinks for switch {} is {}", HexString.toHexString(rootSwitch.getDpid().value()), upstreamLinks); log.debug("ECMPShortestPathGraph:distanceSwitchMap for switch {} is {}", HexString.toHexString(rootSwitch.getDpid().value()), distanceSwitchMap); */ /* for (Integer distance: distanceSwitchMap.keySet()){ for (Switch sw: distanceSwitchMap.get(distance)){ ArrayList<Path> path = getPath(sw); log.debug("ECMPShortestPathGraph:Paths in Pass{} from switch {} to switch {} is {}", distance, HexString.toHexString(rootSwitch.getDpid().value()), HexString.toHexString(sw.getDpid().value()), path); } }*/ } private void getDFSPaths(Dpid dstSwitchDpid, Path path, ArrayList<Path> paths) { Dpid rootSwitchDpid = rootSwitch.getDpid(); for (LinkData upstreamLink : upstreamLinks.get(dstSwitchDpid)) { /* Deep clone the path object */ Path sofarPath = new Path(); if (!path.isEmpty()) sofarPath.addAll(path.subList(0, path.size())); sofarPath.add(upstreamLink); if (upstreamLink.getSrc().getDpid().equals(rootSwitchDpid)) { paths.add(sofarPath); return; } else getDFSPaths(upstreamLink.getSrc().getDpid(), sofarPath, paths); } } /** * Return root switch for the graph * * @return root switch */ public Switch getRootSwitch() { return rootSwitch; } /** * Return the computed ECMP paths from the root switch to a given switch in * the network * * @param targetSwitch the target switch * @return the list of ECMP Paths from the root switch to the target switch */ public ArrayList<Path> getECMPPaths(Switch targetSwitch) { ArrayList<Path> pathArray = paths.get(targetSwitch.getDpid()); if (pathArray == null && switchSearched.containsKey( targetSwitch.getDpid())) { pathArray = new ArrayList<>(); Path path = new Path(); Dpid sw = targetSwitch.getDpid(); getDFSPaths(sw, path, pathArray); paths.put(targetSwitch.getDpid(), pathArray); } return pathArray; } /** * Return the complete info of the computed ECMP paths for each switch * learned in multiple iterations from the root switch * * @return the hash table of Switches learned in multiple Dijkstra * iterations and corresponding ECMP paths to it from the root * switch */ public HashMap<Integer, HashMap<Switch, ArrayList<Path>>> getCompleteLearnedSwitchesAndPaths() { HashMap<Integer, HashMap<Switch, ArrayList<Path>>> pathGraph = new HashMap<Integer, HashMap<Switch, ArrayList<Path>>>(); for (Integer itrIndx : distanceSwitchMap.keySet()) { HashMap<Switch, ArrayList<Path>> swMap = new HashMap<Switch, ArrayList<Path>>(); for (Switch sw : distanceSwitchMap.get(itrIndx)) { swMap.put(sw, getECMPPaths(sw)); } pathGraph.put(itrIndx, swMap); } return pathGraph; } /** * Return the complete info of the computed ECMP paths for each switch * learned in multiple iterations from the root switch in the form of { * Iteration1, Switch<> via {Switch<>, Switch<>} Switch<> via {Switch<>, * Switch<>} Iteration2, Switch<> via {Switch<>, Switch<>, Switch<>} * {Switch<>, Switch<>, Switch<>} Switch<> via {Switch<>, Switch<>, * Switch<>} } * * @return the hash table of Switches learned in multiple Dijkstra * iterations and corresponding ECMP paths in terms of Switches to * be traversed to it from the root switch */ public HashMap<Integer, HashMap<Switch, ArrayList<ArrayList<Dpid>>>> getAllLearnedSwitchesAndVia() { HashMap<Integer, HashMap<Switch, ArrayList<ArrayList<Dpid>>>> switchViaMap = new HashMap<Integer, HashMap<Switch, ArrayList<ArrayList<Dpid>>>>(); for (Integer itrIndx : distanceSwitchMap.keySet()) { HashMap<Switch, ArrayList<ArrayList<Dpid>>> swMap = new HashMap<Switch, ArrayList<ArrayList<Dpid>>>(); for (Switch sw : distanceSwitchMap.get(itrIndx)) { ArrayList<ArrayList<Dpid>> swViaArray = new ArrayList<>(); for (Path path : getECMPPaths(sw)) { ArrayList<Dpid> swVia = new ArrayList<>(); for (LinkData link : path.subList(0, path.size())) { if (link.getSrc().getDpid().equals(rootSwitch.getDpid())) { /* No need to add the root switch again in * the Via list */ continue; } swVia.add(link.getSrc().getDpid()); } swViaArray.add(swVia); } swMap.put(sw, swViaArray); } switchViaMap.put(itrIndx, swMap); } return switchViaMap; } }