Add ONOS GUI (python server, html and javascript)
diff --git a/web/js/onos-topology.js b/web/js/onos-topology.js
new file mode 100644
index 0000000..7bee4c0
--- /dev/null
+++ b/web/js/onos-topology.js
@@ -0,0 +1,243 @@
+function gui(data_source){
+    var width = 960,
+    height = 500;
+    var color = d3.scale.category20();
+
+    var svg = d3.select("body").append("svg:svg")
+	.attr("width", width)
+	.attr("height", height);
+
+    var force = d3.layout.force()
+	.charge(-500)
+	.linkDistance(100)
+	.size([width, height]);
+
+    var path = svg.selectAll("path");
+    var circle = svg.selectAll("circle");
+    var text = svg.selectAll("g");
+
+
+    d3.json(data_source,init);
+
+    function init(json){
+        nodes = force.nodes();
+        links = force.links();
+
+	json.nodes.forEach(function(item) {
+            nodes.push(item);
+	});
+	json.links.forEach(function(item) {
+            links.push(item);
+	});
+	draw(nodes, links);
+    }
+
+    function update(json) {
+	Array.prototype.diff2 = function(arr) {
+	    return this.filter(function(i) {
+		for (var j = 0; j < arr.length ; j++) {
+		    if (arr[j].source === i.source.index && 
+			arr[j].target === i.target.index)
+			return false;
+		}
+		return true;
+	    });
+	};
+
+	Array.prototype.diff = function(arr) {
+	    return this.filter(function(i) {
+		for (var j = 0; j < arr.length ; j++) {
+		    if (arr[j].source.index === i.source && 
+			arr[j].target.index === i.target)
+			return false;
+		}
+		return true;
+	    });
+	};
+
+	Array.prototype.node_diff = function(arr) {
+	    return this.filter(function(i) {
+		for (var j = 0; j < arr.length ; j++) {
+		    if (arr[j].name === i.name)
+			return false;
+		}
+		return true;
+	    });
+	};
+
+
+//        links.sort(function(a,b) {
+//            if (a.source > b.source) {return 1;}
+//            else if (a.source < b.source) {return -1;}
+//            else {
+//                if (a.target > b.target) {return 1;}
+//                if (a.target < b.target) {return -1;}
+//                else {return 0;}
+//            }
+//        });
+//        for (var i=0; i<links.length; i++) {
+//          if (i != 0 &&
+//            links[i].source == links[i-1].source &&
+//            links[i].target == links[i-1].target) {
+//            links[i].linknum = links[i-1].linknum + 1;
+//          }
+//          else {links[i].linknum = 1;};
+//        };
+
+
+
+	function cdiff(topo) {
+            var changed = false;
+            var l_adds = topo.links.diff(links);
+            var l_rems = links.diff2(topo.links);
+
+            var n_adds = topo.nodes.node_diff(nodes);
+            var n_rems = nodes.node_diff(topo.nodes);
+
+            for (var i = 0; i < l_rems.length ; i++) {
+		for (var j = 0; j < links.length; j++) {
+                    if (links[j].source.index == l_rems[i].source.index &&
+			links[j].target.index == l_rems[i].target.index) {
+			links.splice(j,1);
+			changed = true;
+			break;
+                    }
+		}
+            }
+            for (var i = 0; i < l_adds.length; i++) {
+		links.push(l_adds[i]);
+		changed = true;
+            }
+            for (var i = 0; i < n_rems.length; i++) {
+		for (var j = 0; j < nodes.length; j++) {
+		    if ( nodes[j].name == n_rems[i].name ){
+			nodes.splice(j,1);
+			changed = true;
+			break;
+		    }
+		}
+            }
+            for (var i = 0; i < n_adds.length; i++) {
+		nodes.push(n_adds[i]);
+		changed = true;
+            }
+	    return changed
+	}
+
+ 
+
+	var changed = cdiff(json);
+        for (var i = 0; i < json.nodes.length; i++) {
+	    nodes[i].group = json.nodes[i].group
+	}
+
+	console.log(circle);
+
+	console.log("changed?");
+	console.log(changed);
+
+
+	if (changed){
+            path = svg.selectAll("path").data(links)
+            circle = svg.selectAll("circle").data(nodes);
+	    text = svg.selectAll("text").data(nodes);
+
+	    force.stop();
+
+            path.enter().append("svg:path")
+		.attr("class", function(d) { return "link"; })
+		.attr("marker-end", "url(#Triangle)");
+
+            circle.enter().append("svg:circle")
+               .attr("r", 6)
+               .call(force.drag);
+
+/*	    text.enter().append("svg:text")
+		.attr("x", 8)
+		.attr("y", ".31em")
+		.attr("class", "shadow")
+		.text(function(d) { return d.name.split(":")[7]; }); */
+
+	    text.enter().append("svg:text")
+		.attr("x", 8)
+		.attr("y", ".31em")
+		.text(function(d) { return d.name.split(":")[7]; }); 
+
+            circle.append("title")
+	      .text(function(d) { return d.name; });
+
+	    path.exit().remove();
+            circle.exit().remove();
+            text.exit().remove();
+
+	    force.on("tick", tick);
+            force.start();
+	}
+    }
+    function draw(nodes, links){
+        path = svg.append("svg:g").selectAll("path").data(links)
+        circle = svg.append("svg:g").selectAll("circle").data(nodes);
+	text = svg.append("svg:g").selectAll("text").data(nodes);
+
+        path.enter().append("svg:path")
+	    .attr("class", function(d) { return "link"; })
+	    .attr("marker-end", "url(#Triangle)");
+
+        circle.enter().append("svg:circle")
+          .attr("r", 8)
+          .call(force.drag);
+
+/*	text.enter().append("svg:text")
+	    .attr("x", 8)
+	    .attr("y", ".31em")
+	    .attr("class", "shadow")
+	    .text(function(d) { return d.name.split(":")[7]; }); */
+
+	text.enter().append("svg:text")
+	    .attr("x", 8)
+	    .attr("y", ".31em")
+	    .text(function(d) { return d.name.split(":")[7]; }); 
+
+	circle.append("title")
+	    .text(function(d) { return d.name; });
+
+	circle.attr("fill", function(d) { if (d.group == 0){return "blue";}else{ return "gray"; }})
+
+	force.on("tick", tick);
+	path.exit().remove();
+        circle.exit().remove();
+//        text.exit().remove();
+
+	force.start();
+
+	setInterval(function() {
+            $.ajax({
+//		url: 'http://onosnat.onlab.us:8080/topology',
+		url: data_source,
+		success: function(json) {
+		    update(json)
+		},
+		dataType: "json"
+            });
+	}, 3000); 
+    }
+    function tick() {
+	path.attr("d", function(d) {
+	    var dx = d.target.x - d.source.x,
+	    dy = d.target.y - d.source.y,
+	    dr = 1/d.linknum;  //linknum is defined above
+	    dr = 300;
+	    return "M" + d.source.x + "," + d.source.y + "A" + dr + "," + dr + " 0 0,1 " + d.target.x + "," + d.target.y;
+	});
+//	circle.attr("cx", function(d) { return d.x; }).attr("cy", function(d) { return d.y; });
+	circle.attr("transform", function(d) {
+	    return "translate(" + d.x + "," + d.y + ")";
+	})
+	circle.attr("fill", function(d) { if (d.group == 0){return "blue";}else{ return "gray"; }});
+//	text.attr("x", function(d) { return d.x; }).attr("y", function(d) { return d.y; });
+//	text.attr("x", function(d) { return d.x; }).attr("y", function(d) { return d.y; });
+	text.attr("transform", function(d) {
+	    return "translate(" + d.x + "," + d.y + ")";
+	});
+    }
+}
diff --git a/web/onos-topology.html b/web/onos-topology.html
new file mode 100644
index 0000000..1edbaee
--- /dev/null
+++ b/web/onos-topology.html
@@ -0,0 +1,45 @@
+<!DOCTYPE html>
+<html>
+<meta charset="utf-8">
+<style>
+path.link {
+  fill: none;
+  stroke: #666;
+  stroke-width: 1.5px;
+}
+circle {
+  stroke: #333;
+  stroke-width: 1.5px;
+}
+
+text {
+  font: 20px sans-serif;
+  pointer-events: none;
+}
+
+</style>
+<head>
+<title>ONOS GUI</title>
+<script src="http://d3js.org/d3.v3.min.js"></script>
+<script type="text/javascript" src="js/onos-topology.js"></script>
+<script type="text/javascript" src="js/jquery-1.7.2.min.js"></script>
+</head>
+<body>
+<svg width="4in" height="2in" 
+     viewBox="0 0 4000 2000" version="1.1"
+     xmlns="http://www.w3.org/2000/svg">
+  <defs>
+    <marker id="Triangle"
+      viewBox="0 -5 10 10" refX="15" refY="-1.5" 
+      markerUnits="strokeWidth"
+      markerWidth="6" markerHeight="6"
+      orient="auto">
+      <path d="M0,-5L10,0L0,5"/>
+    </marker>
+  </defs>
+<script type="text/javascript">
+gui("http://onosnat.onlab.us:8080/topology");
+</script>
+</svg>
+</body>
+</html>
diff --git a/web/topology_rest.py b/web/topology_rest.py
new file mode 100755
index 0000000..cbb2300
--- /dev/null
+++ b/web/topology_rest.py
@@ -0,0 +1,313 @@
+#! /usr/bin/env python
+import pprint
+import os
+import sys
+import subprocess
+import json
+import argparse
+import io
+import time
+
+from flask import Flask, json, Response, render_template, make_response, request
+
+## Global Var ##
+RestIP="onos1vpc"
+RestPort=8080
+#DBName="onos-network-map"
+
+DEBUG=1
+pp = pprint.PrettyPrinter(indent=4)
+
+app = Flask(__name__)
+
+## Worker Functions ##
+def log_error(txt):
+  print '%s' % (txt)
+
+def debug(txt):
+  if DEBUG:
+    print '%s' % (txt)
+
+## Rest APIs ##
+### File Fetch ###
+@app.route('/ui/img/<filename>', methods=['GET'])
+@app.route('/img/<filename>', methods=['GET'])
+@app.route('/css/<filename>', methods=['GET'])
+@app.route('/js/models/<filename>', methods=['GET'])
+@app.route('/js/views/<filename>', methods=['GET'])
+@app.route('/js/<filename>', methods=['GET'])
+@app.route('/lib/<filename>', methods=['GET'])
+@app.route('/', methods=['GET'])
+@app.route('/<filename>', methods=['GET'])
+@app.route('/tpl/<filename>', methods=['GET'])
+def return_file(filename="index.html"):
+  if request.path == "/":
+    fullpath = "./index.html"
+  else:
+    fullpath = str(request.path)[1:]
+
+  response = make_response(open(fullpath).read())
+  suffix = fullpath.split(".")[-1]
+
+  if suffix == "html" or suffix == "htm":
+    response.headers["Content-type"] = "text/html"
+  elif suffix == "js":
+    response.headers["Content-type"] = "application/javascript"
+  elif suffix == "css":
+    response.headers["Content-type"] = "text/css"
+  elif suffix == "png":
+    response.headers["Content-type"] = "image/png"
+
+  return response
+
+init_topo1 = { 
+  "nodes" : [ 
+    {"name" : "sw0", "group" : 0},
+    {"name" : "sw1", "group" : 0},
+    {"name" : "sw2", "group" : 0},
+    {"name" : "sw3", "group" : 0},
+    {"name" : "sw4", "group" : 0},
+    {"name" : "sw5", "group" : 0},
+    {"name" : "host0", "group" : 1}
+    ],
+  "links" : [
+    {"source" :0, "target": 1},
+    {"source" :1, "target": 0},
+    {"source" :0, "target": 2},
+    {"source" :2, "target": 0},
+    {"source" :1, "target": 3},
+    {"source" :3, "target": 1},
+    {"source" :2, "target": 3},
+    {"source" :3, "target": 2},
+    {"source" :2, "target": 4},
+    {"source" :4, "target": 2},
+    {"source" :3, "target": 5},
+    {"source" :5, "target": 3},
+    {"source" :4, "target": 5},
+    {"source" :5, "target": 4},
+    {"source" :6, "target": 0},
+    {"source" :0, "target": 6}
+    ]
+}
+
+def node_id(switch_array, dpid):
+  id = -1
+  for i, val in enumerate(switch_array):
+    if val['name'] == dpid:
+      id = i
+      break
+
+  return id
+
+@app.route("/topology")
+def topology_for_gui():
+  try:
+    command = "curl -s \'http://%s:%s/wm/core/topology/switches/all/json\'" % (RestIP, RestPort)
+    result = os.popen(command).read()
+    parsedResult = json.loads(result)
+  except:
+    log_error("REST IF has issue: %s" % command)
+    log_error("%s" % result)
+    sys.exit(0)
+
+  topo = {}
+  switches = []
+  links = []
+
+  for v in parsedResult:
+    if v.has_key('dpid'):
+#      if v.has_key('dpid') and str(v['state']) == "ACTIVE":#;if you want only ACTIVE nodes
+      dpid = str(v['dpid'])
+      state = str(v['state'])
+      sw = {}
+      sw['name']=dpid
+      if str(v['state']) == "ACTIVE":
+         sw['group']=0
+      if str(v['state']) == "INACTIVE":
+         sw['group']=1
+
+      switches.append(sw)
+  
+  try:
+    command = "curl -s \'http://%s:%s/wm/core/topology/links/json\'" % (RestIP, RestPort)
+    result = os.popen(command).read()
+    parsedResult = json.loads(result)
+  except:
+    log_error("REST IF has issue: %s" % command)
+    log_error("%s" % result)
+    sys.exit(0)
+
+  for v in parsedResult:
+    link = {}
+    if v.has_key('dst-switch'):
+      dst_dpid = str(v['dst-switch'])
+      dst_id = node_id(switches, dst_dpid)
+    if v.has_key('src-switch'):
+      src_dpid = str(v['src-switch'])
+      src_id = node_id(switches, src_dpid)
+    link['source'] = src_id
+    link['target'] = dst_id
+    links.append(link)
+
+  topo['nodes'] = switches
+  topo['links'] = links
+
+  pp.pprint(topo)
+  js = json.dumps(topo)
+  resp = Response(js, status=200, mimetype='application/json')
+  return resp
+
+
+@app.route("/wm/core/controller/switches/json")
+def query_switch():
+  try:
+    command = "curl -s \'http://%s:%s/wm/core/topology/switches/all/json\'" % (RestIP, RestPort)
+#    http://localhost:8080/wm/core/topology/switches/active/json
+    result = os.popen(command).read()
+    parsedResult = json.loads(result)
+  except:
+    log_error("REST IF has issue: %s" % command)
+    log_error("%s" % result)
+    sys.exit(0)
+
+#  print command
+#  print result
+  switches_ = []
+  for v in parsedResult:
+    if v.has_key('dpid'):
+      if v.has_key('dpid') and str(v['state']) == "ACTIVE":#;if you want only ACTIVE nodes
+        dpid = str(v['dpid'])
+        state = str(v['state'])
+        sw = {}
+        sw['dpid']=dpid
+        sw['active']=state
+        switches_.append(sw)
+
+  pp.pprint(switches_)
+  js = json.dumps(switches_)
+  resp = Response(js, status=200, mimetype='application/json')
+  return resp
+
+@app.route("/wm/device/")
+def devices():
+  try:
+    command = "curl -s http://%s:%s/graphs/%s/vertices\?key=type\&value=device" % (RestIP, RestPort, DBName)
+    result = os.popen(command).read()
+    parsedResult = json.loads(result)['results']
+  except:
+    log_error("REST IF has issue: %s" % command)
+    log_error("%s" % result)
+    sys.exit(0)
+
+  devices = []
+  for v in parsedResult:
+    dl_addr = v['dl_addr']
+    nw_addr = v['nw_addr']
+    vertex = v['_id']
+    mac = []
+    mac.append(dl_addr)
+    ip = []
+    ip.append(nw_addr)
+    device = {}
+    device['entryClass']="DefaultEntryClass"
+    device['mac']=mac
+    device['ipv4']=ip
+    device['vlan']=[]
+    device['lastSeen']=0
+    attachpoints =[]
+
+    port, dpid = deviceV_to_attachpoint(vertex)
+    attachpoint = {}
+    attachpoint['port']=port
+    attachpoint['switchDPID']=dpid
+    attachpoints.append(attachpoint)
+    device['attachmentPoint']=attachpoints
+    devices.append(device)
+
+  print devices
+  js = json.dumps(devices)
+  resp = Response(js, status=200, mimetype='application/json')
+  return resp
+
+#{"entityClass":"DefaultEntityClass","mac":["7c:d1:c3:e0:8c:a3"],"ipv4":["192.168.2.102","10.1.10.35"],"vlan":[],"attachmentPoint":[{"port":13,"switchDPID":"00:01:00:12:e2:78:32:44","errorStatus":null}],"lastSeen":1357333593496}
+
+
+## return fake stat for now
+@app.route("/wm/core/switch/<switchId>/<statType>/json")
+def switch_stat(switchId, statType):
+    if statType == "desc":
+        desc=[{"length":1056,"serialNumber":"None","manufacturerDescription":"Nicira Networks, Inc.","hardwareDescription":"Open vSwitch","softwareDescription":"1.4.0+build0","datapathDescription":"None"}]
+        ret = {}
+        ret[switchId]=desc
+    elif statType == "aggregate":
+        aggr = {"packetCount":0,"byteCount":0,"flowCount":0}
+        ret = {}
+        ret[switchId]=aggr
+    else:
+        ret = {} 
+
+    js = json.dumps(ret)
+    resp = Response(js, status=200, mimetype='application/json')
+    return resp
+
+
+@app.route("/wm/topology/links/json")
+def query_links():
+  try:
+    command = 'curl -s http://%s:%s/graphs/%s/vertices?key=type\&value=port' % (RestIP, RestPort, DBName)
+    result = os.popen(command).read()
+    parsedResult = json.loads(result)['results']
+  except:
+    log_error("REST IF has issue: %s" % command)
+    log_error("%s" % result)
+    sys.exit(0)
+
+  debug("query_links %s" % command)
+  pp.pprint(parsedResult)
+  sport = []
+  links = []
+  for v in parsedResult:
+    srcport = v['_id']
+    try:
+      command = "curl -s http://%s:%s/graphs/%s/vertices/%d/out?_label=link" % (RestIP, RestPort, DBName, srcport)
+      print command
+      result = os.popen(command).read()
+      linkResults = json.loads(result)['results']
+    except:
+      log_error("REST IF has issue: %s" % command)
+      log_error("%s" % result)
+      sys.exit(0)
+
+    for p in linkResults:
+      if p.has_key('type') and p['type'] == "port":
+        dstport = p['_id']
+        (sport, sdpid) = portV_to_port_dpid(srcport)
+        (dport, ddpid) = portV_to_port_dpid(dstport)
+        link = {}
+        link["src-switch"]=sdpid
+        link["src-port"]=sport
+        link["src-port-state"]=0
+        link["dst-switch"]=ddpid
+        link["dst-port"]=dport
+        link["dst-port-state"]=0
+        link["type"]="internal"
+        links.append(link)
+
+  pp.pprint(links)
+  js = json.dumps(links)
+  resp = Response(js, status=200, mimetype='application/json')
+  return resp
+
+if __name__ == "__main__":
+  if len(sys.argv) > 1 and sys.argv[1] == "-d":
+    print "-- query all switches --"
+    query_switch()
+    print "-- query topo --"
+    topology_for_gui()
+#    print "-- query all links --"
+#    query_links()
+#    print "-- query all devices --"
+#    devices()
+  else:
+    app.debug = True
+    app.run(host="0.0.0.0", port=9000)