* Lookup hostTracker correctlt and handle more cases -- e.g., static hosts (active vs. inactive).
Signed-off-by: Suchi Raman <suchi.raman@plexxi.com>
--- /dev/null
+Affinity endpoint:
+=================
+
+This identifies an application endpoint, and may be one of the
+following. This is a location wthin the combined physical + virtual
+network and may be a stationary location referring to a switch/port or
+a virtual endpoint that may move within the network as the
+corresponding virtual machine is moved. The endpoint is identified by
+an individual IP address or a set of IP addresses specified by a
+subnet and mask. All endpoints within a given affinity confguration
+are assumed to be within a single layer 2 domain.
+
+Affinity address domain (not currently implemented):
+==================================================
+
+This represents a domain (i.e, set) of one or more addresses. An
+affinity address may not have a corresponding endpoint on the network,
+for example, for traffic from a set of external addresses (north-south
+traffic in a data center), the source IP address domain is not
+required to map exactly to endpoints or node connectors on the
+network.
+
+Affinity address specifies one or more of the following:
+
+ (a) VLAN + MAC address, or range of MAC addresses.
+
+ (b) Network layer address, IP address, or range of addresses, or
+ prefix/mask representing multiple sources.
+
+ (c) Transport layer address, which is the transport protocol type and
+ port number.
+
+Affinity Group:
+==============
+Affinity group which is an enumeration of one or more items where each
+item is an affinity endpoint or affinity address domain. An affintiy
+group may also contain other affinity groups in addition to endpoints
+and address domains.
+
+Affinity Link:
+=============
+Affinity link connects one group (from group) to another (to
+group). It represents a set of flows that start from the source group
+and end in the destination group. An affinity link has attributes
+(policies) attached to it that represent how these flows must be
+handled. An affinity link also has directionality associated with
+it. A bidirectional affinity link is equivalent to two unidirectional
+affinity links, one in each direction.
+
+Affinity attribute:
+==================
+An affinity attribute is assigned to an affinity link. An attribute is
+one of the following:
+
+ (a) Path affinity. Path affinities define the type of path required
+ by the application. It may be one of the following types -- latency
+ sensitive for applications that require low latency between their
+ components. Examples include access to block storage and network
+ connections between the application and database tier. Bandwidth
+ sensitive applications include video or audio streaming or bulk data
+ operations desiring high throughput. Isolated paths may be required
+ for applications requiring dedicated paths without sharing or
+ interference from other applications.
+
+ * Isolation. This is an attribute that specifies that the
+ application flow must be assigned an isolated path on the
+ network from flows from other affinity links. While flows
+ within the same affinity link may share one or more network
+ links of the path, flows from different affinity links will be
+ allocated to non-overlapping paths.
+
+ // Isolate flows according to certain constraints. No sharing with any other traffic.
+ grouping isolate-path {
+ // Average bandwidth requirement for each flow is estimated in Mbps.
+ leaf average-bandwidth-per-flow {
+ type uint16;
+ }
+ // Peak burst bandwidth, total per affinity link.
+ leaf burst-bandwidth {
+ type uint16;
+ }
+ }
+
+ * Low latency path. This is an attribute that specifies that the
+ flow is allocated lowest hopcount paths between source and
+ destination.
+
+ // Route through low latency path. May share with other types of traffic.
+ grouping low-latency-path {
+ // Average bandwidth estimated per flow, in Mbps.
+ leaf average-bandwidth-per-flow {
+ type uint16;
+ }
+ // Peak burst bandwidth, total per affinity link.
+ leaf burst-bandwidth {
+ type uint16;
+ }
+ }
+
+ * Bandwidth Optimized. Allocate a path of specified bandwidth to
+ this application.
+
+ // Optimize this path such that specified bandwidth is available to it. May share
+ // with other types of traffic.
+ grouping bandwidth-optimized-path {
+ // Average bandwidth estimated per flow, in Mbps.
+ leaf average-bandwidth-per-flow {
+ type uint16;
+ }
+ // Peak burst bandwidth, total per affinity link.
+ leaf burst-bandwidth {
+ type uint16;
+ }
+ }
+
+Flows from affinity links are interpreted as unicast (point to point)
+flows by an implementation of this API.
+
+The following types of affinity dictate a strict action or rule.
+
+ (b) Access control affinity. This is an attribute that specifies that
+ the application flow must either be permitted (PERMIT) or denied (DENY).
+
+ (c) Path redirect affinity. This affinity link must traverse specified
+ chain of waypoint locations, each specified by an IP address or Mac address.
+
+ (d) Tap affinity. All traffic belonging to this affinity link must be
+ mirrored to specified endpoint location. The endpoint location is
+ carried in the affinity attribute configuration.
+
+ (e) Priority forwarding. All traffic belonging to this affinity link
+ must be assigned a priority level. Setting this priority level
+ provides differential treatment for this traffic relative to other
+ flows on the network. One of five levels of priority must be
+ specified (very low=0, low, normal, high, very high=5).
+
+Strict vs. sensitive:
+=====================
+Note that the attribute may be a goal (not strict) -- latency or
+bandwidth sensitive, or an action (strict) -- forward to this port,
+tunnel to this endpoint.
+
import org.opendaylight.controller.hosttracker.IfIptoHost;
import org.opendaylight.controller.hosttracker.IfNewHostNotify;
import org.opendaylight.controller.hosttracker.hostAware.HostNodeConnector;
+import org.opendaylight.controller.hosttracker.HostIdFactory;
+import org.opendaylight.controller.hosttracker.IHostId;
import org.opendaylight.controller.switchmanager.ISwitchManager;
import org.opendaylight.affinity.affinity.InetAddressMask;
import org.opendaylight.controller.hosttracker.HostIdFactory;
-import org.opendaylight.controller.hosttracker.IHostId;
import org.opendaylight.controller.hosttracker.IfIptoHost;
import org.opendaylight.controller.hosttracker.hostAware.HostNodeConnector;
import org.opendaylight.controller.sal.routing.IRouting;
flowName = "[" + groupName + ":" + srcIp + ":" + dstIp + "]";
List<Action> actions = calcForwardingActions(node, srcIp, dstIp, attribs.get(groupName));
// Update flow with actions.
- 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.
- FlowEntry fEntry = new FlowEntry(groupName, flowName, f, node);
- log.info("Install flow entry {} on node {}", fEntry.toString(), node.toString());
- installFlowEntry(fEntry);
+ 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.
+ FlowEntry fEntry = new FlowEntry(groupName, flowName, f, node);
+ log.info("Install flow entry {} on node {}", fEntry.toString(), node.toString());
+ installFlowEntry(fEntry);
+ }
}
}
return true; // error checking
// Lookup output port on this node for this destination.
// Using L2agent
- Output output = getOutputPortL2Agent(node, wp);
- fwdactions.add(output);
+ Output output = getOutputPort(node, wp);
+ if (output != null) {
+ fwdactions.add(output);
+ }
// Using simpleforwarding.
// Output output = getOutputPort(node, wp);
// Controller controller = new Controller();
log.info("Applying tap affinity.");
List<InetAddress> taplist = tap.getTapList();
if (taplist != null) {
- // Only one waypoint server in list.
+ // Add a new rule with original destination + tap destinations.
for (InetAddress tapip: taplist) {
log.info("tap information = {}", tapip);
- // Lookup output port on this node for this destination.
-
- // Using L2agent
- Output output1 = getOutputPortL2Agent(node, tapip);
- Output output2 = getOutputPortL2Agent(node, dst);
-
- fwdactions.add(output1);
- fwdactions.add(output2);
-
- // Using simpleforwarding.
- // Output output = getOutputPort(node, wp);
- // Controller controller = new Controller();
- // fwdactions.add(controller);
+ Output output1 = getOutputPort(node, tapip);
+ if (output1 != null) {
+ fwdactions = merge(fwdactions, output1);
+ }
}
+ Output output2 = getOutputPort(node, dst);
+ if (output2 != null) {
+ fwdactions = merge(fwdactions, output2);
+ }
+
+ // Using simpleforwarding.
+ // Output output = getOutputPort(node, wp);
+ // Controller controller = new Controller();
+ // fwdactions.add(controller);
}
}
return fwdactions;
}
+
+ public List<Action> merge(List<Action> fwdactions, Action a) {
+ if (!fwdactions.contains(a)) {
+ fwdactions.add(a);
+ }
+ return fwdactions;
+ }
/**
* Using L2agent, get the output port toward this IP from this
if (l2agent != null) {
/* Look up the output port leading to the waypoint. */
HostNodeConnector host = (HostNodeConnector) hostTracker.hostFind(ip);
- log.info("output port on node {} toward host {}", node, host);
- NodeConnector dst_connector = l2agent.lookup_output_port(node, host.getDataLayerAddressBytes());
- if (dst_connector != null) {
- op = new Output(dst_connector);
+ if (host != null) {
+ log.info("output port on node {} toward host {}", node, host);
+ NodeConnector dst_connector = l2agent.lookup_output_port(node, host.getDataLayerAddressBytes());
+ if (dst_connector != null) {
+ op = new Output(dst_connector);
+ }
}
} else {
log.info("l2agent is not set!!!");
}
+
+ // host node connector may be null, if this address is static
+ // and not known to the l2agent which relies on learning.
+ if (op == null && isHostInactive(ip)) {
+ // Use routing.
+ op = getOutputPort(node, ip);
+ }
return op;
}
- public Output getOutputPort(Node node, InetAddress wp) {
- IHostId id = HostIdFactory.create(wp, null);
- HostNodeConnector hnConnector = this.hostTracker.hostFind(id);
- Node destNode = hnConnector.getnodeconnectorNode();
+ public boolean isHostActive(InetAddress ipaddr) {
+ Set<HostNodeConnector> activeStaticHosts = hostTracker.getActiveStaticHosts();
+ for (HostNodeConnector h : activeStaticHosts) {
+ InetAddress networkAddress = h.getNetworkAddress();
+ log.info("Checking match {} vs. {}", networkAddress, ipaddr);
+ if (networkAddress == ipaddr) {
+ log.debug("networkaddress found {} = {}", ipaddr, networkAddress);
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public boolean isHostKnown(InetAddress ipaddr) {
+ Set<HostNodeConnector> knownHosts = hostTracker.getAllHosts();
+ for (HostNodeConnector h : knownHosts) {
+ InetAddress networkAddress = h.getNetworkAddress();
+ log.info("Checking match {} vs. {}", networkAddress, ipaddr);
+ if (networkAddress.equals(ipaddr)) {
+ log.debug("networkaddress found {} = {}", ipaddr, networkAddress);
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public boolean isHostInactive(InetAddress ipaddr) {
+ Set<HostNodeConnector> inactiveStaticHosts = hostTracker.getInactiveStaticHosts();
+ for (HostNodeConnector h : inactiveStaticHosts) {
+ InetAddress networkAddress = h.getNetworkAddress();
+ log.info("Checking match {} vs. {}", networkAddress, ipaddr);
+ if (networkAddress.equals(ipaddr)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public HostNodeConnector getInactiveHost(InetAddress ipaddr) {
+ Set<HostNodeConnector> inactiveStaticHosts = hostTracker.getInactiveStaticHosts();
+ for (HostNodeConnector h : inactiveStaticHosts) {
+ InetAddress networkAddress = h.getNetworkAddress();
+ log.info("Checking match {} vs. {}", networkAddress, ipaddr);
+ if (networkAddress.equals(ipaddr)) {
+ return h;
+ }
+ }
+ return null;
+ }
+
+ public HostNodeConnector getHostNodeConnector(InetAddress ipaddr) {
+ /**
+ * This host may be active, inactive/static or not present in the hosts DB.
+ */
+ HostNodeConnector hnConnector;
+ hnConnector = null;
+ log.info("Lookup hostTracker for this host");
- log.debug("from node: {}", node.toString());
- log.debug("dest node: {}", destNode.toString());
-
- // Get path between both the nodes
- NodeConnector forwardPort = null;
- if (node.getNodeIDString().equals(destNode.getNodeIDString())) {
- forwardPort = hnConnector.getnodeConnector();
- log.info("Both source and destination are connected to same switch nodes. output port is {}",
- forwardPort);
- } else {
- Path route = this.routing.getRoute(node, destNode);
- log.info("Path between source and destination switch nodes : {}",
- route.toString());
- forwardPort = route.getEdges().get(0).getTailNodeConnector();
+ // Check inactive hosts.
+ if (isHostInactive(ipaddr)) {
+ log.info("host is from inactive DB");
+ hnConnector = getInactiveHost(ipaddr);
+ } else if (isHostKnown(ipaddr)) {
+ log.info("host is known to hostTracker, attempt a hostfind");
+ IHostId id = HostIdFactory.create(ipaddr, null);
+ hnConnector = this.hostTracker.hostFind(id);
}
- return(new Output(forwardPort));
+ return hnConnector;
+ }
+
+ public Output getOutputPort(Node node, InetAddress ipaddr) {
+ HostNodeConnector hnConnector;
+ hnConnector = getHostNodeConnector(ipaddr);
+ if (hnConnector != null) {
+ Node destNode = hnConnector.getnodeconnectorNode();
+
+ log.debug("from node: {}", node.toString());
+ log.debug("dest node: {}", destNode.toString());
+
+ // Get path between both the nodes
+ NodeConnector forwardPort = null;
+ if (node.getNodeIDString().equals(destNode.getNodeIDString())) {
+ forwardPort = hnConnector.getnodeConnector();
+ log.info("Both source and destination are connected to same switch nodes. output port is {}",
+ forwardPort);
+ } else {
+ Path route = this.routing.getRoute(node, destNode);
+ log.info("Path between source and destination switch nodes : {}",
+ route.toString());
+ forwardPort = route.getEdges().get(0).getTailNodeConnector();
+ }
+ log.info("output port {} on node {} toward host {}", forwardPort, node, hnConnector);
+ return(new Output(forwardPort));
+ }
+ return null;
}
/**
import httplib2
import json
+import requests
import sys
+import urllib2
#
# Configure an affinity link and set its waypoint address.
print "return code %d" % (resp.status)
return content
+# Generic REST query
+def rest_method_2(url, rest_type, payload=None):
+ if (rest_type == "GET"):
+ resp = requests.get(url, auth=('admin', 'admin'))
+ return resp.json()
+ elif (rest_type == "PUT"):
+ headers = {'content-type': 'application/json'}
+ print json.dumps(payload)
+ resp = requests.put(url, auth=('admin', 'admin'), data=json.dumps(payload), headers=headers)
+ print resp.text
+ return resp.status_code
+ elif (rest_type == "DELETE"):
+ resp = requests.delete(url, auth=('admin', 'admin'))
def list_all_hosts():
print "list active hosts"
get_url = 'http://localhost:8080/controller/nb/v2/hosttracker/default/hosts/active'
content = rest_method(get_url, "GET")
+ print content
hostCfg = json.loads(content)
for host in hostCfg['hostConfig']:
print host
get_url = 'http://localhost:8080/affinity/nb/v2/affinity/default/affinity-links'
content = rest_method(get_url, "GET")
affylinks = json.loads(content)
+ print affylinks
for link in affylinks['affinityLinks']:
print "Affinity link: %s" % link
get_affinity_link(link['name'])
put_url = 'http://localhost:8080/affinity/nb/v2/affinity/default/group/clients/add/ip/10.0.0.3'
rest_method(put_url, "PUT")
+
def drop_ws_objects():
print "remove inflows link"
put_url = "http://localhost:8080/affinity/nb/v2/affinity/default/delete/group/clients"
rest_method(put_url, "PUT")
+def tap_example():
+ # Create two affinity groups
+ print "create group A"
+ put_url = 'http://localhost:8080/affinity/nb/v2/affinity/default/create/group/a'
+ rest_method(put_url, "PUT")
+
+ print "create group B"
+ put_url = 'http://localhost:8080/affinity/nb/v2/affinity/default/create/group/b'
+ rest_method(put_url, "PUT")
+
+ print "create link A -> B"
+ put_url = 'http://localhost:8080/affinity/nb/v2/affinity/default/create/link/a_to_b/from/a/to/b'
+ rest_method(put_url, "PUT")
+
+ print "add ip addresses to A"
+ put_url = 'http://localhost:8080/affinity/nb/v2/affinity/default/group/a/add/ip/10.0.0.1'
+ rest_method(put_url, "PUT")
+ put_url = 'http://localhost:8080/affinity/nb/v2/affinity/default/group/a/add/ip/10.0.0.2'
+ rest_method(put_url, "PUT")
+
+ print "add ip addresses to B"
+ put_url = 'http://localhost:8080/affinity/nb/v2/affinity/default/group/b/add/ip/10.0.0.3'
+ rest_method(put_url, "PUT")
+
def repeat_add_link():
print "create link inflows"
put_url = 'http://localhost:8080/affinity/nb/v2/affinity/default/create/link/inflows/from/clients/to/webservers'
put_url = 'http://localhost:8080/affinity/nb/v2/affinity/default/link/' + al + '/settap/' + ipaddr
rest_method(put_url, "PUT")
-# Add a tap to ipaddress.
+# Remove all tap servers and the tap affinity attribute.
def unset_tap(al, ipaddr):
put_url = 'http://localhost:8080/affinity/nb/v2/affinity/default/link/' + al + '/unsettap/' + ipaddr
rest_method(put_url, "PUT")
put_url = 'http://localhost:8080/affinity/nb/v2/flatl2/default/disableaffinity/'
rest_method(put_url, "PUT")
+# Set affinity attributes and make sure they are associated with the affinity link.
+def add_deny():
+ # Set deny.
+ set_deny('deny')
+ get_affinity_link('inflows')
+ set_deny('permit')
+ get_affinity_link('inflows')
+
+def add_waypoint():
+ set_waypoint_address('inflows', '10.0.0.2')
+
+# Add tap servers.
+def test_tap():
+ tap_example()
+ set_tap('a_to_b', '10.0.0.4')
+ add_static_host_tap('a_to_b', '10.0.0.20')
+ get_affinity_link('a_to_b')
+ enable_affinity() # Tap to '10.0.0.4'.
+
+def add_isolate():
+ set_path_isolate()
+ get_affinity_link('inflows')
+ enable_affinity() # Large flow forwarding
+
def main():
global h
print "get_all_affinity_links..."
get_all_affinity_links()
- set_attr()
+ test_tap()
list_all_hosts()
return
-# Set affinity attributes and make sure they are associated with the affinity link.
-def set_attr():
- # Set deny.
- set_deny('deny')
- get_affinity_link('inflows')
-
- # Set waypoint and tap.
- set_deny('permit')
- set_waypoint_address('inflows', '10.0.0.2')
- set_tap('inflows', '10.0.0.6')
- set_tap('inflows', '10.0.0.4')
- get_affinity_link('inflows')
+def add_static_host_tap(al, ipaddr):
+ payload = {'dataLayerAddress':'00:00:00:00:01:01',
+ 'nodeType':'OF',
+ "nodeId":"00:00:00:00:00:00:00:01",
+ "nodeConnectorType":"OF",
+ "nodeConnectorId":"9",
+ "vlan":"1",
+ "staticHost":'true',
+ "networkAddress":ipaddr}
+ url = "http://localhost:8080/controller/nb/v2/hosttracker/default/address/%s" % (ipaddr)
+ print payload
+ status = rest_method_2(url, "PUT", payload)
+ print "add_static_host: ", status
- # Change a few affinity attributes and get the new link configuration.
- unset_tap('inflows', '10.0.0.6')
- print "get_affinity_link..."
- get_affinity_link('inflows')
-
- enable_affinity() # Tap to '10.0.0.4'.
- unset_tap('inflows', '10.0.0.4')
- set_path_isolate()
- get_affinity_link('inflows')
- enable_affinity() # No action for isolate. Restore normal forwarding.
+ # Add tap to static host.
+ print "add tap to new static host" + ipaddr
+ put_url = 'http://localhost:8080/affinity/nb/v2/affinity/default/link/' + al + '/settap/' + ipaddr
+ rest_method(put_url, "PUT")
#if __name__ == "__main__":
# main()
+#opener = {}
+#init_urllib()
h = httplib2.Http(".cache")
h.add_credentials('admin', 'admin')
--- /dev/null
+#!/usr/bin/python
+
+from mininet.cli import CLI
+from mininet.topo import Topo
+from mininet.net import Mininet
+from mininet.link import TCLink, Intf
+from mininet.log import setLogLevel
+from mininet.node import CPULimitedHost, Controller, RemoteController
+
+ctrl='172.15.1.151'
+# To use this topology, run:
+# > sudo ./mn1.py
+# Using spaces rather than '=' is very important, as is having the --link flag
+#c1 = RemoteController( 'c1', ip='192.168.56.1' )
+c1 = RemoteController( 'c1', ip=ctrl )
+
+
+class TestTopo(Topo):
+
+ def __init__(self, **opts):
+ Topo.__init__(self, **opts)
+
+ # Three switches
+ s1 = self.addSwitch('s1')
+ s2 = self.addSwitch('s2')
+ s3 = self.addSwitch('s3')
+
+ # Connect switches into tree
+ self.addLink(s2, s1, bw=20)
+ self.addLink(s3, s1, bw=20)
+ self.addLink(s2, s3, bw=10)
+
+ # Four hosts
+ h1 = self.addHost('h1')
+ h2 = self.addHost('h2')
+ h3 = self.addHost('h3')
+ h4 = self.addHost('h4')
+
+ # Connect hosts to switches
+ self.addLink(h1, s2, bw=10) # These two links get limited
+ self.addLink(h2, s2, bw=10)
+ self.addLink(h3, s3)
+ self.addLink(h4, s3)
+
+topos = { 'net1' : (lambda: TestTopo()) }
+
+def demo():
+ "Create network"
+ topo = TestTopo()
+ net = Mininet(topo=topo, host=CPULimitedHost, link=TCLink, controller=lambda name:RemoteController(name, ip=ctrl, port=6633))
+ net.start()
+ print "*** testing basic connectivity (in-rack and off-rack)"
+# h1, h2 = net.getNodeByName('h1', 'h2')
+# net.ping( [ h1, h2 ] )
+# net.pingAll()
+
+ CLI( net )
+# net.stop()
+
+if __name__ == '__main__':
+ setLogLevel('info')
+ demo()