* Make the demo-only tap routing service functional. 00/5100/1
authorSuchi Raman <suchi.raman@plexxi.com>
Mon, 3 Feb 2014 13:08:44 +0000 (08:08 -0500)
committerSuchi Raman <suchi.raman@plexxi.com>
Mon, 3 Feb 2014 13:08:44 +0000 (08:08 -0500)
* Lookup hostTracker correctlt and handle more cases -- e.g., static hosts (active vs. inactive).

Signed-off-by: Suchi Raman <suchi.raman@plexxi.com>
affinity/yang/src/main/yang/affinity-overview-H2.txt [new file with mode: 0644]
flatl2/src/main/java/org/opendaylight/affinity/flatl2/FlatL2AffinityImpl.java
scripts/affinity.py
scripts/mn3.py [new file with mode: 0644]

diff --git a/affinity/yang/src/main/yang/affinity-overview-H2.txt b/affinity/yang/src/main/yang/affinity-overview-H2.txt
new file mode 100644 (file)
index 0000000..b3dee2a
--- /dev/null
@@ -0,0 +1,142 @@
+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.
+
index 3625278ca8de53e19c8c1c417550654f32c18a74..ed3310ea26e2886cec1b8f885e6c29f29c6eb11f 100644 (file)
@@ -80,11 +80,12 @@ import org.opendaylight.controller.sal.utils.ServiceHelper;
 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;
@@ -317,12 +318,14 @@ public class FlatL2AffinityImpl implements IfNewHostNotify {
                 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
@@ -369,8 +372,10 @@ public class FlatL2AffinityImpl implements IfNewHostNotify {
                 // 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();
@@ -387,28 +392,35 @@ public class FlatL2AffinityImpl implements IfNewHostNotify {
             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
@@ -420,38 +432,121 @@ public class FlatL2AffinityImpl implements IfNewHostNotify {
         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;
     }
 
     /**
index eb4b2992389de1ef1e98f520411d43ad08b3c624..e4c9cd654fd6198b19a8d1170f738cbd70082ab7 100644 (file)
@@ -2,7 +2,9 @@
 
 import httplib2
 import json
+import requests
 import sys
+import urllib2
 
 #
 # Configure an affinity link and set its waypoint address. 
@@ -18,11 +20,25 @@ def rest_method(url, verb):
     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
@@ -47,6 +63,7 @@ def get_all_affinity_links():
     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'])
@@ -89,6 +106,7 @@ def client_ws_example():
     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"
@@ -103,6 +121,30 @@ def drop_ws_objects():
     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'
@@ -127,7 +169,7 @@ def set_tap(al, ipaddr):
     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")
@@ -147,6 +189,30 @@ def disable_affinity():
     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
 
@@ -164,37 +230,34 @@ def main():
     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')
 
diff --git a/scripts/mn3.py b/scripts/mn3.py
new file mode 100644 (file)
index 0000000..8f04ad6
--- /dev/null
@@ -0,0 +1,62 @@
+#!/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()