import org.opendaylight.controller.sal.core.IContainer;
import org.opendaylight.controller.sal.core.Node;
+import org.opendaylight.controller.sal.core.Edge;
import org.opendaylight.controller.sal.core.Host;
import org.opendaylight.controller.sal.core.NodeConnector;
import org.opendaylight.controller.sal.core.NodeTable;
import org.opendaylight.affinity.affinity.IAffinityManager;
import org.opendaylight.affinity.affinity.AffinityAttributeType;
import org.opendaylight.affinity.affinity.AffinityAttribute;
+import org.opendaylight.affinity.affinity.AffinityPath;
import org.opendaylight.affinity.affinity.SetDeny;
import org.opendaylight.affinity.affinity.SetPathIsolate;
import org.opendaylight.affinity.affinity.SetPathRedirect;
}
public void setRouting(IRouting routing) {
this.routing = routing;
+ // Init max throughput edge weights
+ this.routing.initMaxThroughput(null);
}
public void unsetRouting(IRouting routing) {
// Get all flow groups and attribs from the affinity manager.
this.allfgroups = am.getAllFlowGroups();
this.attribs = am.getAllAttributes();
+
+
+ // New implementation using AffintyPath.
+ HashMap<Flow, AffinityPath> flowpaths = calcAffinityPathsForAllFlows(mergeAffinityAttributesPerFlow());
+ for (Flow f: flowpaths.keySet()) {
+ HashMap<Node, List<Action>> flowActions = calcForwardingActions(flowpaths.get(f));
+ InetAddress srcIp = (InetAddress) f.getMatch().getField(MatchType.NW_SRC).getValue();
+ InetAddress dstIp = (InetAddress) f.getMatch().getField(MatchType.NW_DST).getValue();
+ printActionMap(srcIp, dstIp, flowActions);
+ for (Node n: flowActions.keySet()) {
+ programFlows(n, f, flowActions.get(n));
+ }
+ }
+ /** Old implementation that does per-node programming. Demo-only.
for (Node node: this.nodelist) {
programFlowGroupsOnNode(this.allfgroups, this.attribs, node);
}
+ */
return true;
}
+
+ public void programFlows(Node n, Flow f, List<Action> actions) {
+
+ // Update flow with actions.
+ if (actions.size() > 0) {
+ log.info("Adding actions {} to flow {}", actions, f);
+ f.setActions(actions);
+ // Make a flowEntry object. groupName is the policy name,
+ // from the affinity link name. Same for all flows in this bundle.
+ InetAddress srcIp = (InetAddress) f.getMatch().getField(MatchType.NW_SRC).getValue();
+ InetAddress dstIp = (InetAddress) f.getMatch().getField(MatchType.NW_DST).getValue();
+ String flowName = "[" + srcIp + "->" + dstIp + "]";
+
+ FlowEntry fEntry = new FlowEntry("affinity", flowName, f, n);
+ log.info("Install flow entry {} on node {}", fEntry.toString(), n.toString());
+ installFlowEntry(fEntry);
+ }
+ }
+
+ public HashMap<Node, List<Action>> mergeActions(HashMap<Node, List<Action>> a, HashMap<Node, List<Action>> b) {
+ HashMap<Node, List<Action>> result = new HashMap<Node, List<Action>>();
+
+ if (a == null) {
+ return b;
+ }
+ if (b == null) {
+ return a;
+ }
+ // Initialize with all elements of a.
+ result = a;
+ // Add elements from b, merging when necessary.
+ ArrayList<Action> allActions = new ArrayList<Action>();
+ for (Node n: b.keySet()) {
+ // This node is listed in both maps, merge the actions.
+ if (a.get(n) != null) {
+ allActions.addAll(a.get(n));
+ allActions.addAll(b.get(n));
+ result.put(n, allActions);
+ }
+ }
+ return result;
+ }
+
+ // Merge all affinity links into a single result. This result is a
+ // collection that maps Flow (src-dst pair) -> combined set of all
+ // attribs applied to that src-dst pair.
+ public HashMap<Flow, HashMap<AffinityAttributeType, AffinityAttribute>> mergeAffinityAttributesPerFlow() {
+ // per-flow attributes
+ HashMap<Flow, HashMap<AffinityAttributeType, AffinityAttribute>> pfa = new HashMap<Flow, HashMap<AffinityAttributeType, AffinityAttribute>>();
+
+ for (String linkname: this.allfgroups.keySet()) {
+ log.debug("Adding new affinity link", linkname);
+ List<Flow> newflows = this.allfgroups.get(linkname);
+ HashMap<AffinityAttributeType, AffinityAttribute> newattribs = this.attribs.get(linkname);
+
+ for (Flow f: newflows) {
+ if (!pfa.containsKey(f)) {
+ // Create the initial record for this flow (src-dst pair).
+ pfa.put(f, newattribs);
+ } else {
+ // Merge attribs to the key that already exists.
+ pfa.put(f, merge(pfa.get(f), newattribs));
+ }
+ }
+ }
+ return pfa;
+ }
+
+ // tbd: This attribute map should become a class.
+ // Overwriting merge of two atribute HashMaps.
+ public HashMap<AffinityAttributeType, AffinityAttribute> merge(HashMap<AffinityAttributeType, AffinityAttribute> a,
+ HashMap<AffinityAttributeType, AffinityAttribute> b) {
+ HashMap<AffinityAttributeType, AffinityAttribute> result = new HashMap<AffinityAttributeType, AffinityAttribute>();
+
+ for (AffinityAttributeType at: a.keySet()) {
+ result.put(at, a.get(at));
+ }
+ for (AffinityAttributeType at: b.keySet()) {
+ result.put(at, b.get(at));
+ }
+ return result;
+ }
+
+ // A "Flow" here is used to represent the source-destination pair.
+ // Function returns an affinity path object per src-dest pair.
+ public HashMap<Flow, AffinityPath> calcAffinityPathsForAllFlows(HashMap<Flow, HashMap<AffinityAttributeType, AffinityAttribute>>perFlowAttribs) {
+ HashMap<Flow, AffinityPath> perFlowPaths = new HashMap<Flow, AffinityPath>();
+
+ for (Flow f: perFlowAttribs.keySet()) {
+ InetAddress srcIp = (InetAddress) f.getMatch().getField(MatchType.NW_SRC).getValue();
+ InetAddress dstIp = (InetAddress) f.getMatch().getField(MatchType.NW_DST).getValue();
+ String flowName = "[" + srcIp + "->" + dstIp + "]";
+ AffinityPath ap = calcAffinityPath(srcIp, dstIp, perFlowAttribs.get(f));
+ perFlowPaths.put(f, ap);
+ }
+ return perFlowPaths;
+ }
+
+ // xxx Compute the set of output actions for each node in this AffinityPath.
+ public HashMap<Node, List<Action>> calcForwardingActions(AffinityPath ap) {
+
+ HashMap<Node, List<Action>> actionMap;
+ actionMap = new HashMap<Node, List<Action>>();
+
+ // Add nodes in default path into the hash map.
+ // Default path has subpaths, created by redirects. xxx for now, just get one of these.
+ Path p = ap.getDefaultPath().get(0);
+ addrules(ap.getDst(), p, actionMap);
+
+ // Add output ports for each node in the tapPath list. Include
+ // the host node connector of the destination server too.
+ HashMap<HostNodeConnector, Path> tapPaths = ap.getTapPaths();
+ for (HostNodeConnector tapDest: tapPaths.keySet()) {
+ Path tp = tapPaths.get(tapDest);
+ actionMap = addrules(tapDest, tp, actionMap);
+ }
+ return actionMap;
+ }
+
+ // Translate the path (edges + nodes) into a set of per-node forwarding actions.
+ // Coalesce them with the existing set of rules for this affinity path.
+ public HashMap<Node, List<Action>> addrules(HostNodeConnector hnc, Path p, HashMap<Node, List<Action>> actionMap) {
+ HashMap<Node, List<Action>> rules = actionMap;
+
+ Edge lastedge = null;
+ for (Edge e: p.getEdges()) {
+ NodeConnector op = e.getTailNodeConnector();
+ Node node = e.getTailNodeConnector().getNode();
+ List<Action> actions = rules.get(node);
+ rules.put(node, merge(actions, new Output(op)));
+ lastedge = e;
+ }
+ // Add the edge from the lastnode to the destination host.
+ NodeConnector dstNC = hnc.getnodeConnector();
+ Node lastnode = lastedge.getHeadNodeConnector().getNode();
+ // lastnode is also the same as hnc.getnodeConnectorNode();
+ List<Action> actions = rules.get(lastnode);
+ rules.put(lastnode, merge(actions, new Output(dstNC)));
+ return rules;
+ }
+ public void printActionMap(InetAddress src, InetAddress dst, HashMap<Node, List<Action>> aMap) {
+ log.debug("source: {}, destination: {}", src, dst);
+ for (Node n: aMap.keySet()) {
+ String astr = " ";
+ for (Action a: aMap.get(n)) {
+ astr = astr + "; " + a.toString();
+ }
+ log.debug("Node: {}, Output: {}", n, astr);
+ }
+ }
+
/**
* Add flow groups to forwarding rules manager as FlowEntry
* objects. Each flow group corresponds to a policy group in the
* may be REDIRECT, DROP, or TAP.
*/
public boolean programFlowGroupsOnNode(HashMap<String, List<Flow>>flowgroups,
- HashMap<String, HashMap<AffinityAttributeType, AffinityAttribute>>attribs,
- Node node) {
+ HashMap<String, HashMap<AffinityAttributeType,
+ AffinityAttribute>>attribs,
+ Node node) {
for (String groupName: flowgroups.keySet()) {
List<Flow> flowlist = flowgroups.get(groupName);
log.info("flatl2: {} (#flows={})", groupName, flowgroups.get(groupName).size());
if (actions.size() > 0) {
log.info("Adding actions {} to flow {}", actions, f);
f.setActions(actions);
- // Make a flowEntry object. groupName is the policy name, from the affinity link name. Same for all flows in this bundle.
+ // Make a flowEntry object. groupName is the policy name,
+ // from the affinity link name. Same for all flows in this bundle.
FlowEntry fEntry = new FlowEntry(groupName, flowName, f, node);
log.info("Install flow entry {} on node {}", fEntry.toString(), node.toString());
installFlowEntry(fEntry);
* (switch) and the list of configured actions.
*/
- public List<Action> calcForwardingActions(Node node, InetAddress src, InetAddress dst, HashMap<AffinityAttributeType, AffinityAttribute> attribs) {
+ public List<Action> calcForwardingActions(Node node, InetAddress src, InetAddress dst,
+ HashMap<AffinityAttributeType, AffinityAttribute> attribs) {
List<Action> fwdactions = new ArrayList<Action>();
AffinityAttributeType aatype;
log.info("Found a path isolate setting.");
}
+ // Apply MTP path.
+ aatype = AffinityAttributeType.SET_MAX_TPUT_PATH;
+
+ if (attribs.get(aatype) != null) {
+ log.info("Found a max tput setting.");
+ Output output = getOutputPort(node, dst, true);
+ if (output != null) {
+ fwdactions.add(output);
+ }
+ }
+
// Apply redirect
aatype = AffinityAttributeType.SET_PATH_REDIRECT;
// Using L2agent
Output output = getOutputPortL2Agent(node, wp);
+ // Using routing service.
+ // Output output = getOutputPort(node, wp, false);
if (output != null) {
fwdactions.add(output);
}
for (InetAddress tapip: taplist) {
log.info("tap information = {}", tapip);
Output output1 = getOutputPortL2Agent(node, tapip);
+ // Not using L2 agent, using routing service.
+ // Output output1 = getOutputPort(node, tapip, false);
if (output1 != null) {
fwdactions = merge(fwdactions, output1);
}
}
Output output2 = getOutputPortL2Agent(node, dst);
+ // Not using L2 agent, using routing service.
+ // Output output2 = getOutputPort(node, dst, false);
if (output2 != null) {
fwdactions = merge(fwdactions, output2);
}
return fwdactions;
}
+
+
+ /**
+ * Calculate paths for this src-dst pair after applying:
+ * -- default routing and exception/waypoint routing
+ * -- tap destinations.
+ * Return a list of Paths.
+ */
+
+ public AffinityPath calcAffinityPath(InetAddress src, InetAddress dst,
+ HashMap<AffinityAttributeType, AffinityAttribute> attribs) {
+
+ boolean maxTputPath = false;
+ AffinityPath ap;
+
+ log.info("calc paths: src = {}, dst = {}", src, dst);
+
+ AffinityAttributeType aatype;
+
+ // Apply drop
+ aatype = AffinityAttributeType.SET_DENY;
+ if (attribs.get(aatype) != null) {
+ return null;
+ }
+
+ // Apply isolate (no-op now), and continue to add other affinity types to the forwarding actions list.
+ aatype = AffinityAttributeType.SET_PATH_ISOLATE;
+ if (attribs.get(aatype) != null) {
+ log.info("Found a path isolate setting.");
+ }
+
+ // Apply MTP path, set the type of default path to compute.
+ aatype = AffinityAttributeType.SET_MAX_TPUT_PATH;
+ if (attribs.get(aatype) != null) {
+ log.info("Found a max tput setting.");
+ maxTputPath = true;
+ }
+ // Compute the default path, after applying waypoints and add it to the list.
+ List<Path> subpaths = new ArrayList<Path>();
+ HostNodeConnector srcNC, dstNC;
+ srcNC = getHostNodeConnector(src);
+ dstNC = getHostNodeConnector(dst);
+ if (srcNC == null || dstNC == null) {
+ log.info("src or destination does not have a HostNodeConnector. src={}, dst={}", src, dst);
+ return null;
+ }
+ Node srcNode = srcNC.getnodeconnectorNode();
+ Node dstNode = dstNC.getnodeconnectorNode();
+ ap = new AffinityPath(srcNC, dstNC);
+
+ log.debug("from node: {}", srcNC.toString());
+ log.debug("dst node: {}", dstNC.toString());
+
+ // Apply redirect
+ aatype = AffinityAttributeType.SET_PATH_REDIRECT;
+
+ SetPathRedirect rdrct = (SetPathRedirect) attribs.get(aatype);
+
+ // No redirects were added, so calculate the defaultPath by
+ // looking up the appropriate type of route in the routing
+ // service.
+ List<Path> route = new ArrayList<Path>();
+ if (rdrct == null) {
+ Path defPath;
+ if (maxTputPath == true) {
+ defPath = this.routing.getMaxThroughputRoute(srcNode, dstNode);
+ } else {
+ defPath = this.routing.getRoute(srcNode, dstNode);
+ }
+ route.add(defPath);
+ } else {
+ log.info("Found a path redirect setting. Calculating subpaths 1, 2");
+ List<InetAddress> wplist = rdrct.getWaypointList();
+ if (wplist != null) {
+ // Only one waypoint server in list.
+ InetAddress wp = wplist.get(0);
+ log.info("waypoint information = {}", wplist.get(0));
+ HostNodeConnector wpNC = getHostNodeConnector(wp);
+ Node wpNode = wpNC.getnodeconnectorNode();
+ Path subpath1;
+ Path subpath2;
+ subpath1 = this.routing.getRoute(srcNode, wpNode);
+ subpath2 = this.routing.getRoute(wpNode, dstNode);
+ route.add(subpath1);
+ route.add(subpath2);
+ }
+ }
+ if (route.size() > 0) {
+ ap.setDefaultPath(route);
+ }
+
+ // Apply tap, calculate paths to each tap destination and add to AffinityPath.
+ aatype = AffinityAttributeType.SET_TAP;
+
+ SetTap tap = (SetTap) attribs.get(aatype);
+
+ if (tap != null) {
+ log.info("Applying tap affinity.");
+ List<InetAddress> taplist = tap.getTapList();
+ if (taplist != null) {
+ // Add a new rule with original destination + tap destinations.
+ for (InetAddress tapip: taplist) {
+ log.info("Adding tap path to destination = {}", tapip);
+
+ Path tapPath;
+ HostNodeConnector tapNC = getHostNodeConnector(tapip);
+ Node tapNode = tapNC.getnodeconnectorNode();
+ tapPath = this.routing.getRoute(srcNode, tapNode);
+ ap.setTapPath(tapNC, tapPath);
+ }
+ }
+ }
+
+ log.debug("calcAffinityPath: {}", ap.toString());
+ return ap;
+ }
+
public List<Action> merge(List<Action> fwdactions, Action a) {
- if (!fwdactions.contains(a)) {
+ if (fwdactions == null) {
+ fwdactions = new ArrayList<Action>();
+ fwdactions.add(a);
+ } else if (!fwdactions.contains(a)) {
fwdactions.add(a);
}
return fwdactions;
// and not known to the l2agent which relies on learning.
if (op == null && isHostInactive(ip)) {
// Use routing.
- op = getOutputPort(node, ip);
+ op = getOutputPort(node, ip, false);
}
return op;
}
return hnConnector;
}
- public Output getOutputPort(Node node, InetAddress ipaddr) {
+ public Output getOutputPort(Node node, InetAddress ipaddr, boolean mtp) {
HostNodeConnector hnConnector;
hnConnector = getHostNodeConnector(ipaddr);
if (hnConnector != null) {
log.info("Both source and destination are connected to same switch nodes. output port is {}",
forwardPort);
} else {
- Path route = this.routing.getRoute(node, destNode);
+ Path route;
+ if (mtp == true) {
+ log.info("Lookup max throughput route {} -> {}", node, destNode);
+ route = this.routing.getMaxThroughputRoute(node, destNode);
+ } else {
+ route = this.routing.getRoute(node, destNode);
+ }
+
log.info("Path between source and destination switch nodes : {}",
route.toString());
- forwardPort = route.getEdges().get(0).getTailNodeConnector();
+ forwardPort = route.getEdges().get(0).getTailNodeConnector();
}
log.info("output port {} on node {} toward host {}", forwardPort, node, hnConnector);
return(new Output(forwardPort));
}
return null;
}
-
/**
* Install this flow entry object.
*/