Import Floodlight v0.90
diff --git a/src/ext/floodlight/example/README b/src/ext/floodlight/example/README
new file mode 100644
index 0000000..e3e8179
--- /dev/null
+++ b/src/ext/floodlight/example/README
@@ -0,0 +1,28 @@
+One of Floodlight's main goals is extensibility and flexibility.
+
+To prove that point, this directory includes a number of useful
+utilities as examples of what can do with this extensibility.
+
+UTILITIES:
+--------------------------
+
+graphDeps.py and graphTopo.py
+
+    Read the module dependencies (graphDeps.py) or the topology
+    from the REST API and output it in the 'dot' format used by the
+    popular graphviz (www.graphviz.org) package so that they can
+    be visualized.
+
+    Example usage:
+        ./graphTopo.py $hostname                    # generate .dot file
+        dot -Tpdf -o $hostname.pdf $hostname.dot    # convert to PDF
+        open $hostname.pdf                          # open to view topology
+        
+
+
+packetStreamerClientExample.py
+
+    Example client for the packet streamer server in floodlight.
+    Allows you to intercept packets from floodlight's packet_in
+    processing chain and read them.
+
diff --git a/src/ext/floodlight/example/cli.py b/src/ext/floodlight/example/cli.py
new file mode 100755
index 0000000..ca8b443
--- /dev/null
+++ b/src/ext/floodlight/example/cli.py
@@ -0,0 +1,115 @@
+#!/usr/bin/python
+
+import sys
+import argparse
+import json
+import httplib
+import urllib2
+
+class RestApi(object):
+
+    def __init__(self, server,port):
+        self.server = server
+        self.port = port
+
+    def get(self, path):
+        #ret = self.rest_call(path, {}, 'GET')
+        #return ret[2]
+        f = urllib2.urlopen('http://'+self.server+':'+str(self.port)+path)
+        ret = f.read()
+        return json.loads(ret)
+
+    def set(self, path, data):
+        ret = self.rest_call(path, data, 'POST')
+        return ret[0] == 200
+
+    def remove(self, objtype, data):
+        #ret = self.rest_call(data, 'DELETE')
+        return ret[0] == 200
+
+    def rest_call(self, path, data, action):
+        headers = {
+            'Content-type': 'application/json',
+            'Accept': 'application/json',
+            }
+        body = json.dumps(data)
+        conn = httplib.HTTPConnection(self.server, self.port)
+        conn.request(action, path, body, headers)
+        response = conn.getresponse()
+        ret = (response.status, response.reason, response.read())
+        conn.close()
+        print str(ret[2])
+        return ret
+
+
+    
+usage_desc =  """
+Command descriptions: 
+
+    host [debug]
+    link [tunnellinks]
+    port <blocked | broadcast>
+    memory
+    switch
+    switchclusters
+    counter [DPID] <name>
+    switch_stats [DPID] <port | queue | flow | aggregate | desc | table | features | host>
+"""
+
+def lookupPath(cmd):
+    path = ''
+
+    numargs = len(args.otherargs)
+
+    if args.cmd == 'switch_stats':
+        if numargs == 1:
+            path = '/wm/core/switch/all/'+args.otherargs[0]+'/json'
+        elif numargs == 2:
+            path = '/wm/core/switch/'+args.otherargs[0]+'/'+args.otherargs[1]+'/json'
+    elif args.cmd == 'switch':
+        path = '/wm/core/controller/switches/json'
+    elif args.cmd == 'counter':
+        if numargs == 1:
+            path = '/wm/core/counter/'+args.otherargs[0]+'/json'
+        elif numargs == 2:
+            path = '/wm/core/counter/'+args.otherargs[0]+'/'+args.otherargs[1]+'/json'
+    elif args.cmd == 'memory':
+        path = '/wm/core/memory/json'
+    elif args.cmd == 'link':
+        if numargs == 0:
+            path = '/wm/topology/links/json'
+        elif numargs == 1:
+            path = '/wm/topology/'+args.otherargs[0]+'/json'
+    elif args.cmd == 'port' and numargs == 1:
+        if args.otherargs[0] == "blocked":
+            path = '/wm/topology/blockedports/json'
+        elif args.otherargs[0] == "broadcast":
+            path = '/wm/topology/broadcastdomainports/json'
+    elif args.cmd == 'switchclusters':
+        path = '/wm/topology/switchclusters/json'
+    elif args.cmd == 'host':
+        path = '/wm/device/'
+        if len(args.otherargs) == 1 and args.otherargs[0] == 'debug':
+            path = '/wm/device/debug'
+    else:
+        print usage_desc
+        path = ''
+        exit(0)
+    return path
+
+parser = argparse.ArgumentParser(description='process args', usage=usage_desc, epilog='foo bar help')
+parser.add_argument('--ip', default='localhost')
+parser.add_argument('--port', default=8080)
+parser.add_argument('cmd')
+parser.add_argument('otherargs', nargs='*')
+args = parser.parse_args()
+
+#print args
+
+rest = RestApi(args.ip, args.port)
+path = lookupPath(args.cmd)
+
+out = rest.get(path)
+print json.dumps(out,sort_keys=True, indent=4)
+print "Number of items: " + str(len(out))
+
diff --git a/src/ext/floodlight/example/graphDeps.py b/src/ext/floodlight/example/graphDeps.py
new file mode 100755
index 0000000..505d516
--- /dev/null
+++ b/src/ext/floodlight/example/graphDeps.py
@@ -0,0 +1,72 @@
+#!/usr/bin/python
+
+import urllib2
+import json
+import sys
+
+
+def simple_json_get(url):
+    return json.loads(urllib2.urlopen(url).read())
+
+
+def shorten(s):
+    return  s.replace('net.floodlightcontroller','n.f'
+            ).replace('com.bigswitch','c.b')
+
+def usage(s):
+    sys.stderr.write("Usage:\ngrahDeps.py hostname [port]\n%s" % s)
+    sys.stderr.write("\n\n\n\n    writes data to 'hostname.dot' for use with graphviz\n")
+    sys.exit(1)
+
+
+if __name__ == '__main__':
+
+    host='localhost'
+    port=8080
+
+    if len(sys.argv) == 1 or sys.argv[1] == '-h' or sys.argv[1] == '--help':
+        usage("need to specify hostname")
+
+    host = sys.argv[1]
+    if len(sys.argv) > 2:
+        port = int(sys.argv[2])
+
+    sys.stderr.write("Connecting to %s:%d ..." % (host,port))
+    URL="http://%s:%d/wm/core/module/loaded/json" % (host,port)
+
+    deps = simple_json_get(URL)
+    serviceMap = {}
+    nodeMap = {}
+    nodeCount = 0
+
+    sys.stderr.write("Writing to %s.dot ..." % (host))
+    f = open("%s.dot" % host, 'w')
+
+    f.write( "digraph Deps {\n")
+
+    for mod, info in deps.iteritems():
+        # sys.stderr.write("Discovered module %s\n" % mod)
+        nodeMap[mod] = "n%d" % nodeCount
+        nodeCount += 1
+        label = shorten(mod) + "\\n"
+        for service, serviceImpl in info['provides'].iteritems():
+            # sys.stderr.write("     Discovered service %s implemented with %s\n" % (service,serviceImpl))
+            label += "\\nService=%s" % shorten(service)
+            serviceMap[serviceImpl] = mod
+        f.write("     %s [ label=\"%s\", color=\"blue\"];\n" % (nodeMap[mod], label))
+
+    f.write("\n")      # for readability
+
+    for mod, info in deps.iteritems():
+        for dep, serviceImpl in info['depends'].iteritems():
+            f.write("     %s -> %s [ label=\"%s\"];\n" % (
+                    nodeMap[mod],
+                    shorten(nodeMap[serviceMap[serviceImpl]]),
+                    shorten(dep)))
+        
+
+    f.write("}\n")
+    f.close();
+    sys.stderr.write("Now type\ndot -Tpdf -o %s.pdf %s.dot\n" % (
+        host, host))
+        
diff --git a/src/ext/floodlight/example/graphTopo.py b/src/ext/floodlight/example/graphTopo.py
new file mode 100755
index 0000000..b89a763
--- /dev/null
+++ b/src/ext/floodlight/example/graphTopo.py
@@ -0,0 +1,77 @@
+#!/usr/bin/python
+
+import urllib2
+import json
+import sys
+
+
+def simple_json_get(url):
+    return json.loads(urllib2.urlopen(url).read())
+
+
+def shorten(s):
+    return  s.replace('net.floodlightcontroller','n.f'
+            ).replace('com.bigswitch','c.b')
+
+def usage(s):
+    sys.stderr.write("Usage:\ngrahTopo.py hostname [port]\n%s" % s)
+    sys.stderr.write("\n\n\n\n    writes data to 'hostname.dot' for use with graphviz\n")
+    sys.exit(1)
+
+
+if __name__ == '__main__':
+
+    host='localhost'
+    port=8080
+
+    if len(sys.argv) == 1 or sys.argv[1] == '-h' or sys.argv[1] == '--help':
+        usage("need to specify hostname")
+
+    host = sys.argv[1]
+    if len(sys.argv) > 2:
+        port = int(sys.argv[2])
+
+    sys.stderr.write("Connecting to %s:%d ..." % (host,port))
+    URL="http://%s:%d/wm/topology/links/json" % (host,port)
+
+    # {
+    # "dst-port": 2, 
+    # "dst-switch": "00:00:00:00:00:00:00:0a", 
+    # "src-port": 3, 
+    # "src-switch": "00:00:00:00:00:00:00:0c"
+    # }
+
+    links = simple_json_get(URL)
+    nodeMap = {}
+
+    sys.stderr.write("Writing to %s.dot ..." % (host))
+    f = open("%s.dot" % host, 'w')
+
+    f.write( "digraph Deps {\n")
+
+    for link in links:
+        # sys.stderr.write("Discovered module %s\n" % mod)
+        if not link['dst-switch'] in nodeMap:
+            sw = link['dst-switch']
+            nodeMap[sw] = "n%d" % len(nodeMap)
+            f.write("     %s [ label=\"dpid=%s\", color=\"blue\"];\n" % (nodeMap[sw], sw))
+
+        if not link['src-switch'] in nodeMap:
+            sw = link['src-switch']
+            nodeMap[sw] = "n%d" % len(nodeMap)
+            f.write("     %s [ label=\"dpid=%s\", color=\"blue\"];\n" % (nodeMap[sw], sw))
+
+
+        f.write("     %s -> %s [ label=\"%s\"];\n" % (
+                nodeMap[link['dst-switch']],
+                nodeMap[link['src-switch']],
+                "src_port %d --> dst_port %d" % (link['src-port'],link['dst-port'])
+                )
+                )
+        
+
+    f.write("}\n")
+    f.close();
+    sys.stderr.write("Now type\ndot -Tpdf -o %s.pdf %s.dot\n" % (
+        host, host))
+        
diff --git a/src/ext/floodlight/example/packetStreamerClientExample.py b/src/ext/floodlight/example/packetStreamerClientExample.py
new file mode 100755
index 0000000..4510374
--- /dev/null
+++ b/src/ext/floodlight/example/packetStreamerClientExample.py
@@ -0,0 +1,138 @@
+#!/usr/bin/python
+
+import urllib2
+import json
+import re
+import sys
+
+from optparse import OptionParser
+
+sys.path.append('~/floodlight/target/gen-py')
+sys.path.append('~/floodlight/thrift/lib/py')
+
+from packetstreamer import PacketStreamer
+from packetstreamer.ttypes import *
+
+from thrift import Thrift
+from thrift.transport import TSocket
+from thrift.transport import TTransport
+from thrift.protocol import TBinaryProtocol
+
+SESSIONID = 'sessionId'
+usage = "usage: %prog [options]"
+parser = OptionParser(usage=usage, version="%prog 1.0")
+parser.add_option("-c", "--controller", dest="controller", metavar="CONTROLLER_IP",
+                  default="127.0.0.1", help="controller's IP address")
+parser.add_option("-m", "--mac", dest="mac", metavar="HOST_MAC",
+                  help="The host mac address to trace the OF packets")
+
+(options, args) = parser.parse_args()
+
+def validateIp(ip):
+    ipReg = ("(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)" 
+             "\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)"
+             "\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)"
+             "\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)")
+    m = re.compile(ipReg).match(ip)
+    if m:
+        return True
+    else :
+        return False
+
+def validateMac(mac):
+    macReg = '([a-fA-F0-9]{2}:){5}[a-fA-F0-9]{2}' # same regex as above
+    m = re.compile(macReg).match(mac)
+    if m:
+        return True
+    else :
+        return False
+
+if not validateIp(options.controller):
+    parser.error("Invalid format for ip address.")
+
+if not options.mac:
+    parser.error("-m or --mac option is required.")
+
+if not validateMac(options.mac):
+    parser.error("Invalid format for mac address. Format: xx:xx:xx:xx:xx:xx")
+
+controller = options.controller
+host = options.mac
+
+url = 'http://%s:8080/wm/core/packettrace/json' % controller
+filter = {'mac':host, 'direction':'both', 'period':1000}
+post_data = json.dumps(filter)
+request = urllib2.Request(url, post_data, {'Content-Type':'application/json'})
+response_text = None
+
+def terminateTrace(sid):
+    global controller
+
+    filter = {SESSIONID:sid, 'period':-1}
+    post_data = json.dumps(filter)
+    url = 'http://%s:8080/wm/core/packettrace/json' % controller
+    request = urllib2.Request(url, post_data, {'Content-Type':'application/json'})
+    try:
+        response = urllib2.urlopen(request)
+        response_text = response.read()
+    except Exception, e:
+        # Floodlight may not be running, but we don't want that to be a fatal
+        # error, so we just ignore the exception in that case.
+        print "Exception:", e
+
+try: 
+    response = urllib2.urlopen(request)
+    response_text = response.read()
+except Exception, e:
+    # Floodlight may not be running, but we don't want that to be a fatal
+    # error, so we just ignore the exception in that case.
+    print "Exception:", e
+    exit
+
+if not response_text:
+    print "Failed to start a packet trace session"
+    sys.exit()
+
+response_text = json.loads(response_text)
+
+sessionId = None
+if SESSIONID in response_text:
+    sessionId = response_text[SESSIONID]
+else:
+    print "Failed to start a packet trace session"
+    sys.exit()
+
+try:
+
+    # Make socket
+    transport = TSocket.TSocket('localhost', 9090)
+
+    # Buffering is critical. Raw sockets are very slow
+    transport = TTransport.TFramedTransport(transport)
+
+    # Wrap in a protocol
+    protocol = TBinaryProtocol.TBinaryProtocol(transport)
+
+    # Create a client to use the protocol encoder
+    client = PacketStreamer.Client(protocol)
+
+    # Connect!
+    transport.open()
+
+    while 1:
+        packets = client.getPackets(sessionId)
+        for packet in packets:
+            print "Packet: %s"% packet
+            if "FilterTimeout" in packet:
+                sys.exit()
+
+except Thrift.TException, e:
+    print '%s' % (e.message)
+    terminateTrace(sessionId)
+
+except KeyboardInterrupt, e:
+    terminateTrace(sessionId)
+
+# Close!
+transport.close()
+