package net.floodlightcontroller.topology;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.PriorityQueue;
import java.util.Set;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import net.floodlightcontroller.util.ClusterDFS;
import net.floodlightcontroller.core.annotations.LogMessageCategory;
import net.floodlightcontroller.core.annotations.LogMessageDoc;
import net.floodlightcontroller.routing.BroadcastTree;
import net.floodlightcontroller.routing.Link;
import net.floodlightcontroller.routing.Route;
import net.floodlightcontroller.routing.RouteId;
import net.floodlightcontroller.util.LRUHashMap;
/**
* A representation of a network topology. Used internally by
* {@link TopologyManager}
*/
@LogMessageCategory("Network Topology")
public class TopologyInstance {
public static final short LT_SH_LINK = 1;
public static final short LT_BD_LINK = 2;
public static final short LT_TUNNEL = 3;
public static final int MAX_LINK_WEIGHT = 10000;
public static final int MAX_PATH_WEIGHT = Integer.MAX_VALUE - MAX_LINK_WEIGHT - 1;
public static final int PATH_CACHE_SIZE = 1000;
protected static Logger log = LoggerFactory.getLogger(TopologyInstance.class);
protected Map<Long, Set<Short>> switchPorts; // Set of ports for each switch
/** Set of switch ports that are marked as blocked. A set of blocked
* switch ports may be provided at the time of instantiation. In addition,
* we may add additional ports to this set.
*/
protected Set<NodePortTuple> blockedPorts;
protected Map<NodePortTuple, Set<Link>> switchPortLinks; // Set of links organized by node port tuple
/** Set of links that are blocked. */
protected Set<Link> blockedLinks;
protected Set<Long> switches;
protected Set<NodePortTuple> broadcastDomainPorts;
protected Set<NodePortTuple> tunnelPorts;
protected Set<Cluster> clusters; // set of openflow domains
protected Map<Long, Cluster> switchClusterMap; // switch to OF domain map
// States for routing
protected Map<Long, BroadcastTree> destinationRootedTrees;
protected Map<Long, Set<NodePortTuple>> clusterBroadcastNodePorts;
protected Map<Long, BroadcastTree> clusterBroadcastTrees;
protected LRUHashMap<RouteId, Route> pathcache;
public TopologyInstance() {
this.switches = new HashSet<Long>();
this.switchPorts = new HashMap<Long, Set<Short>>();
this.switchPortLinks = new HashMap<NodePortTuple, Set<Link>>();
this.broadcastDomainPorts = new HashSet<NodePortTuple>();
this.tunnelPorts = new HashSet<NodePortTuple>();
this.blockedPorts = new HashSet<NodePortTuple>();
this.blockedLinks = new HashSet<Link>();
}
public TopologyInstance(Map<Long, Set<Short>> switchPorts,
Map<NodePortTuple, Set<Link>> switchPortLinks)
{
this.switches = new HashSet<Long>(switchPorts.keySet());
this.switchPorts = new HashMap<Long, Set<Short>>(switchPorts);
this.switchPortLinks = new HashMap<NodePortTuple,
Set<Link>>(switchPortLinks);
this.broadcastDomainPorts = new HashSet<NodePortTuple>();
this.tunnelPorts = new HashSet<NodePortTuple>();
this.blockedPorts = new HashSet<NodePortTuple>();
this.blockedLinks = new HashSet<Link>();
clusters = new HashSet<Cluster>();
switchClusterMap = new HashMap<Long, Cluster>();
}
public TopologyInstance(Map<Long, Set<Short>> switchPorts,
Set<NodePortTuple> blockedPorts,
Map<NodePortTuple, Set<Link>> switchPortLinks,
Set<NodePortTuple> broadcastDomainPorts,
Set<NodePortTuple> tunnelPorts){
// copy these structures
this.switches = new HashSet<Long>(switchPorts.keySet());
this.switchPorts = new HashMap<Long, Set<Short>>();
for(long sw: switchPorts.keySet()) {
this.switchPorts.put(sw, new HashSet<Short>(switchPorts.get(sw)));
}
this.blockedPorts = new HashSet<NodePortTuple>(blockedPorts);
this.switchPortLinks = new HashMap<NodePortTuple, Set<Link>>();
for(NodePortTuple npt: switchPortLinks.keySet()) {
this.switchPortLinks.put(npt,
new HashSet<Link>(switchPortLinks.get(npt)));
}
this.broadcastDomainPorts = new HashSet<NodePortTuple>(broadcastDomainPorts);
this.tunnelPorts = new HashSet<NodePortTuple>(tunnelPorts);
blockedLinks = new HashSet<Link>();
clusters = new HashSet<Cluster>();
switchClusterMap = new HashMap<Long, Cluster>();
destinationRootedTrees = new HashMap<Long, BroadcastTree>();
clusterBroadcastTrees = new HashMap<Long, BroadcastTree>();
clusterBroadcastNodePorts = new HashMap<Long, Set<NodePortTuple>>();
pathcache = new LRUHashMap<RouteId, Route>(PATH_CACHE_SIZE);
}
public void compute() {
// Step 1: Compute clusters ignoring broadcast domain links
// Create nodes for clusters in the higher level topology
// Must ignore blocked links.
identifyOpenflowDomains();
// Step 0: Remove all links connected to blocked ports.
// removeLinksOnBlockedPorts();
// Step 1.1: Add links to clusters
// Avoid adding blocked links to clusters
addLinksToOpenflowDomains();
// Step 2. Compute shortest path trees in each cluster for
// unicast routing. The trees are rooted at the destination.
// Cost for tunnel links and direct links are the same.
calculateShortestPathTreeInClusters();
// Step 3. Compute broadcast tree in each cluster.
// Cost for tunnel links are high to discourage use of
// tunnel links. The cost is set to the number of nodes
// in the cluster + 1, to use as minimum number of
// clusters as possible.
calculateBroadcastNodePortsInClusters();
// Step 4. print topology.
// printTopology();
}
public void printTopology() {
log.trace("-----------------------------------------------");
log.trace("Links: {}",this.switchPortLinks);
log.trace("broadcastDomainPorts: {}", broadcastDomainPorts);
log.trace("tunnelPorts: {}", tunnelPorts);
log.trace("clusters: {}", clusters);
log.trace("destinationRootedTrees: {}", destinationRootedTrees);
log.trace("clusterBroadcastNodePorts: {}", clusterBroadcastNodePorts);
log.trace("-----------------------------------------------");
}
protected void addLinksToOpenflowDomains() {
for(long s: switches) {
if (switchPorts.get(s) == null) continue;
for (short p: switchPorts.get(s)) {
NodePortTuple np = new NodePortTuple(s, p);
if (switchPortLinks.get(np) == null) continue;
if (isBroadcastDomainPort(np)) continue;
for(Link l: switchPortLinks.get(np)) {
if (isBlockedLink(l)) continue;
if (isBroadcastDomainLink(l)) continue;
Cluster c1 = switchClusterMap.get(l.getSrc());
Cluster c2 = switchClusterMap.get(l.getDst());
if (c1 ==c2) {
c1.addLink(l);
}
}
}
}
}
/**
* @author Srinivasan Ramasubramanian
*
* This function divides the network into clusters. Every cluster is
* a strongly connected component. The network may contain unidirectional
* links. The function calls dfsTraverse for performing depth first
* search and cluster formation.
*
* The computation of strongly connected components is based on
* Tarjan's algorithm. For more details, please see the Wikipedia
* link below.
*
* http://en.wikipedia.org/wiki/Tarjan%27s_strongly_connected_components_algorithm
*/
@LogMessageDoc(level="ERROR",
message="No DFS object for switch {} found.",
explanation="The internal state of the topology module is corrupt",
recommendation=LogMessageDoc.REPORT_CONTROLLER_BUG)
public void identifyOpenflowDomains() {
Map<Long, ClusterDFS> dfsList = new HashMap<Long, ClusterDFS>();
if (switches == null) return;
for (Long key: switches) {
ClusterDFS cdfs = new ClusterDFS();
dfsList.put(key, cdfs);
}
Set<Long> currSet = new HashSet<Long>();
for (Long sw: switches) {
ClusterDFS cdfs = dfsList.get(sw);
if (cdfs == null) {
log.error("No DFS object for switch {} found.", sw);
}else if (!cdfs.isVisited()) {
dfsTraverse(0, 1, sw, dfsList, currSet);
}
}
}
/**
* @author Srinivasan Ramasubramanian
*
* This algorithm computes the depth first search (DFS) traversal of the
* switches in the network, computes the lowpoint, and creates clusters
* (of strongly connected components).
*
* The computation of strongly connected components is based on
* Tarjan's algorithm. For more details, please see the Wikipedia
* link below.
*
* http://en.wikipedia.org/wiki/Tarjan%27s_strongly_connected_components_algorithm
*
* The initialization of lowpoint and the check condition for when a
* cluster should be formed is modified as we do not remove switches that
* are already part of a cluster.
*
* A return value of -1 indicates that dfsTraverse failed somewhere in the middle
* of computation. This could happen when a switch is removed during the cluster
* computation procedure.
*
* @param parentIndex: DFS index of the parent node
* @param currIndex: DFS index to be assigned to a newly visited node
* @param currSw: ID of the current switch
* @param dfsList: HashMap of DFS data structure for each switch
* @param currSet: Set of nodes in the current cluster in formation
* @return long: DSF index to be used when a new node is visited
*/
private long dfsTraverse (long parentIndex, long currIndex, long currSw,
Map<Long, ClusterDFS> dfsList, Set <Long> currSet) {
//Get the DFS object corresponding to the current switch
ClusterDFS currDFS = dfsList.get(currSw);
// Get all the links corresponding to this switch
//Assign the DFS object with right values.
currDFS.setVisited(true);
currDFS.setDfsIndex(currIndex);
currDFS.setParentDFSIndex(parentIndex);
currIndex++;
// Traverse the graph through every outgoing link.
if (switchPorts.get(currSw) != null){
for(Short p: switchPorts.get(currSw)) {
Set<Link> lset = switchPortLinks.get(new NodePortTuple(currSw, p));
if (lset == null) continue;
for(Link l:lset) {
long dstSw = l.getDst();
// ignore incoming links.
if (dstSw == currSw) continue;
// ignore if the destination is already added to
// another cluster
if (switchClusterMap.get(dstSw) != null) continue;
// ignore the link if it is blocked.
if (isBlockedLink(l)) continue;
// ignore this link if it is in broadcast domain
if (isBroadcastDomainLink(l)) continue;
// Get the DFS object corresponding to the dstSw
ClusterDFS dstDFS = dfsList.get(dstSw);
if (dstDFS.getDfsIndex() < currDFS.getDfsIndex()) {
// could be a potential lowpoint
if (dstDFS.getDfsIndex() < currDFS.getLowpoint())
currDFS.setLowpoint(dstDFS.getDfsIndex());
} else if (!dstDFS.isVisited()) {
// make a DFS visit
currIndex = dfsTraverse(currDFS.getDfsIndex(), currIndex, dstSw,
dfsList, currSet);
if (currIndex < 0) return -1;
// update lowpoint after the visit
if (dstDFS.getLowpoint() < currDFS.getLowpoint())
currDFS.setLowpoint(dstDFS.getLowpoint());
}
// else, it is a node already visited with a higher
// dfs index, just ignore.
}
}
}
// Add current node to currSet.
currSet.add(currSw);
// Cluster computation.
// If the node's lowpoint is greater than its parent's DFS index,
// we need to form a new cluster with all the switches in the
// currSet.
if (currDFS.getLowpoint() > currDFS.getParentDFSIndex()) {
// The cluster thus far forms a strongly connected component.
// create a new switch cluster and the switches in the current
// set to the switch cluster.
Cluster sc = new Cluster();
for(long sw: currSet){
sc.add(sw);
switchClusterMap.put(sw, sc);
}
// delete all the nodes in the current set.
currSet.clear();
// add the newly formed switch clusters to the cluster set.
clusters.add(sc);
}
return currIndex;
}
/**
* Go through every link and identify it is a blocked link or not.
* If blocked, remove it from the switchport links and put them in the
* blocked link category.
*
* Note that we do not update the tunnel ports and broadcast domain
* port structures. We need those to still answer the question if the
* ports are tunnel or broadcast domain ports.
*
* If we add additional ports to blocked ports later on, we may simply
* call this method again to remove the links on the newly blocked ports.
*/
protected void removeLinksOnBlockedPorts() {
Iterator<NodePortTuple> nptIter;
Iterator<Link> linkIter;
// Iterate through all the links and all the switch ports
// and move the links on blocked switch ports to blocked links
nptIter = this.switchPortLinks.keySet().iterator();
while (nptIter.hasNext()) {
NodePortTuple npt = nptIter.next();
linkIter = switchPortLinks.get(npt).iterator();
while (linkIter.hasNext()) {
Link link = linkIter.next();
if (isBlockedLink(link)) {
this.blockedLinks.add(link);
linkIter.remove();
}
}
// Note that at this point, the switchport may have
// no links in it. We could delete the switch port,
// but we will leave it as is.
}
}
public Set<NodePortTuple> getBlockedPorts() {
return this.blockedPorts;
}
protected Set<Link> getBlockedLinks() {
return this.blockedLinks;
}
/** Returns true if a link has either one of its switch ports
* blocked.
* @param l
* @return
*/
protected boolean isBlockedLink(Link l) {
NodePortTuple n1 = new NodePortTuple(l.getSrc(), l.getSrcPort());
NodePortTuple n2 = new NodePortTuple(l.getDst(), l.getDstPort());
return (isBlockedPort(n1) || isBlockedPort(n2));
}
protected boolean isBlockedPort(NodePortTuple npt) {
return blockedPorts.contains(npt);
}
protected boolean isTunnelPort(NodePortTuple npt) {
return tunnelPorts.contains(npt);
}
protected boolean isTunnelLink(Link l) {
NodePortTuple n1 = new NodePortTuple(l.getSrc(), l.getSrcPort());
NodePortTuple n2 = new NodePortTuple(l.getDst(), l.getDstPort());
return (isTunnelPort(n1) || isTunnelPort(n2));
}
public boolean isBroadcastDomainLink(Link l) {
NodePortTuple n1 = new NodePortTuple(l.getSrc(), l.getSrcPort());
NodePortTuple n2 = new NodePortTuple(l.getDst(), l.getDstPort());
return (isBroadcastDomainPort(n1) || isBroadcastDomainPort(n2));
}
public boolean isBroadcastDomainPort(NodePortTuple npt) {
return broadcastDomainPorts.contains(npt);
}
class NodeDist implements Comparable<NodeDist> {
private Long node;
public Long getNode() {
return node;
}
private int dist;
public int getDist() {
return dist;
}
public NodeDist(Long node, int dist) {
this.node = node;
this.dist = dist;
}
public int compareTo(NodeDist o) {
if (o.dist == this.dist) {
return (int)(o.node - this.node);
}
return o.dist - this.dist;
}
}
protected BroadcastTree dijkstra(Cluster c, Long root,
Map<Link, Integer> linkCost,
boolean isDstRooted) {
HashMap<Long, Link> nexthoplinks = new HashMap<Long, Link>();
//HashMap<Long, Long> nexthopnodes = new HashMap<Long, Long>();
HashMap<Long, Integer> cost = new HashMap<Long, Integer>();
int w;
for (Long node: c.links.keySet()) {
nexthoplinks.put(node, null);
//nexthopnodes.put(node, null);
cost.put(node, MAX_PATH_WEIGHT);
}
HashMap<Long, Boolean> seen = new HashMap<Long, Boolean>();
PriorityQueue<NodeDist> nodeq = new PriorityQueue<NodeDist>();
nodeq.add(new NodeDist(root, 0));
cost.put(root, 0);
while (nodeq.peek() != null) {
NodeDist n = nodeq.poll();
Long cnode = n.getNode();
int cdist = n.getDist();
if (cdist >= MAX_PATH_WEIGHT) break;
if (seen.containsKey(cnode)) continue;
seen.put(cnode, true);
for (Link link: c.links.get(cnode)) {
Long neighbor;
if (isDstRooted == true) neighbor = link.getSrc();
else neighbor = link.getDst();
// links directed toward cnode will result in this condition
// if (neighbor == cnode) continue;
if (linkCost == null || linkCost.get(link)==null) w = 1;
else w = linkCost.get(link);
int ndist = cdist + w; // the weight of the link, always 1 in current version of floodlight.
if (ndist < cost.get(neighbor)) {
cost.put(neighbor, ndist);
nexthoplinks.put(neighbor, link);
//nexthopnodes.put(neighbor, cnode);
nodeq.add(new NodeDist(neighbor, ndist));
}
}
}
BroadcastTree ret = new BroadcastTree(nexthoplinks, cost);
return ret;
}
protected void calculateShortestPathTreeInClusters() {
pathcache.clear();
destinationRootedTrees.clear();
Map<Link, Integer> linkCost = new HashMap<Link, Integer>();
int tunnel_weight = switchPorts.size() + 1;
for(NodePortTuple npt: tunnelPorts) {
if (switchPortLinks.get(npt) == null) continue;
for(Link link: switchPortLinks.get(npt)) {
if (link == null) continue;
linkCost.put(link, tunnel_weight);
}
}
for(Cluster c: clusters) {
for (Long node : c.links.keySet()) {
BroadcastTree tree = dijkstra(c, node, linkCost, true);
destinationRootedTrees.put(node, tree);
}
}
}
protected void calculateBroadcastTreeInClusters() {
for(Cluster c: clusters) {
// c.id is the smallest node that's in the cluster
BroadcastTree tree = destinationRootedTrees.get(c.id);
clusterBroadcastTrees.put(c.id, tree);
}
}
protected void calculateBroadcastNodePortsInClusters() {
clusterBroadcastTrees.clear();
calculateBroadcastTreeInClusters();
log.debug("openflow islands : " + clusters.size());
for(Cluster c: clusters) {
// c.id is the smallest node that's in the cluster
BroadcastTree tree = clusterBroadcastTrees.get(c.id);
log.info("Broadcast Tree {}", tree);
Set<NodePortTuple> nptSet = new HashSet<NodePortTuple>();
Map<Long, Link> links = tree.getLinks();
if (links == null) continue;
for(long nodeId: links.keySet()) {
Link l = links.get(nodeId);
if (l == null) continue;
NodePortTuple npt1 = new NodePortTuple(l.getSrc(), l.getSrcPort());
NodePortTuple npt2 = new NodePortTuple(l.getDst(), l.getDstPort());
nptSet.add(npt1);
nptSet.add(npt2);
}
clusterBroadcastNodePorts.put(c.id, nptSet);
}
}
protected Route buildroute(RouteId id, long srcId, long dstId) {
NodePortTuple npt;
LinkedList<NodePortTuple> switchPorts =
new LinkedList<NodePortTuple>();
if (destinationRootedTrees == null) return null;
if (destinationRootedTrees.get(dstId) == null) return null;
Map<Long, Link> nexthoplinks =
destinationRootedTrees.get(dstId).getLinks();
if (!switches.contains(srcId) || !switches.contains(dstId)) {
// This is a switch that is not connected to any other switch
// hence there was no update for links (and hence it is not
// in the network)
log.debug("buildroute: Standalone switch: {}", srcId);
// The only possible non-null path for this case is
// if srcId equals dstId --- and that too is an 'empty' path []
} else if ((nexthoplinks!=null) && (nexthoplinks.get(srcId)!=null)) {
while (srcId != dstId) {
Link l = nexthoplinks.get(srcId);
npt = new NodePortTuple(l.getSrc(), l.getSrcPort());
switchPorts.addLast(npt);
npt = new NodePortTuple(l.getDst(), l.getDstPort());
switchPorts.addLast(npt);
srcId = nexthoplinks.get(srcId).getDst();
}
}
// else, no path exists, and path equals null
Route result = null;
if (switchPorts != null && !switchPorts.isEmpty())
result = new Route(id, switchPorts);
if (log.isTraceEnabled()) {
log.trace("buildroute: {}", result);
}
return result;
}
protected int getCost(long srcId, long dstId) {
BroadcastTree bt = destinationRootedTrees.get(dstId);
if (bt == null) return -1;
return (bt.getCost(srcId));
}
/*
* Getter Functions
*/
protected Set<Cluster> getClusters() {
return clusters;
}
// IRoutingEngineService interfaces
protected boolean routeExists(long srcId, long dstId) {
BroadcastTree bt = destinationRootedTrees.get(dstId);
if (bt == null) return false;
Link link = bt.getLinks().get(srcId);
if (link == null) return false;
return true;
}
protected Route getRoute(long srcId, short srcPort,
long dstId, short dstPort) {
// Return null the route source and desitnation are the
// same switchports.
if (srcId == dstId && srcPort == dstPort)
return null;
List<NodePortTuple> nptList;
NodePortTuple npt;
Route r = getRoute(srcId, dstId);
if (r == null && srcId != dstId) return null;
if (r != null) {
nptList= new ArrayList<NodePortTuple>(r.getPath());
} else {
nptList = new ArrayList<NodePortTuple>();
}
npt = new NodePortTuple(srcId, srcPort);
nptList.add(0, npt); // add src port to the front
npt = new NodePortTuple(dstId, dstPort);
nptList.add(npt); // add dst port to the end
RouteId id = new RouteId(srcId, dstId);
r = new Route(id, nptList);
return r;
}
protected Route getRoute(long srcId, long dstId) {
RouteId id = new RouteId(srcId, dstId);
Route result = null;
if (pathcache.containsKey(id)) {
result = pathcache.get(id);
} else {
result = buildroute(id, srcId, dstId);
pathcache.put(id, result);
}
if (log.isTraceEnabled()) {
log.trace("getRoute: {} -> {}", id, result);
}
return result;
}
protected BroadcastTree getBroadcastTreeForCluster(long clusterId){
Cluster c = switchClusterMap.get(clusterId);
if (c == null) return null;
return clusterBroadcastTrees.get(c.id);
}
//
// ITopologyService interface method helpers.
//
protected boolean isInternalToOpenflowDomain(long switchid, short port) {
return !isAttachmentPointPort(switchid, port);
}
public boolean isAttachmentPointPort(long switchid, short port) {
NodePortTuple npt = new NodePortTuple(switchid, port);
if (switchPortLinks.containsKey(npt)) return false;
return true;
}
protected long getOpenflowDomainId(long switchId) {
Cluster c = switchClusterMap.get(switchId);
if (c == null) return switchId;
return c.getId();
}
protected long getL2DomainId(long switchId) {
return getOpenflowDomainId(switchId);
}
protected Set<Long> getSwitchesInOpenflowDomain(long switchId) {
Cluster c = switchClusterMap.get(switchId);
if (c == null) return null;
return (c.getNodes());
}
protected boolean inSameOpenflowDomain(long switch1, long switch2) {
Cluster c1 = switchClusterMap.get(switch1);
Cluster c2 = switchClusterMap.get(switch2);
if (c1 != null && c2 != null)
return (c1.getId() == c2.getId());
return (switch1 == switch2);
}
public boolean isAllowed(long sw, short portId) {
return true;
}
protected boolean
isIncomingBroadcastAllowedOnSwitchPort(long sw, short portId) {
if (isInternalToOpenflowDomain(sw, portId)) {
long clusterId = getOpenflowDomainId(sw);
NodePortTuple npt = new NodePortTuple(sw, portId);
if (clusterBroadcastNodePorts.get(clusterId).contains(npt))
return true;
else return false;
}
return true;
}
public boolean isConsistent(long oldSw, short oldPort, long newSw,
short newPort) {
if (isInternalToOpenflowDomain(newSw, newPort)) return true;
return (oldSw == newSw && oldPort == newPort);
}
protected Set<NodePortTuple>
getBroadcastNodePortsInCluster(long sw) {
long clusterId = getOpenflowDomainId(sw);
return clusterBroadcastNodePorts.get(clusterId);
}
public boolean inSameBroadcastDomain(long s1, short p1, long s2, short p2) {
return false;
}
public boolean inSameL2Domain(long switch1, long switch2) {
return inSameOpenflowDomain(switch1, switch2);
}
public NodePortTuple getOutgoingSwitchPort(long src, short srcPort,
long dst, short dstPort) {
// Use this function to redirect traffic if needed.
return new NodePortTuple(dst, dstPort);
}
public NodePortTuple getIncomingSwitchPort(long src, short srcPort,
long dst, short dstPort) {
// Use this function to reinject traffic from a different port if needed.
return new NodePortTuple(src, srcPort);
}
public Set<Long> getSwitches() {
return switches;
}
public Set<Short> getPortsWithLinks(long sw) {
return switchPorts.get(sw);
}
public Set<Short> getBroadcastPorts(long targetSw, long src, short srcPort) {
Set<Short> result = new HashSet<Short>();
long clusterId = getOpenflowDomainId(targetSw);
for(NodePortTuple npt: clusterBroadcastNodePorts.get(clusterId)) {
if (npt.getNodeId() == targetSw) {
result.add(npt.getPortId());
}
}
return result;
}
public NodePortTuple
getAllowedOutgoingBroadcastPort(long src, short srcPort, long dst,
short dstPort) {
// TODO Auto-generated method stub
return null;
}
public NodePortTuple
getAllowedIncomingBroadcastPort(long src, short srcPort) {
// TODO Auto-generated method stub
return null;
}
}