Merge remote-tracking branch 'origin/master'
diff --git a/clean-cassandra.sh b/clean-cassandra.sh
index a8a88eb..89cb7a4 100755
--- a/clean-cassandra.sh
+++ b/clean-cassandra.sh
@@ -1,7 +1,7 @@
-#! /usr/bin/expect
+#! /usr/bin/expect -f
set timeout 5
spawn ~/apache-cassandra-1.1.4/bin/cassandra-cli
-expect "\[default\@unknown\]"
-send "drop onos;\r"
-expect "\[default@unknown\]"
-send "quit;\r"
+expect "unknown\]\ "
+send "drop keyspace onos;\n"
+expect "unknown\]\ "
+send "quit;\n"
diff --git a/ctrl-add-ext.sh b/ctrl-add-ext-template.sh
similarity index 100%
rename from ctrl-add-ext.sh
rename to ctrl-add-ext-template.sh
diff --git a/demo-scripts/Sprint-4/cleanup.sh b/demo-scripts/Sprint-4/cleanup.sh
new file mode 100755
index 0000000..9e05b43
--- /dev/null
+++ b/demo-scripts/Sprint-4/cleanup.sh
@@ -0,0 +1,28 @@
+#! /bin/sh
+CLUSTER=/home/masayosi/bin/hosts-3x3.txt
+function ilink_up {
+ echo "add link at $2"
+ n=`dsh -w $1 "sudo tc qdisc show dev $2" | grep netem | wc -l`
+ if [ $n -eq 1 ]; then
+ echo "dsh -w $1 sudo tc qdisc change dev $2 root netem loss 0%"
+ dsh -w $1 "sudo tc qdisc change dev $2 root netem loss 0%"
+ else
+ echo "dsh -w $1 sudo tc qdisc add dev $2 root netem loss 0%"
+ dsh -w $1 "sudo tc qdisc add dev $2 root netem loss 0%"
+ fi
+ echo "done"
+}
+
+ilink_up onos9vpc tapa0
+ilink_up onos10vpc tapb0
+ilink_up onos10vpc tapb1
+ilink_up onos11vpc tapc0
+
+#echo "stopping mininet"
+#dsh -g onos 'sudo mn -c'
+echo "stopping ONOS"
+dsh -g onos 'cd ONOS; ./start-onos.sh stop'
+echo "stopping Cassandra"
+dsh -g onos 'cd ONOS; ./start-cassandra.sh stop'
+echo "Removing Cassandra DB"
+dsh -g onos 'sudo rm -rf /var/lib/cassandra/*'
diff --git a/demo-scripts/Sprint-4/demo-prep.sh b/demo-scripts/Sprint-4/demo-prep.sh
new file mode 100755
index 0000000..80c3aa4
--- /dev/null
+++ b/demo-scripts/Sprint-4/demo-prep.sh
@@ -0,0 +1,69 @@
+#! /bin/bash
+export CLUSTER=/home/masayosi/bin/hosts-3x3.txt
+function cmd {
+while [ 1 ] ; do
+ $@
+ echo "Continue? [y/n]:"
+ read -t 2 value
+ if [ x$value == "xy" ]; then
+ break;
+ fi
+ sleep 1
+done
+}
+
+./start-onos.sh 'onos9vpc,onos10vpc,onos11vpc,onos12vpc' stop
+./start-onos.sh 'onos9vpc,onos10vpc,onos11vpc,onos12vpc' status
+#./start-cassandra.sh 'onos9vpc' start
+#sleep 2
+#./start-cassandra.sh 'onos10vpc,onos11vpc,onos12vpc' start
+
+dsh -w onos9vpc 'cd ONOS;./clean-cassandra.sh'
+cmd ./start-cassandra.sh onos9vpc status
+sleep 5
+echo "Set mininet local"
+dsh -g onos 'ONOS/ctrl-none.sh'
+echo "Start ONOS on 9, 10 and 12"
+./start-onos.sh 'onos9vpc' start
+sleep 1
+./start-onos.sh 'onos10vpc' start
+sleep 1
+./start-onos.sh 'onos12vpc' start
+while [ 1 ] ; do
+ ./start-onos.sh 'onos9vpc,onos10vpc,onos12vpc' status
+ echo "Continue? [y/s/n]:"
+ read -t 2 value
+ if [ x$value == "xy" ]; then
+ break;
+ elif [ x$value == "xs" ]; then
+ ./start-onos.sh 'onos9vpc,onos10vpc,onos12vpc' start
+ fi
+ sleep 1
+done
+sleep 2
+dsh -g onos 'ONOS/ctrl-local.sh'
+#sleep 2
+#./start-onos.sh 'onos9vpc,onos10vpc,onos12vpc' start
+while [ 1 ] ; do
+ ./start-onos.sh 'onos9vpc,onos10vpc,onos12vpc' status
+ echo "Continue? [y/s/n]:"
+ read -t 2 value
+ if [ x$value == "xy" ]; then
+ break;
+ elif [ x$value == "xs" ]; then
+ ./start-onos.sh 'onos9vpc,onos10vpc,onos12vpc' start
+ fi
+ sleep 1
+done
+#cmd dsh -w onos9vpc,onos10vpc 'ONOS/ctrl-add-ext.sh'
+#while [ 1 ] ; do
+# ./start-onos.sh 'onos9vpc,onos10vpc,onos12vpc' status
+# echo "Continue? [y/n]:"
+# read -t 2 value
+# if [ x$value == "xy" ]; then
+# break;
+# fi
+# sleep 1
+#done
+#dsh -w onos9vpc,onos10vpc 'ONOS/ctrl-add-ext.sh'
+#sleep 1
diff --git a/demo-scripts/Sprint-4/demo.sh b/demo-scripts/Sprint-4/demo.sh
new file mode 100755
index 0000000..a835daf
--- /dev/null
+++ b/demo-scripts/Sprint-4/demo.sh
@@ -0,0 +1,43 @@
+#! /bin/bash
+export CLUSTER=/home/masayosi/bin/hosts-3x3.txt
+function cmd {
+while [ 1 ] ; do
+ $@
+ echo "Continue? [y/n]:"
+ read -t 2 value
+ if [ x$value == "xy" ]; then
+ break;
+ fi
+ sleep 1
+done
+}
+
+dsh -w onos9vpc,onos10vpc 'ONOS/ctrl-add-ext.sh'
+sleep 5
+
+echo "start ONOS on onos11vpc"
+read
+./start-onos.sh onos11vpc start
+echo "done"
+
+echo "kill ONOS on onos9"
+read
+./start-onos.sh onos9vpc stop
+echo "done"
+
+echo "kill ONOS on onos11"
+read
+dsh -w onos11vpc 'ONOS/ctrl-add-ext.sh'
+sleep 1
+./start-onos.sh onos11vpc stop
+echo "done"
+
+echo "bring back ONOS on onos9 and onos11"
+read
+./start-onos.sh 'onos9vpc,onos11vpc' start
+echo "done"
+
+echo "kill ONOS on onos10"
+read
+./start-onos.sh onos10vpc stop
+echo "done"
diff --git a/demo-scripts/Sprint-4/network-change.sh b/demo-scripts/Sprint-4/network-change.sh
new file mode 100755
index 0000000..4fd49f9
--- /dev/null
+++ b/demo-scripts/Sprint-4/network-change.sh
@@ -0,0 +1,138 @@
+#! /bin/sh
+
+function Wait {
+ echo "press ret> "
+ read
+}
+
+function port_down {
+ echo "Taking down $2 port $3"
+ Wait
+ dsh -w $1 "sudo ifconfig $2-$3 down"
+ echo "done"
+}
+function port_up {
+ echo "bring up $2 port $3"
+ Wait
+ dsh -w $1 "sudo ifconfig $2-$3 up"
+ echo "done"
+}
+
+function port_change {
+ port_down $1 $2 $3
+ port_up $1 $2 $3
+}
+
+function switch_remove {
+ echo "taking switch $2 out"
+ Wait
+ dsh -w $1 "sudo ovs-vsctl set-controller $2 tcp:127.0.0.1:6639"
+ echo "done"
+}
+
+function switch_back {
+ echo "taking switch $2 back"
+ Wait
+ ctrl="tcp:127.0.0.1:6633"
+ dsh -w $1 "sudo ovs-vsctl set-controller $2 $ctrl"
+ echo "done"
+}
+function switch_go_back {
+ switch_remove $1 $2
+ switch_back $1 $2
+}
+
+function link_down {
+ echo "remove link from sw $2 port $3"
+ n=`dsh -w $1 "sudo tc qdisc show dev $2-$3" | grep netem | wc -l`
+ if [ $n -eq 1 ]; then
+ echo "dsh -w $1 sudo tc qdisc change dev $2-$3 root netem loss 100%"
+ dsh -w $1 "sudo tc qdisc change dev $2-$3 root netem loss 100%"
+ else
+ echo "dsh -w $1 sudo tc qdisc add dev $2-$3 root netem 100%"
+ dsh -w $1 "sudo tc qdisc add dev $2-$3 root netem loss 100%"
+ fi
+ echo "done"
+}
+function link_up {
+ echo "add link from sw $2 port $3"
+ n=`dsh -w $1 "sudo tc qdisc show dev $2-$3" | grep netem | wc -l`
+ if [ $n -eq 1 ]; then
+ echo "dsh -w $1 sudo tc qdisc change dev $2-$3 root netem loss 0%"
+ dsh -w $1 "sudo tc qdisc change dev $2-$3 root netem loss 0%"
+ else
+ echo "dsh -w $1 sudo tc qdisc add dev $2-$3 root netem loss 0%"
+ dsh -w $1 "sudo tc qdisc add dev $2-$3 root netem loss 0%"
+ fi
+ echo "done"
+}
+function link_change {
+ link_down $1 $2 $3
+ link_up $1 $2 $3
+}
+
+function ilink_down {
+ echo "remove link from $2"
+ n=`dsh -w $1 "sudo tc qdisc show dev $2" | grep netem | wc -l`
+ if [ $n -eq 1 ]; then
+ echo "dsh -w $1 sudo tc qdisc change dev $2 root netem loss 100%"
+ dsh -w $1 "sudo tc qdisc change dev $2 root netem loss 100%"
+ else
+ echo "dsh -w $1 sudo tc qdisc add dev $2 root netem 100%"
+ dsh -w $1 "sudo tc qdisc add dev $2 root netem loss 100%"
+ fi
+ echo "done"
+}
+function ilink_up {
+ echo "add link at $2"
+ n=`dsh -w $1 "sudo tc qdisc show dev $2" | grep netem | wc -l`
+ if [ $n -eq 1 ]; then
+ echo "dsh -w $1 sudo tc qdisc change dev $2 root netem loss 0%"
+ dsh -w $1 "sudo tc qdisc change dev $2 root netem loss 0%"
+ else
+ echo "dsh -w $1 sudo tc qdisc add dev $2 root netem loss 0%"
+ dsh -w $1 "sudo tc qdisc add dev $2 root netem loss 0%"
+ fi
+ echo "done"
+}
+function ilink_change {
+ ilink_down $1 $2
+ ilink_up $1 $2
+}
+
+
+#port_change onos9vpc swa1 eth2
+#port_change onos10vpc swb3 eth4
+#port_change onos11vpc swc5 eth2
+
+#switch_go_back onos9vpc swa1
+#switch_go_back onos10vpc swb3
+#switch_go_back onos11vpc swc3
+
+#echo "link down between swa4 and swa3"
+#Wait
+#link_down onos9vpc swa4 eth2
+#link_down onos9vpc swa3 eth4
+#echo "link up between swa4 and swa3"
+Wait
+link_up onos9vpc swa3 eth4
+link_up onos9vpc swa4 eth2
+
+echo "link down between swb4 and swb3"
+Wait
+link_down onos10vpc swb4 eth2
+link_down onos10vpc swb3 eth4
+echo "link down between swb4 and swb3"
+Wait
+link_up onos10vpc swb3 eth4
+link_up onos10vpc swb4 eth2
+
+echo "link down between network 1 and network2"
+Wait
+ilink_down onos9vpc tapa0
+ilink_down onos10vpc tapb0
+
+echo "link up between network 1 and network2"
+Wait
+ilink_up onos10vpc tapb0
+ilink_up onos9vpc tapa0
diff --git a/demo-scripts/Sprint-4/start-cassandra.sh b/demo-scripts/Sprint-4/start-cassandra.sh
new file mode 100755
index 0000000..860a320
--- /dev/null
+++ b/demo-scripts/Sprint-4/start-cassandra.sh
@@ -0,0 +1,6 @@
+#! /bin/sh
+if [ $# != 2 ]; then
+ echo "$0 server cmd"
+fi
+CLUSTER=/home/masayosi/bin/hosts-3x3.txt
+dsh -w $1 "cd ONOS; sudo ./start-cassandra.sh $2"
diff --git a/demo-scripts/Sprint-4/start-onos.sh b/demo-scripts/Sprint-4/start-onos.sh
new file mode 100755
index 0000000..3c90e87
--- /dev/null
+++ b/demo-scripts/Sprint-4/start-onos.sh
@@ -0,0 +1,6 @@
+#! /bin/sh
+if [ $# != 2 ]; then
+ echo "$0 server cmd"
+fi
+CLUSTER=/home/masayosi/bin/hosts-3x3.txt
+dsh -w $1 "cd ONOS; sudo ./start-onos.sh $2"
diff --git a/demo-scripts/Sprint-4/update.sh b/demo-scripts/Sprint-4/update.sh
new file mode 100755
index 0000000..30c9391
--- /dev/null
+++ b/demo-scripts/Sprint-4/update.sh
@@ -0,0 +1,4 @@
+#! /bin/sh
+CLUSTER=/home/masayosi/bin/hosts-3x3.txt
+
+dsh -g onos 'cd ONOS; git pull; ant'
diff --git a/onos.properties b/onos.properties
deleted file mode 100644
index 498fce5..0000000
--- a/onos.properties
+++ /dev/null
@@ -1,19 +0,0 @@
-floodlight.modules = net.floodlightcontroller.storage.memory.MemoryStorageSource,\
-net.floodlightcontroller.core.FloodlightProvider,\
-net.floodlightcontroller.threadpool.ThreadPool,\
-net.floodlightcontroller.devicemanager.internal.DeviceManagerImpl,\
-net.floodlightcontroller.staticflowentry.StaticFlowEntryPusher,\
-net.floodlightcontroller.firewall.Firewall,\
-net.floodlightcontroller.forwarding.Forwarding,\
-net.floodlightcontroller.jython.JythonDebugInterface,\
-net.floodlightcontroller.counter.CounterStore,\
-net.floodlightcontroller.perfmon.PktInProcessingTime,\
-net.floodlightcontroller.ui.web.StaticWebRoutable,\
-net.floodlightcontroller.onoslistener.OnosPublisher, \
-net.onrc.onos.registry.controller.ZookeeperRegistry
-net.floodlightcontroller.restserver.RestApiServer.port = 8080
-net.floodlightcontroller.core.FloodlightProvider.openflowport = 6633
-net.floodlightcontroller.jython.JythonDebugInterface.port = 6655
-net.floodlightcontroller.forwarding.Forwarding.idletimeout = 5
-net.floodlightcontroller.forwarding.Forwarding.hardtimeout = 0
-net.floodlightcontroller.onoslistener.OnosPublisher.dbconf = /tmp/cassandra.titan
diff --git a/src/main/java/net/floodlightcontroller/bgproute/BgpRoute.java b/src/main/java/net/floodlightcontroller/bgproute/BgpRoute.java
new file mode 100644
index 0000000..2819253
--- /dev/null
+++ b/src/main/java/net/floodlightcontroller/bgproute/BgpRoute.java
@@ -0,0 +1,181 @@
+package net.floodlightcontroller.bgproute;
+
+import java.util.Collection;
+import java.util.Map;
+import java.util.ArrayList;
+import java.util.HashMap;
+
+import net.floodlightcontroller.core.module.FloodlightModuleContext;
+import net.floodlightcontroller.core.module.FloodlightModuleException;
+import net.floodlightcontroller.core.module.IFloodlightModule;
+import net.floodlightcontroller.core.module.IFloodlightService;
+import net.floodlightcontroller.core.IFloodlightProviderService;
+
+import net.floodlightcontroller.linkdiscovery.ILinkDiscovery.LDUpdate;
+import net.floodlightcontroller.restserver.IRestApiService;
+import net.floodlightcontroller.topology.ITopologyListener;
+import net.floodlightcontroller.topology.ITopologyService;
+import net.floodlightcontroller.restclient.RestClient;
+
+import net.floodlightcontroller.linkdiscovery.ILinkDiscovery;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class BgpRoute implements IFloodlightModule, IBgpRouteService, ITopologyListener {
+
+ protected static Logger log = LoggerFactory.getLogger(BgpRoute.class);
+
+ protected IFloodlightProviderService floodlightProvider;
+ protected ITopologyService topology;
+
+ protected static Ptree ptree;
+
+ @Override
+ public Collection<Class<? extends IFloodlightService>> getModuleServices() {
+ Collection<Class<? extends IFloodlightService>> l = new ArrayList<Class<? extends IFloodlightService>>();
+ l.add(IBgpRouteService.class);
+ return l;
+ }
+
+ @Override
+ public Map<Class<? extends IFloodlightService>, IFloodlightService> getServiceImpls() {
+ Map<Class<? extends IFloodlightService>, IFloodlightService> m = new HashMap<Class<? extends IFloodlightService>, IFloodlightService>();
+ m.put(IBgpRouteService.class, this);
+ return m;
+ }
+
+ protected IRestApiService restApi;
+
+ @Override
+ public Collection<Class<? extends IFloodlightService>> getModuleDependencies() {
+ Collection<Class<? extends IFloodlightService>> l = new ArrayList<Class<? extends IFloodlightService>>();
+ l.add(IFloodlightProviderService.class);
+ l.add(ITopologyService.class);
+ l.add(IBgpRouteService.class);
+ return l;
+ }
+
+ @Override
+ public void init(FloodlightModuleContext context)
+ throws FloodlightModuleException {
+
+ ptree = new Ptree(32);
+
+ // Register floodlight provider and REST handler.
+ floodlightProvider = context.getServiceImpl(IFloodlightProviderService.class);
+ restApi = context.getServiceImpl(IRestApiService.class);
+ topology = context.getServiceImpl(ITopologyService.class);
+
+ // Test.
+ //test();
+ }
+
+ public Ptree getPtree() {
+ return ptree;
+ }
+
+ // Return nexthop address as byte array.
+ public Rib lookupRib(byte[] dest) {
+ if (ptree == null) {
+ log.debug("lookupRib: ptree null");
+ return null;
+ }
+
+ PtreeNode node = ptree.match(dest, 32);
+ if (node == null) {
+ log.debug("lookupRib: ptree node null");
+ return null;
+ }
+ if (node.rib == null) {
+ log.debug("lookupRib: ptree rib null");
+ return null;
+ }
+ ptree.delReference(node);
+
+ return node.rib;
+ }
+
+ @SuppressWarnings("unused")
+ private void test() {
+ System.out.println("Here it is");
+ Prefix p = new Prefix("128.0.0.0", 8);
+ Prefix q = new Prefix("8.0.0.0", 8);
+ Prefix r = new Prefix("10.0.0.0", 24);
+ Prefix a = new Prefix("10.0.0.1", 32);
+
+ ptree.acquire(p.getAddress(), p.masklen);
+ ptree.acquire(q.getAddress(), q.masklen);
+ ptree.acquire(r.getAddress(), r.masklen);
+
+ System.out.println("Traverse start");
+ for (PtreeNode node = ptree.begin(); node != null; node = ptree.next(node)) {
+ Prefix p_result = new Prefix(node.key, node.keyBits);
+ }
+
+ PtreeNode n = ptree.match(a.getAddress(), a.masklen);
+ if (n != null) {
+ System.out.println("Matched prefix for 10.0.0.1:");
+ Prefix x = new Prefix(n.key, n.keyBits);
+ ptree.delReference(n);
+ }
+
+ n = ptree.lookup(p.getAddress(), p.masklen);
+ if (n != null) {
+ ptree.delReference(n);
+ ptree.delReference(n);
+ }
+ System.out.println("Traverse start");
+ for (PtreeNode node = ptree.begin(); node != null; node = ptree.next(node)) {
+ Prefix p_result = new Prefix(node.key, node.keyBits);
+ }
+
+ n = ptree.lookup(q.getAddress(), q.masklen);
+ if (n != null) {
+ ptree.delReference(n);
+ ptree.delReference(n);
+ }
+ System.out.println("Traverse start");
+ for (PtreeNode node = ptree.begin(); node != null; node = ptree.next(node)) {
+ Prefix p_result = new Prefix(node.key, node.keyBits);
+ }
+
+ n = ptree.lookup(r.getAddress(), r.masklen);
+ if (n != null) {
+ ptree.delReference(n);
+ ptree.delReference(n);
+ }
+ System.out.println("Traverse start");
+ for (PtreeNode node = ptree.begin(); node != null; node = ptree.next(node)) {
+ Prefix p_result = new Prefix(node.key, node.keyBits);
+ }
+
+ }
+
+ @Override
+ public void startUp(FloodlightModuleContext context) {
+ restApi.addRestletRoutable(new BgpRouteWebRoutable());
+ topology.addListener((ITopologyListener) this);
+ }
+
+ @Override
+ public void topologyChanged() {
+ boolean change = false;
+ String changelog = "";
+
+ for (LDUpdate ldu : topology.getLastLinkUpdates()) {
+ if (ldu.getOperation().equals(ILinkDiscovery.UpdateOperation.PORT_DOWN)) {
+ change = true;
+ changelog = changelog + " down ";
+ } else if (ldu.getOperation().equals(ILinkDiscovery.UpdateOperation.PORT_UP)) {
+ change = true;
+ changelog = changelog + " up ";
+ }
+ }
+ log.info ("received topo change" + changelog);
+
+ if (change) {
+ RestClient.get ("http://localhost:5000/topo_change");
+ }
+ }
+}
diff --git a/src/main/java/net/floodlightcontroller/bgproute/BgpRouteResource.java b/src/main/java/net/floodlightcontroller/bgproute/BgpRouteResource.java
new file mode 100644
index 0000000..d5abb5a
--- /dev/null
+++ b/src/main/java/net/floodlightcontroller/bgproute/BgpRouteResource.java
@@ -0,0 +1,139 @@
+package net.floodlightcontroller.bgproute;
+
+import org.restlet.resource.Get;
+import org.restlet.resource.Post;
+import org.restlet.resource.Delete;
+import org.restlet.resource.ServerResource;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import net.floodlightcontroller.restclient.RestClient;
+
+public class BgpRouteResource extends ServerResource {
+
+ protected static Logger log = LoggerFactory
+ .getLogger(BgpRouteResource.class);
+
+ private String addrToString(byte [] addr) {
+ String str = "";
+
+ for (int i = 0; i < 4; i++) {
+ int val = (addr[i] & 0xff);
+ str += val;
+ if (i != 3)
+ str += ".";
+ }
+
+ return str;
+ }
+
+ @SuppressWarnings("unused")
+ @Get
+ public String get(String fmJson) {
+ String dest = (String) getRequestAttributes().get("dest");
+ String output = "";
+ IBgpRouteService bgpRoute = (IBgpRouteService)getContext().getAttributes().
+ get(IBgpRouteService.class.getCanonicalName());
+
+ if (dest != null) {
+ Prefix p = new Prefix(dest, 32);
+ if (p == null) {
+ return "[GET]: dest address format is wrong";
+ }
+ byte [] nexthop = bgpRoute.lookupRib(p.getAddress()).nextHop.getAddress();
+ if (nexthop != null) {
+ output += "{\"result\": \"" + addrToString(nexthop) + "\"}\n";
+ } else {
+ output += "{\"result\": \"Nexthop does not exist\"}\n";
+ }
+ } else {
+ Ptree ptree = bgpRoute.getPtree();
+ output += "{\n \"rib\": [\n";
+ boolean printed = false;
+ for (PtreeNode node = ptree.begin(); node != null; node = ptree.next(node)) {
+ if (node.rib == null) {
+ continue;
+ }
+ if (printed == true) {
+ output += ",\n";
+ }
+ output += " {\"prefix\": \"" + addrToString(node.key) + "/" + node.keyBits +"\", ";
+ output += "\"nexthop\": \"" + addrToString(node.rib.nextHop.getAddress()) +"\"}";
+ printed = true;
+ }
+ //output += "{\"router_id\": \"" + addrToString(node.rib.routerId.getAddress()) +"\"}\n";
+ output += "\n ]\n}\n";
+ }
+
+ return output;
+ }
+ @Post
+ public String store(String fmJson) {
+ IBgpRouteService bgpRoute = (IBgpRouteService)getContext().getAttributes().
+ get(IBgpRouteService.class.getCanonicalName());
+
+ Ptree ptree = bgpRoute.getPtree();
+
+ String router_id = (String) getRequestAttributes().get("routerid");
+ String prefix = (String) getRequestAttributes().get("prefix");
+ String mask = (String) getRequestAttributes().get("mask");
+ String nexthop = (String) getRequestAttributes().get("nexthop");
+ String capability = (String) getRequestAttributes().get("capability");
+ String reply = null;
+
+ if (capability == null) {
+ // this is a prefix add
+ Prefix p = new Prefix(prefix, Integer.valueOf(mask));
+ PtreeNode node = ptree.acquire(p.getAddress(), p.masklen);
+ Rib rib = new Rib(router_id, nexthop, p.masklen);
+
+ if (node.rib != null) {
+ node.rib = null;
+ ptree.delReference(node);
+ }
+ node.rib = rib;
+
+ reply = "[POST: " + prefix + "/" + mask + ":" + nexthop + "]";
+ log.info(reply);
+
+ RestClient.get("http://localhost:5000/bgp_update");
+ }
+
+ return reply + "\n";
+ }
+
+ @Delete
+ public String delete(String fmJson) {
+ IBgpRouteService bgpRoute = (IBgpRouteService)getContext().getAttributes().
+ get(IBgpRouteService.class.getCanonicalName());
+
+ Ptree ptree = bgpRoute.getPtree();
+
+ String routerId = (String) getRequestAttributes().get("routerid");
+ String prefix = (String) getRequestAttributes().get("prefix");
+ String mask = (String) getRequestAttributes().get("mask");
+ String nextHop = (String) getRequestAttributes().get("nexthop");
+ String capability = (String) getRequestAttributes().get("capability");
+ String reply = null;
+
+ if (capability == null) {
+ // this is a prefix delete
+ Prefix p = new Prefix(prefix, Integer.valueOf(mask));
+ PtreeNode node = ptree.lookup(p.getAddress(), p.masklen);
+ Rib r = new Rib(routerId, nextHop, p.masklen);
+
+ if (node != null && node.rib != null) {
+ if (r.equals(node.rib)) {
+ node.rib = null;
+ ptree.delReference(node);
+ }
+ }
+
+ reply = "[DELE: " + prefix + "/" + mask + ":" + nextHop + "]";
+ log.info(reply);
+
+ RestClient.get("http://localhost:5000/bgp_update");
+ }
+
+ return reply + "\n";
+ }
+}
diff --git a/src/main/java/net/floodlightcontroller/bgproute/BgpRouteWebRoutable.java b/src/main/java/net/floodlightcontroller/bgproute/BgpRouteWebRoutable.java
new file mode 100644
index 0000000..37d5696
--- /dev/null
+++ b/src/main/java/net/floodlightcontroller/bgproute/BgpRouteWebRoutable.java
@@ -0,0 +1,24 @@
+package net.floodlightcontroller.bgproute;
+
+import org.restlet.Context;
+import org.restlet.Restlet;
+import org.restlet.routing.Router;
+
+import net.floodlightcontroller.restserver.RestletRoutable;
+
+public class BgpRouteWebRoutable implements RestletRoutable {
+ @Override
+ public Restlet getRestlet(Context context) {
+ Router router = new Router(context);
+ router.attach("/json", BgpRouteResource.class);
+ router.attach("/rib/{dest}", BgpRouteResource.class);
+ router.attach("/{routerid}/{prefix}/{mask}/{nexthop}", BgpRouteResource.class);
+ router.attach("/{routerid}/{capability}", BgpRouteResource.class);
+ return router;
+ }
+
+ @Override
+ public String basePath() {
+ return "/wm/bgp";
+ }
+}
diff --git a/src/main/java/net/floodlightcontroller/bgproute/IBgpRouteService.java b/src/main/java/net/floodlightcontroller/bgproute/IBgpRouteService.java
new file mode 100644
index 0000000..62bdf5e
--- /dev/null
+++ b/src/main/java/net/floodlightcontroller/bgproute/IBgpRouteService.java
@@ -0,0 +1,11 @@
+package net.floodlightcontroller.bgproute;
+
+import net.floodlightcontroller.core.module.IFloodlightService;
+
+public interface IBgpRouteService extends IFloodlightService {
+
+ public Rib lookupRib(byte[] dest);
+
+ public Ptree getPtree();
+
+}
diff --git a/src/main/java/net/floodlightcontroller/bgproute/Prefix.java b/src/main/java/net/floodlightcontroller/bgproute/Prefix.java
new file mode 100644
index 0000000..7ba014b
--- /dev/null
+++ b/src/main/java/net/floodlightcontroller/bgproute/Prefix.java
@@ -0,0 +1,35 @@
+package net.floodlightcontroller.bgproute;
+
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+
+public class Prefix {
+ public int masklen;
+ protected InetAddress address;
+
+ Prefix(byte[] addr, int masklen) {
+ try {
+ address = InetAddress.getByAddress(addr);
+ } catch (UnknownHostException e) {
+ System.out.println("InetAddress exception");
+ return;
+ }
+ this.masklen = masklen;
+ System.out.println(address.toString() + "/" + masklen);
+ }
+
+ Prefix(String str, int masklen) {
+ try {
+ address = InetAddress.getByName(str);
+ //System.out.println(address.toString());
+ } catch (UnknownHostException e) {
+ System.out.println("InetAddress exception");
+ return;
+ }
+ this.masklen = masklen;
+ }
+
+ public byte [] getAddress() {
+ return address.getAddress();
+ }
+}
diff --git a/src/main/java/net/floodlightcontroller/bgproute/Ptree.java b/src/main/java/net/floodlightcontroller/bgproute/Ptree.java
new file mode 100644
index 0000000..d53789e
--- /dev/null
+++ b/src/main/java/net/floodlightcontroller/bgproute/Ptree.java
@@ -0,0 +1,316 @@
+package net.floodlightcontroller.bgproute;
+
+public class Ptree {
+ int maxKeyBits;
+ int maxKeyOctets;
+ int refCount;
+ PtreeNode top;
+ byte maskBits[] = { (byte)0x00, (byte)0x80, (byte)0xc0, (byte)0xe0, (byte)0xf0, (byte)0xf8, (byte)0xfc, (byte)0xfe, (byte)0xff };
+
+ // Constructor.
+ Ptree(int max_key_bits) {
+ maxKeyBits = max_key_bits;
+ maxKeyOctets = bit_to_octet(max_key_bits);
+ refCount = 0;
+ }
+
+ public PtreeNode acquire(byte [] key) {
+ return acquire(key, maxKeyBits);
+ }
+
+ public PtreeNode acquire(byte [] key, int key_bits) {
+ if (key_bits > maxKeyBits) {
+ return null;
+ }
+
+ PtreeNode node = top;
+ PtreeNode match = null;
+
+ while (node != null
+ && node.keyBits <= key_bits
+ && key_match(node.key, node.keyBits, key, key_bits) == true) {
+ if (node.keyBits == key_bits) {
+ return addReference(node);
+ }
+
+ match = node;
+
+ if (bit_check(key, node.keyBits) == true) {
+ node = node.right;
+ } else {
+ node = node.left;
+ }
+ }
+
+ PtreeNode add = null;
+
+ if (node == null) {
+ add = new PtreeNode(key, key_bits, maxKeyOctets);
+
+ if (match != null) {
+ node_link(match, add);
+ } else {
+ top = add;
+ }
+ } else {
+ add = node_common(node, key, key_bits);
+ if (add == null) {
+ return null;
+ }
+
+ if (match != null) {
+ node_link(match, add);
+ } else {
+ top = add;
+ }
+ node_link(add, node);
+
+ if (add.keyBits != key_bits) {
+ match = add;
+
+ add = new PtreeNode(key, key_bits, maxKeyOctets);
+ node_link(match, add);
+ }
+ }
+
+ return addReference(add);
+ }
+
+ public PtreeNode lookup(byte [] key, int key_bits) {
+ if (key_bits > maxKeyBits) {
+ return null;
+ }
+
+ PtreeNode node = top;
+
+ while (node != null
+ && node.keyBits <= key_bits
+ && key_match(node.key, node.keyBits, key, key_bits) == true) {
+ if (node.keyBits == key_bits) {
+ return addReference(node);
+ }
+
+ if (bit_check(key, node.keyBits) == true) {
+ node = node.right;
+ } else {
+ node = node.left;
+ }
+ }
+ return null;
+ }
+
+ public PtreeNode match(byte [] key, int key_bits) {
+ if (key_bits > maxKeyBits) {
+ return null;
+ }
+ PtreeNode node = top;
+ PtreeNode matched = null;
+
+ if(node!=null)
+
+ while (node != null
+ && node.keyBits <= key_bits
+ && key_match(node.key, node.keyBits, key, key_bits) == true) {
+ matched = node;
+
+ if (bit_check(key, node.keyBits) == true) {
+ node = node.right;
+ } else {
+ node = node.left;
+ }
+ }
+
+ if (matched != null) {
+ return addReference(matched);
+ }
+
+ return null;
+ }
+
+ public PtreeNode begin() {
+ if (top == null) {
+ return null;
+ }
+ return addReference(top);
+ }
+
+ public PtreeNode next(PtreeNode node) {
+ PtreeNode next;
+
+ if (node.left != null) {
+ next = node.left;
+ addReference(next);
+ delReference(node);
+ return next;
+ }
+ if (node.right != null) {
+ next = node.right;
+ addReference(next);
+ delReference(node);
+ return next;
+ }
+
+ PtreeNode start = node;
+ while (node.parent != null) {
+ if (node.parent.left == node && node.parent.right != null) {
+ next = node.parent.right;
+ addReference(next);
+ delReference(start);
+ return next;
+ }
+ node = node.parent;
+ }
+
+ delReference(start);
+
+ return null;
+ }
+
+ static public int bit_to_octet(int key_bits) {
+ return Math.max((key_bits + 7) / 8, 1);
+ }
+
+ private PtreeNode addReference(PtreeNode node) {
+ node.refCount++;
+ return node;
+ }
+
+ public void delReference(PtreeNode node) {
+ if (node.refCount > 0) {
+ node.refCount--;
+ }
+ if (node.refCount == 0) {
+ node_remove(node);
+ }
+ }
+
+ private boolean key_match(byte [] key1, int key1_len, byte [] key2, int key2_len) {
+ int offset;
+ int shift;
+
+ if (key1_len > key2_len) {
+ return false;
+ }
+
+ offset = (Math.min(key1_len, key2_len)) / 8;
+ shift = (Math.min(key1_len, key2_len)) % 8;
+
+ if (shift != 0) {
+ if ((maskBits[shift] & (key1[offset] ^ key2[offset])) != 0) {
+ return false;
+ }
+ }
+
+ while (offset != 0) {
+ offset--;
+ if (key1[offset] != key2[offset]) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ private boolean bit_check(byte [] key, int key_bits) {
+ int offset = key_bits / 8;
+ int shift = 7 - (key_bits % 8);
+ int bit = key[offset] & 0xff;
+
+ bit >>= shift;
+
+ if ((bit & 1) == 1) {
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ private void node_link(PtreeNode node, PtreeNode add) {
+ boolean bit = bit_check(add.key, node.keyBits);
+
+ if (bit == true) {
+ node.right = add;
+ } else {
+ node.left = add;
+ }
+ add.parent = node;
+ }
+
+ @SuppressWarnings("unused")
+ private PtreeNode node_common(PtreeNode node, byte [] key, int key_bits) {
+ int i;
+ int limit = Math.min(node.keyBits, key_bits) / 8;
+
+ for (i = 0; i < limit; i++) {
+ if (node.key[i] != key[i]) {
+ break;
+ }
+ }
+
+ int common_len = i * 8;
+ int boundary = 0;
+
+ if (common_len != key_bits) {
+ byte diff = (byte)(node.key[i] ^ key[i]);
+ byte mask = (byte)0x80;
+ int shift_mask = 0;
+
+ while (common_len < key_bits && ((mask & diff) == 0)) {
+ boundary = 1;
+
+ shift_mask = (mask & 0xff);
+ shift_mask >>= 1;
+ mask = (byte)shift_mask;
+
+ common_len++;
+ }
+ }
+
+ PtreeNode add = new PtreeNode(null, common_len, maxKeyOctets);
+ if (add == null)
+ return null;
+
+ int j;
+ for (j = 0; j < i; j++)
+ add.key[j] = node.key[j];
+
+ if (boundary != 0)
+ add.key[j] = (byte)(node.key[j] & maskBits[add.keyBits % 8]);
+
+ return add;
+ }
+
+ private void node_remove(PtreeNode node) {
+ PtreeNode child;
+ PtreeNode parent;
+
+ if (node.left != null && node.right != null) {
+ return;
+ }
+
+ if (node.left != null) {
+ child = node.left;
+ } else {
+ child = node.right;
+ }
+
+ parent = node.parent;
+
+ if (child != null) {
+ child.parent = parent;
+ }
+
+ if (parent != null) {
+ if (parent.left == node) {
+ parent.left = child;
+ } else {
+ parent.right = child;
+ }
+ } else {
+ top = child;
+ }
+
+ if (parent != null && parent.refCount == 0) {
+ node_remove(parent);
+ }
+ }
+}
diff --git a/src/main/java/net/floodlightcontroller/bgproute/PtreeNode.java b/src/main/java/net/floodlightcontroller/bgproute/PtreeNode.java
new file mode 100644
index 0000000..4d2c265
--- /dev/null
+++ b/src/main/java/net/floodlightcontroller/bgproute/PtreeNode.java
@@ -0,0 +1,44 @@
+package net.floodlightcontroller.bgproute;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class PtreeNode {
+ public PtreeNode parent;
+ public PtreeNode left;
+ public PtreeNode right;
+
+ public byte key[];
+ public int keyBits;
+
+ public int refCount;
+
+ public Rib rib;
+ protected static Logger log = LoggerFactory.getLogger(BgpRoute.class);
+
+ PtreeNode(byte [] key, int key_bits, int max_key_octet) {
+ parent = null;
+ left = null;
+ right = null;
+ refCount = 0;
+ rib = null;
+ this.key = new byte[max_key_octet];
+ this.keyBits = key_bits;
+ log.debug("inside Ptreenode constructor key {} bits {}", key, key_bits);
+
+ int octet = Ptree.bit_to_octet(key_bits);
+ for (int i = 0; i < max_key_octet; i++) {
+ if (i < octet) {
+ if (key != null) {
+ log.debug(octet + ": filling key[{}] {}", i, key[i]);
+ this.key[i] = key[i];
+ } else {
+ log.debug("no filling, null key", i);
+ }
+ } else {
+ log.debug("filling key {} as 0", i);
+ this.key[i] = 0;
+ }
+ }
+ }
+}
diff --git a/src/main/java/net/floodlightcontroller/bgproute/Rib.java b/src/main/java/net/floodlightcontroller/bgproute/Rib.java
new file mode 100644
index 0000000..71868ff
--- /dev/null
+++ b/src/main/java/net/floodlightcontroller/bgproute/Rib.java
@@ -0,0 +1,45 @@
+package net.floodlightcontroller.bgproute;
+
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+
+public class Rib {
+ protected InetAddress routerId;
+ protected InetAddress nextHop;
+ protected int masklen;
+// protected int distance;
+
+ Rib(InetAddress router_id, InetAddress nexthop, int masklen) {
+ this.routerId = router_id;
+ this.nextHop = nexthop;
+ this.masklen = masklen;
+// this.distance = distance;
+ }
+
+ Rib(String router_id, String nexthop, int masklen) {
+ try {
+ this.routerId = InetAddress.getByName(router_id);
+ } catch (UnknownHostException e) {
+ System.out.println("InetAddress exception");
+ }
+ try {
+ this.nextHop = InetAddress.getByName(nexthop);
+ } catch (UnknownHostException e) {
+ System.out.println("InetAddress exception");
+ }
+ this.masklen = masklen;
+ }
+
+ public InetAddress getNextHop() {
+ return nextHop;
+ }
+
+ public int getMasklen(){
+ return masklen;
+ }
+
+ public boolean equals(Rib r) {
+
+ return this.routerId == r.routerId && this.nextHop == r.nextHop && this.masklen == r.masklen;
+ }
+}
diff --git a/src/main/java/net/floodlightcontroller/core/INetMapTopologyObjects.java b/src/main/java/net/floodlightcontroller/core/INetMapTopologyObjects.java
index 4af1deb..8788aa7 100644
--- a/src/main/java/net/floodlightcontroller/core/INetMapTopologyObjects.java
+++ b/src/main/java/net/floodlightcontroller/core/INetMapTopologyObjects.java
@@ -132,4 +132,101 @@
@GremlinGroovy("_().in('host').in('on').path(){it.number}{it.dpid}")
public Iterable<SwitchPort> getAttachmentPoints();*/
}
+
+public interface IFlowPath extends IBaseObject {
+ @Property("flow_id")
+ public String getFlowId();
+
+ @Property("flow_id")
+ public void setFlowId(String flowId);
+
+ @Property("installer_id")
+ public String getInstallerId();
+
+ @Property("installer_id")
+ public void setInstallerId(String installerId);
+
+ @Property("src_switch")
+ public String getSrcSwitch();
+
+ @Property("src_switch")
+ public void setSrcSwitch(String srcSwitch);
+
+ @Property("src_port")
+ public Short getSrcPort();
+
+ @Property("src_port")
+ public void setSrcPort(Short srcPort);
+
+ @Property("dst_switch")
+ public String getDstSwitch();
+
+ @Property("dst_switch")
+ public void setDstSwitch(String dstSwitch);
+
+ @Property("dst_port")
+ public Short getDstPort();
+
+ @Property("dst_port")
+ public void setDstPort(Short dstPort);
+
+ @Adjacency(label="flow", direction=Direction.IN)
+ public Iterable<IFlowEntry> getFlowEntries();
+
+ @Adjacency(label="flow", direction=Direction.IN)
+ public void addFlowEntry(final IFlowEntry flowEntry);
+
+ @Adjacency(label="flow", direction=Direction.IN)
+ public void removeFlowEntry(final IFlowEntry flowEntry);
+ }
+
+public interface IFlowEntry extends IBaseObject {
+ @Property("flow_entry_id")
+ public String getFlowEntryId();
+
+ @Property("flow_entry_id")
+ public void setFlowEntryId(String flowEntryId);
+
+ @Property("switch_dpid")
+ public String getSwitchDpid();
+
+ @Property("switch_dpid")
+ public void setSwitchDpid(String switchDpid);
+
+ @Property("in_port")
+ public Short getInPort();
+
+ @Property("in_port")
+ public void setInPort(Short inPort);
+
+ @Property("out_port")
+ public Short getOutPort();
+
+ @Property("out_port")
+ public void setOutPort(Short outPort);
+
+ @Property("user_state")
+ public String getUserState();
+
+ @Property("user_state")
+ public void setUserState(String userState);
+
+ @Property("switch_state")
+ public String getSwitchState();
+
+ @Property("switch_state")
+ public void setSwitchState(String switchState);
+
+ @Property("error_state_type")
+ public String getErrorStateType();
+
+ @Property("error_state_type")
+ public void setErrorStateType(String errorStateType);
+
+ @Property("error_state_code")
+ public String getErrorStateCode();
+
+ @Property("error_state_code")
+ public void setErrorStateCode(String errorStateCode);
+ }
}
diff --git a/src/main/java/net/floodlightcontroller/core/internal/Controller.java b/src/main/java/net/floodlightcontroller/core/internal/Controller.java
index 3e657d1..d4a44fc 100644
--- a/src/main/java/net/floodlightcontroller/core/internal/Controller.java
+++ b/src/main/java/net/floodlightcontroller/core/internal/Controller.java
@@ -489,36 +489,25 @@
Role role = null;
- if (sw.getRole() == null){
- if (hasControl){
- role = Role.MASTER;
- }
- else {
- role = Role.SLAVE;
- }
- }
- else if (hasControl && sw.getRole() == Role.SLAVE) {
- // Send a MASTER role request to the switch.
- // If this is the first role request,
- // this is a probe that we'll use to determine if the switch
- // actually supports the role request message. If it does we'll
- // get back a role reply message. If it doesn't we'll get back an
- // OFError message.
- // If role is MASTER we will promote switch to active
- // list when we receive the switch's role reply messages
+ /*
+ * issue #229
+ * Cannot rely on sw.getRole() as it can be behind due to pending
+ * role changes in the queue. Just submit it and late the RoleChanger
+ * handle duplicates.
+ */
+
+ if (hasControl){
role = Role.MASTER;
}
- else if (!hasControl && sw.getRole() == Role.MASTER) {
- //Send a SLAVE role request to the switch
+ else {
role = Role.SLAVE;
}
-
- if (role != null) {
- log.debug("Sending role request {} msg to {}", role, sw);
- Collection<OFSwitchImpl> swList = new ArrayList<OFSwitchImpl>(1);
- swList.add(sw);
- roleChanger.submitRequest(swList, role);
- }
+
+ log.debug("Sending role request {} msg to {}", role, sw);
+ Collection<OFSwitchImpl> swList = new ArrayList<OFSwitchImpl>(1);
+ swList.add(sw);
+ roleChanger.submitRequest(swList, role);
+
}
}
diff --git a/src/main/java/net/floodlightcontroller/core/internal/SwitchStorageImpl.java b/src/main/java/net/floodlightcontroller/core/internal/SwitchStorageImpl.java
index 3039679..6be26cb 100644
--- a/src/main/java/net/floodlightcontroller/core/internal/SwitchStorageImpl.java
+++ b/src/main/java/net/floodlightcontroller/core/internal/SwitchStorageImpl.java
@@ -58,6 +58,7 @@
}
} catch (TitanException e) {
// TODO: handle exceptions
+ graph.stopTransaction(Conclusion.FAILURE);
log.info("SwitchStorage:setStatus dpid:{} state: {} failed", dpid, state);
}
@@ -98,6 +99,7 @@
}
} catch (TitanException e) {
// TODO: handle exceptions
+ graph.stopTransaction(Conclusion.FAILURE);
log.error("SwitchStorage:addPort dpid:{} port:{} failed", dpid, port.getPortNumber());
}
@@ -149,6 +151,7 @@
/*
* retry till we succeed?
*/
+ graph.stopTransaction(Conclusion.FAILURE);
log.info("SwitchStorage:addSwitch dpid:{} failed", dpid);
}
@@ -168,6 +171,7 @@
}
} catch (TitanException e) {
// TODO: handle exceptions
+ graph.stopTransaction(Conclusion.FAILURE);
log.error("SwitchStorage:deleteSwitch {} failed", dpid);
}
@@ -190,6 +194,7 @@
}
} catch (TitanException e) {
// TODO: handle exceptions
+ graph.stopTransaction(Conclusion.FAILURE);
log.info("SwitchStorage:deletePort dpid:{} port:{} failed", dpid, port);
}
}
diff --git a/src/main/java/net/floodlightcontroller/flowcache/FlowManager.java b/src/main/java/net/floodlightcontroller/flowcache/FlowManager.java
index 12ed505..8cf1982 100644
--- a/src/main/java/net/floodlightcontroller/flowcache/FlowManager.java
+++ b/src/main/java/net/floodlightcontroller/flowcache/FlowManager.java
@@ -1,10 +1,22 @@
package net.floodlightcontroller.flowcache;
+import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
+import java.util.EnumSet;
import java.util.HashMap;
+import java.util.List;
import java.util.Map;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.ScheduledFuture;
+import java.util.concurrent.TimeUnit;
+import net.floodlightcontroller.core.IFloodlightProviderService;
+import net.floodlightcontroller.core.INetMapStorage;
+import net.floodlightcontroller.core.INetMapTopologyObjects.IFlowEntry;
+import net.floodlightcontroller.core.INetMapTopologyObjects.IFlowPath;
+import net.floodlightcontroller.core.IOFSwitch;
import net.floodlightcontroller.core.module.FloodlightModuleContext;
import net.floodlightcontroller.core.module.FloodlightModuleException;
import net.floodlightcontroller.core.module.IFloodlightModule;
@@ -13,20 +25,178 @@
import net.floodlightcontroller.flowcache.web.FlowWebRoutable;
import net.floodlightcontroller.restserver.IRestApiService;
import net.floodlightcontroller.util.CallerId;
+import net.floodlightcontroller.util.DataPath;
+import net.floodlightcontroller.util.Dpid;
import net.floodlightcontroller.util.DataPathEndpoints;
+import net.floodlightcontroller.util.FlowEntry;
+import net.floodlightcontroller.util.FlowEntryId;
+import net.floodlightcontroller.util.FlowEntrySwitchState;
+import net.floodlightcontroller.util.FlowEntryUserState;
import net.floodlightcontroller.util.FlowId;
import net.floodlightcontroller.util.FlowPath;
+import net.floodlightcontroller.util.OFMessageDamper;
+import net.floodlightcontroller.util.Port;
+import net.onrc.onos.util.GraphDBConnection;
+import net.onrc.onos.util.GraphDBConnection.Transaction;
+
+import org.openflow.protocol.OFFlowMod;
+import org.openflow.protocol.OFMatch;
+import org.openflow.protocol.OFPacketOut;
+import org.openflow.protocol.OFType;
+import org.openflow.protocol.action.OFAction;
+import org.openflow.protocol.action.OFActionOutput;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-public class FlowManager implements IFloodlightModule, IFlowService {
+public class FlowManager implements IFloodlightModule, IFlowService, INetMapStorage {
+
+ public GraphDBConnection conn;
protected IRestApiService restApi;
+ protected IFloodlightProviderService floodlightProvider;
+
+ protected OFMessageDamper messageDamper;
+
+ protected static int OFMESSAGE_DAMPER_CAPACITY = 50000; // TODO: find sweet spot
+ protected static int OFMESSAGE_DAMPER_TIMEOUT = 250; // ms
+ public static short FLOWMOD_DEFAULT_IDLE_TIMEOUT = 0; // infinity
+ public static short FLOWMOD_DEFAULT_HARD_TIMEOUT = 0; // infinite
/** The logger. */
- private static Logger logger =
- LoggerFactory.getLogger(FlowManager.class);
+ private static Logger log = LoggerFactory.getLogger(FlowManager.class);
+
+ // The periodic task(s)
+ private final ScheduledExecutorService scheduler =
+ Executors.newScheduledThreadPool(1);
+ final Runnable reader = new Runnable() {
+ public void run() {
+ // log.debug("Reading Flow Entries from the Network Map...");
+ if (floodlightProvider == null) {
+ log.debug("FloodlightProvider service not found!");
+ return;
+ }
+
+ Map<Long, IOFSwitch> mySwitches = floodlightProvider.getSwitches();
+
+ // Fetch all Flow Entries
+ Iterable<IFlowEntry> flowEntries = conn.utils().getAllFlowEntries(conn);
+ for (IFlowEntry flowEntryObj : flowEntries) {
+ FlowEntryId flowEntryId =
+ new FlowEntryId(flowEntryObj.getFlowEntryId());
+ String userState = flowEntryObj.getUserState();
+ String switchState = flowEntryObj.getSwitchState();
+
+ log.debug("Found Flow Entry {}: ", flowEntryId.toString());
+ log.debug("User State {}:", userState);
+ log.debug("Switch State {}:", switchState);
+
+ if (! switchState.equals("FE_SWITCH_NOT_UPDATED")) {
+ // Ignore the entry: nothing to do
+ continue;
+ }
+
+ Dpid dpid = new Dpid(flowEntryObj.getSwitchDpid());
+ IOFSwitch mySwitch = mySwitches.get(dpid.value());
+ if (mySwitch == null) {
+ log.debug("Flow Entry ignored: not my switch");
+ continue;
+ }
+
+ //
+ // Create the Open Flow Flow Modification Entry to push
+ //
+ OFFlowMod fm =
+ (OFFlowMod) floodlightProvider.getOFMessageFactory()
+ .getMessage(OFType.FLOW_MOD);
+ long cookie = flowEntryId.value();
+
+ short flowModCommand = OFFlowMod.OFPFC_ADD;
+ if (userState.equals("FE_USER_ADD")) {
+ flowModCommand = OFFlowMod.OFPFC_ADD;
+ } else if (userState.equals("FE_USER_MODIFY")) {
+ flowModCommand = OFFlowMod.OFPFC_MODIFY_STRICT;
+ } else if (userState.equals("FE_USER_DELETE")) {
+ flowModCommand = OFFlowMod.OFPFC_DELETE_STRICT;
+ } else {
+ // Unknown user state. Ignore the entry
+ continue;
+ }
+
+ OFMatch match = new OFMatch();
+ match.setInputPort(flowEntryObj.getInPort());
+
+ OFActionOutput action = new OFActionOutput();
+ action.setMaxLength((short)0xffff);
+ action.setPort(flowEntryObj.getOutPort());
+ List<OFAction> actions = new ArrayList<OFAction>();
+ actions.add(action);
+
+ fm.setIdleTimeout(FLOWMOD_DEFAULT_IDLE_TIMEOUT)
+ .setHardTimeout(FLOWMOD_DEFAULT_HARD_TIMEOUT)
+ .setBufferId(OFPacketOut.BUFFER_ID_NONE)
+ .setCookie(cookie)
+ .setCommand(flowModCommand)
+ .setMatch(match)
+ .setActions(actions)
+ .setLengthU(OFFlowMod.MINIMUM_LENGTH+OFActionOutput.MINIMUM_LENGTH);
+ //
+ // TODO: Set the following flag
+ // fm.setFlags(OFFlowMod.OFPFF_SEND_FLOW_REM);
+ // See method ForwardingBase::pushRoute()
+ //
+ try {
+ messageDamper.write(mySwitch, fm, null);
+ mySwitch.flush();
+ flowEntryObj.setSwitchState("FE_SWITCH_UPDATED");
+ if (userState.equals("FE_USER_DELETE")) {
+ // Delete the entry
+ IFlowPath flowObj = null;
+ flowObj = conn.utils().getFlowPathByFlowEntry(conn,
+ flowEntryObj);
+ if (flowObj != null)
+ log.debug("Found FlowPath to be deleted");
+ else
+ log.debug("Did not find FlowPath to be deleted");
+ flowObj.removeFlowEntry(flowEntryObj);
+ conn.utils().removeFlowEntry(conn, flowEntryObj);
+
+ // Test whether the last flow entry
+ Iterable<IFlowEntry> tmpflowEntries =
+ flowObj.getFlowEntries();
+ boolean found = false;
+ for (IFlowEntry tmpflowEntryObj : tmpflowEntries) {
+ found = true;
+ break;
+ }
+ if (! found) {
+ // Remove the Flow Path as well
+ conn.utils().removeFlowPath(conn, flowObj);
+ }
+ }
+ } catch (IOException e) {
+ log.error("Failure writing flow mod from network map", e);
+ }
+ }
+ conn.endTx(Transaction.COMMIT);
+ }
+ };
+ final ScheduledFuture<?> readerHandle =
+ scheduler.scheduleAtFixedRate(reader, 3, 3, TimeUnit.SECONDS);
+
+ @Override
+ public void init(String conf) {
+ conn = GraphDBConnection.getInstance(conf);
+ }
+
+ public void finalize() {
+ close();
+ }
+
+ @Override
+ public void close() {
+ conn.close();
+ }
@Override
public Collection<Class<? extends IFloodlightService>> getModuleServices() {
@@ -52,6 +222,7 @@
getModuleDependencies() {
Collection<Class<? extends IFloodlightService>> l =
new ArrayList<Class<? extends IFloodlightService>>();
+ l.add(IFloodlightProviderService.class);
l.add(IRestApiService.class);
return l;
}
@@ -59,7 +230,14 @@
@Override
public void init(FloodlightModuleContext context)
throws FloodlightModuleException {
+ floodlightProvider = context.getServiceImpl(IFloodlightProviderService.class);
restApi = context.getServiceImpl(IRestApiService.class);
+ messageDamper = new OFMessageDamper(OFMESSAGE_DAMPER_CAPACITY,
+ EnumSet.of(OFType.FLOW_MOD),
+ OFMESSAGE_DAMPER_TIMEOUT);
+ // TODO: An ugly hack!
+ String conf = "/tmp/cassandra.titan";
+ this.init(conf);
}
@Override
@@ -79,7 +257,135 @@
*/
@Override
public boolean addFlow(FlowPath flowPath, FlowId flowId) {
- // TODO
+
+ //
+ // Assign the FlowEntry IDs
+ // TODO: This is an ugly hack!
+ // The Flow Entry IDs are set to 1000*FlowId + Index
+ //
+ int i = 1;
+ for (FlowEntry flowEntry : flowPath.dataPath().flowEntries()) {
+ long id = flowPath.flowId().value() * 1000 + i;
+ ++i;
+ flowEntry.setFlowEntryId(new FlowEntryId(id));
+ }
+
+ IFlowPath flowObj = null;
+ try {
+ if ((flowObj = conn.utils().searchFlowPath(conn, flowPath.flowId()))
+ != null) {
+ log.debug("Adding FlowPath with FlowId {}: found existing FlowPath",
+ flowPath.flowId().toString());
+ } else {
+ flowObj = conn.utils().newFlowPath(conn);
+ log.debug("Adding FlowPath with FlowId {}: creating new FlowPath",
+ flowPath.flowId().toString());
+ }
+ } catch (Exception e) {
+ // TODO: handle exceptions
+ conn.endTx(Transaction.ROLLBACK);
+ log.error(":addFlow FlowId:{} failed",
+ flowPath.flowId().toString());
+ }
+ if (flowObj == null)
+ return false;
+
+ //
+ // Set the Flow key:
+ // - flowId
+ //
+ flowObj.setFlowId(flowPath.flowId().toString());
+ flowObj.setType("flow");
+
+ //
+ // Set the Flow attributes:
+ // - flowPath.installerId()
+ // - flowPath.dataPath().srcPort()
+ // - flowPath.dataPath().dstPort()
+ //
+ flowObj.setInstallerId(flowPath.installerId().toString());
+ flowObj.setSrcSwitch(flowPath.dataPath().srcPort().dpid().toString());
+ flowObj.setSrcPort(flowPath.dataPath().srcPort().port().value());
+ flowObj.setDstSwitch(flowPath.dataPath().dstPort().dpid().toString());
+ flowObj.setDstPort(flowPath.dataPath().dstPort().port().value());
+
+ // Flow edges:
+ // HeadFE
+
+
+ //
+ // Flow Entries:
+ // flowPath.dataPath().flowEntries()
+ //
+ for (FlowEntry flowEntry : flowPath.dataPath().flowEntries()) {
+ IFlowEntry flowEntryObj = null;
+ boolean found = false;
+ try {
+ if ((flowEntryObj = conn.utils().searchFlowEntry(conn, flowEntry.flowEntryId())) != null) {
+ log.debug("Adding FlowEntry with FlowEntryId {}: found existing FlowEntry",
+ flowEntry.flowEntryId().toString());
+ found = true;
+ } else {
+ flowEntryObj = conn.utils().newFlowEntry(conn);
+ log.debug("Adding FlowEntry with FlowEntryId {}: creating new FlowEntry",
+ flowEntry.flowEntryId().toString());
+ }
+ } catch (Exception e) {
+ // TODO: handle exceptions
+ conn.endTx(Transaction.ROLLBACK);
+ log.error(":addFlow FlowEntryId:{} failed",
+ flowEntry.flowEntryId().toString());
+ }
+ if (flowEntryObj == null)
+ return false;
+
+ //
+ // Set the Flow Entry key:
+ // - flowEntry.flowEntryId()
+ //
+ flowEntryObj.setFlowEntryId(flowEntry.flowEntryId().toString());
+ flowEntryObj.setType("flow_entry");
+
+ //
+ // Set the Flow Entry attributes:
+ // - flowEntry.flowEntryMatch()
+ // - flowEntry.flowEntryActions()
+ // - flowEntry.dpid()
+ // - flowEntry.inPort()
+ // - flowEntry.outPort()
+ // - flowEntry.flowEntryUserState()
+ // - flowEntry.flowEntrySwitchState()
+ // - flowEntry.flowEntryErrorState()
+ //
+ flowEntryObj.setSwitchDpid(flowEntry.dpid().toString());
+ flowEntryObj.setInPort(flowEntry.inPort().value());
+ flowEntryObj.setOutPort(flowEntry.outPort().value());
+ // TODO: Hacks with hard-coded state names!
+ if (found)
+ flowEntryObj.setUserState("FE_USER_MODIFY");
+ else
+ flowEntryObj.setUserState("FE_USER_ADD");
+ flowEntryObj.setSwitchState("FE_SWITCH_NOT_UPDATED");
+ //
+ // TODO: Take care of the FlowEntryMatch, FlowEntryAction set,
+ // and FlowEntryErrorState.
+ //
+
+ // Flow Entries edges:
+ // Flow
+ // NextFE
+ // InPort
+ // OutPort
+ // Switch
+ if (! found)
+ flowObj.addFlowEntry(flowEntryObj);
+ }
+ conn.endTx(Transaction.COMMIT);
+
+ //
+ // TODO: We need a proper Flow ID allocation mechanism.
+ //
+ flowId.setValue(flowPath.flowId().value());
return true;
}
@@ -91,7 +397,46 @@
*/
@Override
public boolean deleteFlow(FlowId flowId) {
- // TODO
+ IFlowPath flowObj = null;
+ //
+ // We just mark the entries for deletion,
+ // and let the switches remove each individual entry after
+ // it has been removed from the switches.
+ //
+ try {
+ if ((flowObj = conn.utils().searchFlowPath(conn, flowId))
+ != null) {
+ log.debug("Deleting FlowPath with FlowId {}: found existing FlowPath",
+ flowId.toString());
+ } else {
+ log.debug("Deleting FlowPath with FlowId {}: FlowPath not found",
+ flowId.toString());
+ }
+ } catch (Exception e) {
+ // TODO: handle exceptions
+ conn.endTx(Transaction.ROLLBACK);
+ log.error(":deleteFlow FlowId:{} failed", flowId.toString());
+ }
+ if (flowObj == null)
+ return true; // OK: No such flow
+
+ //
+ // Find and mark for deletion all Flow Entries
+ //
+ Iterable<IFlowEntry> flowEntries = flowObj.getFlowEntries();
+ boolean empty = true; // TODO: an ugly hack
+ for (IFlowEntry flowEntryObj : flowEntries) {
+ empty = false;
+ // flowObj.removeFlowEntry(flowEntryObj);
+ // conn.utils().removeFlowEntry(conn, flowEntryObj);
+ flowEntryObj.setUserState("FE_USER_DELETE");
+ flowEntryObj.setSwitchState("FE_SWITCH_NOT_UPDATED");
+ }
+ // Remove from the database empty flows
+ if (empty)
+ conn.utils().removeFlowPath(conn, flowObj);
+ conn.endTx(Transaction.COMMIT);
+
return true;
}
@@ -99,51 +444,215 @@
* Get a previously added flow.
*
* @param flowId the Flow ID of the flow to get.
- * @param flowPath the return-by-reference flow path.
- * @return true on success, otherwise false.
+ * @return the Flow Path if found, otherwise null.
*/
@Override
- public boolean getFlow(FlowId flowId, FlowPath flowPath) {
- // TODO
- return true;
+ public FlowPath getFlow(FlowId flowId) {
+ IFlowPath flowObj = null;
+ try {
+ if ((flowObj = conn.utils().searchFlowPath(conn, flowId))
+ != null) {
+ log.debug("Get FlowPath with FlowId {}: found existing FlowPath",
+ flowId.toString());
+ } else {
+ log.debug("Get FlowPath with FlowId {}: FlowPath not found",
+ flowId.toString());
+ }
+ } catch (Exception e) {
+ // TODO: handle exceptions
+ conn.endTx(Transaction.ROLLBACK);
+ log.error(":getFlow FlowId:{} failed", flowId.toString());
+ }
+ if (flowObj == null)
+ return null; // Flow not found
+
+ //
+ // Extract the Flow state
+ //
+ FlowPath flowPath = extractFlowPath(flowObj);
+ conn.endTx(Transaction.COMMIT);
+
+ return flowPath;
}
/**
- * Get a previously added flow by a specific installer for given
+ * Get all previously added flows by a specific installer for a given
* data path endpoints.
*
* @param installerId the Caller ID of the installer of the flow to get.
* @param dataPathEndpoints the data path endpoints of the flow to get.
- * @param flowPath the return-by-reference flow path.
- * @return true on success, otherwise false.
+ * @return the Flow Paths if found, otherwise null.
*/
@Override
- public boolean getFlow(CallerId installerId,
- DataPathEndpoints dataPathEndpoints,
- FlowPath flowPath) {
- // TODO
- return true;
+ public ArrayList<FlowPath> getAllFlows(CallerId installerId,
+ DataPathEndpoints dataPathEndpoints) {
+ //
+ // TODO: The implementation below is not optimal:
+ // We fetch all flows, and then return only the subset that match
+ // the query conditions.
+ // We should use the appropriate Titan/Gremlin query to filter-out
+ // the flows as appropriate.
+ //
+ ArrayList<FlowPath> allFlows = getAllFlows();
+
+ if (allFlows == null) {
+ log.debug("Get FlowPaths for installerId{} and dataPathEndpoints{}: no FlowPaths found", installerId, dataPathEndpoints);
+ return null;
+ }
+
+ ArrayList<FlowPath> flowPaths = new ArrayList<FlowPath>();
+ for (FlowPath flow : allFlows) {
+ //
+ // TODO: String-based comparison is sub-optimal.
+ // We are using it for now to save us the extra work of
+ // implementing the "equals()" and "hashCode()" methods.
+ //
+ if (! flow.installerId().toString().equals(installerId.toString()))
+ continue;
+ if (! flow.dataPath().srcPort().toString().equals(dataPathEndpoints.srcPort().toString())) {
+ continue;
+ }
+ if (! flow.dataPath().dstPort().toString().equals(dataPathEndpoints.dstPort().toString())) {
+ continue;
+ }
+ flowPaths.add(flow);
+ }
+
+ if (flowPaths.isEmpty()) {
+ log.debug("Get FlowPaths for installerId{} and dataPathEndpoints{}: no FlowPaths found", installerId, dataPathEndpoints);
+ flowPaths = null;
+ } else {
+ log.debug("Get FlowPaths for installerId{} and dataPathEndpoints{}: FlowPaths are found", installerId, dataPathEndpoints);
+ }
+
+ return flowPaths;
}
/**
* Get all installed flows by all installers for given data path endpoints.
*
* @param dataPathEndpoints the data path endpoints of the flows to get.
- * @param flowPaths the return-by-reference list of flows.
+ * @return the Flow Paths if found, otherwise null.
*/
@Override
- public void getAllFlows(DataPathEndpoints dataPathEndpoints,
- ArrayList<FlowPath> flowPaths) {
- // TODO
+ public ArrayList<FlowPath> getAllFlows(DataPathEndpoints dataPathEndpoints) {
+ //
+ // TODO: The implementation below is not optimal:
+ // We fetch all flows, and then return only the subset that match
+ // the query conditions.
+ // We should use the appropriate Titan/Gremlin query to filter-out
+ // the flows as appropriate.
+ //
+ ArrayList<FlowPath> allFlows = getAllFlows();
+
+ if (allFlows == null) {
+ log.debug("Get FlowPaths for dataPathEndpoints{}: no FlowPaths found", dataPathEndpoints);
+ return null;
+ }
+
+ ArrayList<FlowPath> flowPaths = new ArrayList<FlowPath>();
+ for (FlowPath flow : allFlows) {
+ //
+ // TODO: String-based comparison is sub-optimal.
+ // We are using it for now to save us the extra work of
+ // implementing the "equals()" and "hashCode()" methods.
+ //
+ if (! flow.dataPath().srcPort().toString().equals(dataPathEndpoints.srcPort().toString())) {
+ continue;
+ }
+ if (! flow.dataPath().dstPort().toString().equals(dataPathEndpoints.dstPort().toString())) {
+ continue;
+ }
+ flowPaths.add(flow);
+ }
+
+ if (flowPaths.isEmpty()) {
+ log.debug("Get FlowPaths for dataPathEndpoints{}: no FlowPaths found", dataPathEndpoints);
+ flowPaths = null;
+ } else {
+ log.debug("Get FlowPaths for dataPathEndpoints{}: FlowPaths are found", dataPathEndpoints);
+ }
+
+ return flowPaths;
}
/**
* Get all installed flows by all installers.
*
- * @param flowPaths the return-by-reference list of flows.
+ * @return the Flow Paths if found, otherwise null.
*/
@Override
- public void getAllFlows(ArrayList<FlowPath> flowPaths) {
- // TODO
+ public ArrayList<FlowPath> getAllFlows() {
+ Iterable<IFlowPath> flowPathsObj = null;
+
+ try {
+ if ((flowPathsObj = conn.utils().getAllFlowPaths(conn)) != null) {
+ log.debug("Get all FlowPaths: found FlowPaths");
+ } else {
+ log.debug("Get all FlowPaths: no FlowPaths found");
+ }
+ } catch (Exception e) {
+ // TODO: handle exceptions
+ conn.endTx(Transaction.ROLLBACK);
+ log.error(":getAllFlowPaths failed");
+ }
+ if ((flowPathsObj == null) || (flowPathsObj.iterator().hasNext() == false))
+ return null; // No Flows found
+
+ ArrayList<FlowPath> flowPaths = new ArrayList<FlowPath>();
+ for (IFlowPath flowObj : flowPathsObj) {
+ //
+ // Extract the Flow state
+ //
+ FlowPath flowPath = extractFlowPath(flowObj);
+ flowPaths.add(flowPath);
+ }
+
+ conn.endTx(Transaction.COMMIT);
+
+ return flowPaths;
+ }
+
+ /**
+ * Extract Flow Path State from a Titan Database Object @ref IFlowPath.
+ *
+ * @param flowObj the object to extract the Flow Path State from.
+ * @return the extracted Flow Path State.
+ */
+ private FlowPath extractFlowPath(IFlowPath flowObj) {
+ FlowPath flowPath = new FlowPath();
+
+ //
+ // Extract the Flow state
+ //
+ flowPath.setFlowId(new FlowId(flowObj.getFlowId()));
+ flowPath.setInstallerId(new CallerId(flowObj.getInstallerId()));
+ flowPath.dataPath().srcPort().setDpid(new Dpid(flowObj.getSrcSwitch()));
+ flowPath.dataPath().srcPort().setPort(new Port(flowObj.getSrcPort()));
+ flowPath.dataPath().dstPort().setDpid(new Dpid(flowObj.getDstSwitch()));
+ flowPath.dataPath().dstPort().setPort(new Port(flowObj.getDstPort()));
+
+ //
+ // Extract all Flow Entries
+ //
+ Iterable<IFlowEntry> flowEntries = flowObj.getFlowEntries();
+ for (IFlowEntry flowEntryObj : flowEntries) {
+ FlowEntry flowEntry = new FlowEntry();
+ flowEntry.setFlowEntryId(new FlowEntryId(flowEntryObj.getFlowEntryId()));
+ flowEntry.setDpid(new Dpid(flowEntryObj.getSwitchDpid()));
+ flowEntry.setInPort(new Port(flowEntryObj.getInPort()));
+ flowEntry.setOutPort(new Port(flowEntryObj.getOutPort()));
+ String userState = flowEntryObj.getUserState();
+ flowEntry.setFlowEntryUserState(FlowEntryUserState.valueOf(userState));
+ String switchState = flowEntryObj.getSwitchState();
+ flowEntry.setFlowEntrySwitchState(FlowEntrySwitchState.valueOf(switchState));
+ //
+ // TODO: Take care of the FlowEntryMatch, FlowEntryAction set,
+ // and FlowEntryErrorState.
+ //
+ flowPath.dataPath().flowEntries().add(flowEntry);
+ }
+
+ return flowPath;
}
}
diff --git a/src/main/java/net/floodlightcontroller/flowcache/IFlowService.java b/src/main/java/net/floodlightcontroller/flowcache/IFlowService.java
index 956caab..b159661 100644
--- a/src/main/java/net/floodlightcontroller/flowcache/IFlowService.java
+++ b/src/main/java/net/floodlightcontroller/flowcache/IFlowService.java
@@ -36,37 +36,33 @@
* Get a previously added flow.
*
* @param flowId the Flow ID of the flow to get.
- * @param flowPath the return-by-reference flow path.
- * @return true on success, otherwise false.
+ * @return the Flow Path if found, otherwise null.
*/
- boolean getFlow(FlowId flowId, FlowPath flowPath);
+ FlowPath getFlow(FlowId flowId);
/**
- * Get a previously added flow by a specific installer for given
+ * Get all previously added flows by a specific installer for a given
* data path endpoints.
*
* @param installerId the Caller ID of the installer of the flow to get.
* @param dataPathEndpoints the data path endpoints of the flow to get.
- * @param flowPath the return-by-reference flow path.
- * @return true on success, otherwise false.
+ * @return the Flow Paths if found, otherwise null.
*/
- boolean getFlow(CallerId installerId,
- DataPathEndpoints dataPathEndpoints,
- FlowPath flowPath);
+ ArrayList<FlowPath> getAllFlows(CallerId installerId,
+ DataPathEndpoints dataPathEndpoints);
/**
* Get all installed flows by all installers for given data path endpoints.
*
* @param dataPathEndpoints the data path endpoints of the flows to get.
- * @param flowPaths the return-by-reference list of flows.
+ * @return the Flow Paths if found, otherwise null.
*/
- void getAllFlows(DataPathEndpoints dataPathEndpoints,
- ArrayList<FlowPath> flowPaths);
+ ArrayList<FlowPath> getAllFlows(DataPathEndpoints dataPathEndpoints);
/**
* Get all installed flows by all installers.
*
- * @param flowPaths the return-by-reference list of flows.
+ * @return the Flow Paths if found, otherwise null.
*/
- void getAllFlows(ArrayList<FlowPath> flowPaths);
+ ArrayList<FlowPath> getAllFlows();
}
diff --git a/src/main/java/net/floodlightcontroller/flowcache/web/AddFlowResource.java b/src/main/java/net/floodlightcontroller/flowcache/web/AddFlowResource.java
index feb43d3..cdccae1 100644
--- a/src/main/java/net/floodlightcontroller/flowcache/web/AddFlowResource.java
+++ b/src/main/java/net/floodlightcontroller/flowcache/web/AddFlowResource.java
@@ -10,7 +10,7 @@
import org.codehaus.jackson.map.ObjectMapper;
import org.codehaus.jackson.map.JsonMappingException;
import org.codehaus.jackson.map.ObjectMapper;
-import org.restlet.resource.Get;
+import org.restlet.resource.Post;
import org.restlet.resource.ServerResource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -19,8 +19,8 @@
protected static Logger log = LoggerFactory.getLogger(AddFlowResource.class);
- @Get("json")
- public FlowId retrieve() {
+ @Post("json")
+ public FlowId store(String flowJson) {
FlowId result = new FlowId();
IFlowService flowService =
@@ -37,7 +37,7 @@
// NOTE: The "flow" is specified in JSON format.
//
ObjectMapper mapper = new ObjectMapper();
- String flowPathStr = (String) getRequestAttributes().get("flow");
+ String flowPathStr = flowJson;
FlowPath flowPath = null;
log.debug("Add Flow Path: " + flowPathStr);
try {
diff --git a/src/main/java/net/floodlightcontroller/flowcache/web/FlowWebRoutable.java b/src/main/java/net/floodlightcontroller/flowcache/web/FlowWebRoutable.java
index 0859397..cfd3505 100644
--- a/src/main/java/net/floodlightcontroller/flowcache/web/FlowWebRoutable.java
+++ b/src/main/java/net/floodlightcontroller/flowcache/web/FlowWebRoutable.java
@@ -13,11 +13,11 @@
@Override
public Restlet getRestlet(Context context) {
Router router = new Router(context);
- router.attach("/add/{flow}/json", AddFlowResource.class);
+ router.attach("/add/json", AddFlowResource.class);
router.attach("/delete/{flow-id}/json", DeleteFlowResource.class);
router.attach("/get/{flow-id}/json", GetFlowByIdResource.class);
- router.attach("/get/{installer-id}/{src-dpid}/{src-port}/{dst-dpid}/{dst-port}/json", GetFlowByInstallerIdResource.class);
- router.attach("/getall/{src-dpid}/{src-port}/{dst-dpid}/{dst-port}/json", GetAllFlowsByEndpointsResource.class);
+ router.attach("/getall-by-installer-id/{installer-id}/{src-dpid}/{src-port}/{dst-dpid}/{dst-port}/json", GetAllFlowsByInstallerIdResource.class);
+ router.attach("/getall-by-endpoints/{src-dpid}/{src-port}/{dst-dpid}/{dst-port}/json", GetAllFlowsByEndpointsResource.class);
router.attach("/getall/json", GetAllFlowsResource.class);
return router;
}
diff --git a/src/main/java/net/floodlightcontroller/flowcache/web/GetAllFlowsByEndpointsResource.java b/src/main/java/net/floodlightcontroller/flowcache/web/GetAllFlowsByEndpointsResource.java
index c485d91..34d79c8 100644
--- a/src/main/java/net/floodlightcontroller/flowcache/web/GetAllFlowsByEndpointsResource.java
+++ b/src/main/java/net/floodlightcontroller/flowcache/web/GetAllFlowsByEndpointsResource.java
@@ -19,7 +19,7 @@
@Get("json")
public ArrayList<FlowPath> retrieve() {
- ArrayList<FlowPath> result = new ArrayList<FlowPath>();
+ ArrayList<FlowPath> result = null;
IFlowService flowService =
(IFlowService)getContext().getAttributes().
@@ -48,7 +48,7 @@
DataPathEndpoints dataPathEndpoints =
new DataPathEndpoints(srcSwitchPort, dstSwitchPort);
- flowService.getAllFlows(dataPathEndpoints, result);
+ result = flowService.getAllFlows(dataPathEndpoints);
return result;
}
diff --git a/src/main/java/net/floodlightcontroller/flowcache/web/GetFlowByInstallerIdResource.java b/src/main/java/net/floodlightcontroller/flowcache/web/GetAllFlowsByInstallerIdResource.java
similarity index 80%
rename from src/main/java/net/floodlightcontroller/flowcache/web/GetFlowByInstallerIdResource.java
rename to src/main/java/net/floodlightcontroller/flowcache/web/GetAllFlowsByInstallerIdResource.java
index cb4e6ef..e3043dc 100644
--- a/src/main/java/net/floodlightcontroller/flowcache/web/GetFlowByInstallerIdResource.java
+++ b/src/main/java/net/floodlightcontroller/flowcache/web/GetAllFlowsByInstallerIdResource.java
@@ -1,5 +1,7 @@
package net.floodlightcontroller.flowcache.web;
+import java.util.ArrayList;
+
import net.floodlightcontroller.flowcache.IFlowService;
import net.floodlightcontroller.util.CallerId;
import net.floodlightcontroller.util.DataPathEndpoints;
@@ -13,12 +15,12 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-public class GetFlowByInstallerIdResource extends ServerResource {
- protected static Logger log = LoggerFactory.getLogger(GetFlowByInstallerIdResource.class);
+public class GetAllFlowsByInstallerIdResource extends ServerResource {
+ protected static Logger log = LoggerFactory.getLogger(GetAllFlowsByInstallerIdResource.class);
@Get("json")
- public FlowPath retrieve() {
- FlowPath result = null;
+ public ArrayList<FlowPath> retrieve() {
+ ArrayList<FlowPath> result = null;
IFlowService flowService =
(IFlowService)getContext().getAttributes().
@@ -36,7 +38,8 @@
String dstDpidStr = (String) getRequestAttributes().get("dst-dpid");
String dstPortStr = (String) getRequestAttributes().get("dst-port");
- log.debug("Get Flow By Installer: " + installerIdStr + " Endpoints: " +
+ log.debug("Get All Flow By Installer: " + installerIdStr +
+ " Endpoints: " +
srcDpidStr + "--" + srcPortStr + "--" +
dstDpidStr + "--" + dstPortStr);
@@ -50,7 +53,7 @@
DataPathEndpoints dataPathEndpoints =
new DataPathEndpoints(srcSwitchPort, dstSwitchPort);
- flowService.getFlow(installerId, dataPathEndpoints, result);
+ result = flowService.getAllFlows(installerId, dataPathEndpoints);
return result;
}
diff --git a/src/main/java/net/floodlightcontroller/flowcache/web/GetAllFlowsResource.java b/src/main/java/net/floodlightcontroller/flowcache/web/GetAllFlowsResource.java
index deb4d04..92317cf 100644
--- a/src/main/java/net/floodlightcontroller/flowcache/web/GetAllFlowsResource.java
+++ b/src/main/java/net/floodlightcontroller/flowcache/web/GetAllFlowsResource.java
@@ -15,7 +15,7 @@
@Get("json")
public ArrayList<FlowPath> retrieve() {
- ArrayList<FlowPath> result = new ArrayList<FlowPath>();
+ ArrayList<FlowPath> result = null;
IFlowService flowService =
(IFlowService)getContext().getAttributes().
@@ -29,9 +29,7 @@
// Extract the arguments
log.debug("Get All Flows Endpoints");
- flowService.getAllFlows(result);
- FlowPath flowPath = new FlowPath();
- result.add(flowPath);
+ result = flowService.getAllFlows();
return result;
}
diff --git a/src/main/java/net/floodlightcontroller/flowcache/web/GetFlowByIdResource.java b/src/main/java/net/floodlightcontroller/flowcache/web/GetFlowByIdResource.java
index d5b2730..85d5b7e 100644
--- a/src/main/java/net/floodlightcontroller/flowcache/web/GetFlowByIdResource.java
+++ b/src/main/java/net/floodlightcontroller/flowcache/web/GetFlowByIdResource.java
@@ -31,7 +31,7 @@
log.debug("Get Flow Id: " + flowIdStr);
- flowService.getFlow(flowId, result);
+ result = flowService.getFlow(flowId);
return result;
}
diff --git a/src/main/java/net/floodlightcontroller/restclient/RestClient.java b/src/main/java/net/floodlightcontroller/restclient/RestClient.java
new file mode 100644
index 0000000..07eab45
--- /dev/null
+++ b/src/main/java/net/floodlightcontroller/restclient/RestClient.java
@@ -0,0 +1,51 @@
+package net.floodlightcontroller.restclient;
+
+import java.io.IOException;
+import java.net.HttpURLConnection;
+import java.net.MalformedURLException;
+import java.net.URL;
+
+public class RestClient {
+
+ public static void get (String str) {
+
+ if (str == null)
+ return;
+
+ try {
+
+ URL url = new URL(str);
+ HttpURLConnection conn = (HttpURLConnection) url.openConnection();
+ conn.setRequestMethod("GET");
+ conn.setRequestProperty("Accept", "application/json");
+
+ if (conn.getResponseCode() != 200) {
+ throw new RuntimeException("Failed : HTTP error code : "
+ + conn.getResponseCode());
+ }
+
+ /* Disable reading the output from the server for now
+ *
+ BufferedReader br = new BufferedReader(new InputStreamReader(
+ (conn.getInputStream())));
+
+ String output;
+ System.out.println("Output from Server .... \n");
+ while ((output = br.readLine()) != null) {
+ System.out.println(output);
+ }
+ */
+
+ conn.disconnect();
+
+ } catch (MalformedURLException e) {
+
+ e.printStackTrace();
+
+ } catch (IOException e) {
+
+ e.printStackTrace();
+
+ }
+ }
+}
diff --git a/src/main/java/net/floodlightcontroller/routing/TopoRouteService.java b/src/main/java/net/floodlightcontroller/routing/TopoRouteService.java
index f979cc7..94e4769 100644
--- a/src/main/java/net/floodlightcontroller/routing/TopoRouteService.java
+++ b/src/main/java/net/floodlightcontroller/routing/TopoRouteService.java
@@ -22,6 +22,7 @@
import org.openflow.util.HexString;
import com.thinkaurelius.titan.core.TitanGraph;
+import com.tinkerpop.blueprints.TransactionalGraph.Conclusion;
import com.tinkerpop.blueprints.Vertex;
import javax.script.ScriptContext;
@@ -35,7 +36,7 @@
public class TopoRouteService implements IFloodlightModule, ITopoRouteService {
/** The logger. */
- private static Logger logger =
+ private static Logger log =
LoggerFactory.getLogger(TopoRouteService.class);
@Override
@@ -115,14 +116,18 @@
// Get the source vertex
Iterator<Vertex> iter = titanGraph.getVertices("dpid", dpid_src).iterator();
- if (! iter.hasNext())
+ if (! iter.hasNext()) {
+ // titanGraph.stopTransaction(Conclusion.SUCCESS);
return null; // Source vertex not found
+ }
Vertex v_src = iter.next();
// Get the destination vertex
iter = titanGraph.getVertices("dpid", dpid_dest).iterator();
- if (! iter.hasNext())
+ if (! iter.hasNext()) {
+ // titanGraph.stopTransaction(Conclusion.SUCCESS);
return null; // Destination vertex not found
+ }
Vertex v_dest = iter.next();
//
@@ -138,6 +143,7 @@
flowEntry.setInPort(src.port());
flowEntry.setOutPort(dest.port());
result_data_path.flowEntries().add(flowEntry);
+ // titanGraph.stopTransaction(Conclusion.SUCCESS);
return result_data_path;
}
@@ -156,6 +162,7 @@
engine.eval(gremlin);
} catch (ScriptException e) {
System.err.println("Caught ScriptException running Gremlin script: " + e.getMessage());
+ // titanGraph.stopTransaction(Conclusion.SUCCESS);
return null;
}
@@ -224,6 +231,7 @@
result_data_path.flowEntries().add(flowEntry);
}
}
+ // titanGraph.stopTransaction(Conclusion.SUCCESS);
if (result_data_path.flowEntries().size() > 0)
return result_data_path;
diff --git a/src/main/java/net/floodlightcontroller/util/DataPath.java b/src/main/java/net/floodlightcontroller/util/DataPath.java
index 71e0a2f..b2dded6 100644
--- a/src/main/java/net/floodlightcontroller/util/DataPath.java
+++ b/src/main/java/net/floodlightcontroller/util/DataPath.java
@@ -19,6 +19,8 @@
* Default constructor.
*/
public DataPath() {
+ srcPort = new SwitchPort();
+ dstPort = new SwitchPort();
flowEntries = new ArrayList<FlowEntry>();
}
diff --git a/src/main/java/net/floodlightcontroller/util/FlowEntry.java b/src/main/java/net/floodlightcontroller/util/FlowEntry.java
index dfb8f82..64c32b4 100644
--- a/src/main/java/net/floodlightcontroller/util/FlowEntry.java
+++ b/src/main/java/net/floodlightcontroller/util/FlowEntry.java
@@ -1,36 +1,21 @@
package net.floodlightcontroller.util;
+import java.util.ArrayList;
+
import net.floodlightcontroller.util.Dpid;
-import net.floodlightcontroller.util.FlowEntryActions;
+import net.floodlightcontroller.util.FlowEntryAction;
import net.floodlightcontroller.util.FlowEntryId;
import net.floodlightcontroller.util.FlowEntryMatch;
+import net.floodlightcontroller.util.FlowEntrySwitchState;
+import net.floodlightcontroller.util.FlowEntryUserState;
import net.floodlightcontroller.util.Port;
+import net.floodlightcontroller.util.MACAddress;
+import net.floodlightcontroller.util.IPv4;
+
import org.codehaus.jackson.annotate.JsonProperty;
/**
- * The Flow Entry state as set by the user (via the ONOS API).
- */
-enum FlowEntryUserState {
- FE_USER_UNKNOWN, // Initialization value: state unknown
- FE_USER_ADD, // Flow entry that is added
- FE_USER_MODIFY, // Flow entry that is modified
- FE_USER_DELETE // Flow entry that is deleted
-}
-
-/**
- * The Flow Entry state as set by the controller.
- */
-enum FlowEntrySwitchState {
- FE_SWITCH_UNKNOWN, // Initialization value: state unknown
- FE_SWITCH_NOT_UPDATED, // Switch not updated with this entry
- FE_SWITCH_UPDATE_IN_PROGRESS, // Switch update in progress
- FE_SWITCH_UPDATED, // Switch updated with this entry
- FE_SWITCH_UPDATE_FAILED // Error updating the switch with this entry
-}
-
-
-/**
* The class representing the Flow Entry.
*
* NOTE: The specification is incomplete. E.g., the entry needs to
@@ -39,7 +24,7 @@
public class FlowEntry {
private FlowEntryId flowEntryId; // The Flow Entry ID
private FlowEntryMatch flowEntryMatch; // The Flow Entry Match
- private FlowEntryActions flowEntryActions; // The Flow Entry Actions
+ private ArrayList<FlowEntryAction> flowEntryActions; // The Flow Entry Actions
private Dpid dpid; // The Switch DPID
private Port inPort; // The Switch incoming port
private Port outPort; // The Switch outgoing port
@@ -52,6 +37,85 @@
* Default constructor.
*/
public FlowEntry() {
+ // TODO: Test code
+ /*
+ MACAddress mac = MACAddress.valueOf("01:02:03:04:05:06");
+ IPv4 ipv4 = new IPv4("1.2.3.4");
+ IPv4Net ipv4net = new IPv4Net("5.6.7.0/24");
+
+ flowEntryMatch = new FlowEntryMatch();
+ flowEntryMatch.enableInPort(new Port((short)10));
+ flowEntryMatch.enableSrcMac(mac);
+ flowEntryMatch.enableDstMac(mac);
+ flowEntryMatch.enableVlanId((short)20);
+ flowEntryMatch.enableVlanPriority((byte)30);
+ flowEntryMatch.enableEthernetFrameType((short)40);
+ flowEntryMatch.enableIpToS((byte)50);
+ flowEntryMatch.enableIpProto((byte)60);
+ flowEntryMatch.enableSrcIPv4Net(ipv4net);
+ flowEntryMatch.enableDstIPv4Net(ipv4net);
+ flowEntryMatch.enableSrcTcpUdpPort((short)70);
+ flowEntryMatch.enableDstTcpUdpPort((short)80);
+
+ FlowEntryAction action = null;
+ ArrayList<FlowEntryAction> actions = new ArrayList<FlowEntryAction>();
+
+ action = new FlowEntryAction();
+ action.setActionOutput(new Port((short)12));
+ actions.add(action);
+
+ action = new FlowEntryAction();
+ action.setActionOutputToController((short)13);
+ actions.add(action);
+
+ action = new FlowEntryAction();
+ action.setActionSetVlanId((short)14);
+ actions.add(action);
+
+ action = new FlowEntryAction();
+ action.setActionSetVlanPriority((byte)15);
+ actions.add(action);
+
+ action = new FlowEntryAction();
+ action.setActionStripVlan(true);
+ actions.add(action);
+
+ action = new FlowEntryAction();
+ action.setActionSetEthernetSrcAddr(mac);
+ actions.add(action);
+
+ action = new FlowEntryAction();
+ action.setActionSetEthernetDstAddr(mac);
+ actions.add(action);
+
+ action = new FlowEntryAction();
+ action.setActionSetIPv4SrcAddr(ipv4);
+ actions.add(action);
+
+ action = new FlowEntryAction();
+ action.setActionSetIPv4DstAddr(ipv4);
+ actions.add(action);
+
+ action = new FlowEntryAction();
+ action.setActionSetIpToS((byte)16);
+ actions.add(action);
+
+ action = new FlowEntryAction();
+ action.setActionSetTcpUdpSrcPort((short)17);
+ actions.add(action);
+
+ action = new FlowEntryAction();
+ action.setActionSetTcpUdpDstPort((short)18);
+ actions.add(action);
+
+ action = new FlowEntryAction();
+ action.setActionEnqueue(new Port((short)19), 20);
+ actions.add(action);
+
+ setFlowEntryActions(actions);
+ */
+
+
flowEntryUserState = FlowEntryUserState.FE_USER_UNKNOWN;
flowEntrySwitchState = FlowEntrySwitchState.FE_SWITCH_UNKNOWN;
}
@@ -98,7 +162,9 @@
* @return the Flow Entry Actions.
*/
@JsonProperty("flowEntryActions")
- public FlowEntryActions flowEntryActions() { return flowEntryActions; }
+ public ArrayList<FlowEntryAction> flowEntryActions() {
+ return flowEntryActions;
+ }
/**
* Set the Flow Entry Actions.
@@ -106,7 +172,7 @@
* @param flowEntryActions the Flow Entry Actions to set.
*/
@JsonProperty("flowEntryActions")
- public void setFlowEntryActions(FlowEntryActions flowEntryActions) {
+ public void setFlowEntryActions(ArrayList<FlowEntryAction> flowEntryActions) {
this.flowEntryActions = flowEntryActions;
}
@@ -234,7 +300,8 @@
* Convert the flow entry to a string.
*
* The string has the following form:
- * [flowEntryId=XXX flowEntryMatch=XXX flowEntryActions=XXX dpid=XXX
+ * [flowEntryId=XXX flowEntryMatch=XXX flowEntryAction=XXX
+ * flowEntryAction=XXX flowEntryAction=XXX dpid=XXX
* inPort=XXX outPort=XXX flowEntryUserState=XXX flowEntrySwitchState=XXX
* flowEntryErrorState=XXX]
* @return the flow entry as a string.
@@ -243,7 +310,9 @@
public String toString() {
String ret = "[flowEntryId=" + this.flowEntryId.toString();
ret += " flowEntryMatch=" + this.flowEntryMatch.toString();
- ret += " flowEntryActions=" + this.flowEntryActions.toString();
+ for (FlowEntryAction fa : flowEntryActions) {
+ ret += " flowEntryAction=" + fa.toString();
+ }
ret += " dpid=" + this.dpid.toString();
ret += " inPort=" + this.inPort.toString();
ret += " outPort=" + this.outPort.toString();
diff --git a/src/main/java/net/floodlightcontroller/util/FlowEntryAction.java b/src/main/java/net/floodlightcontroller/util/FlowEntryAction.java
new file mode 100644
index 0000000..304bb5c
--- /dev/null
+++ b/src/main/java/net/floodlightcontroller/util/FlowEntryAction.java
@@ -0,0 +1,820 @@
+package net.floodlightcontroller.util;
+
+import net.floodlightcontroller.util.IPv4;
+import net.floodlightcontroller.util.MACAddress;
+import net.floodlightcontroller.util.Port;
+
+import org.codehaus.jackson.annotate.JsonProperty;
+
+/**
+ * The class representing a single Flow Entry action.
+ *
+ * A set of Flow Entry actions need to be applied to each packet.
+ */
+public class FlowEntryAction {
+ /**
+ * Special action values.
+ *
+ * Those values are taken as-is from the OpenFlow-v1.0.0 specification
+ * (pp 21-22).
+ */
+ public enum ActionValues {
+ ACTION_OUTPUT ((short)0x0), // Output to switch port
+ ACTION_SET_VLAN_VID ((short)0x1), // Set the 802.1q VLAN id
+ ACTION_SET_VLAN_PCP ((short)0x2), // Set the 802.1q priority
+ ACTION_STRIP_VLAN ((short)0x3), // Strip the 802.1q header
+ ACTION_SET_DL_SRC ((short)0x4), // Ethernet source address
+ ACTION_SET_DL_DST ((short)0x5), // Ethernet destination address
+ ACTION_SET_NW_SRC ((short)0x6), // IP source address
+ ACTION_SET_NW_DST ((short)0x7), // IP destination address
+ ACTION_SET_NW_TOS ((short)0x8), // IP ToS (DSCP field, 6 bits)
+ ACTION_SET_TP_SRC ((short)0x9), // TCP/UDP source port
+ ACTION_SET_TP_DST ((short)0xa), // TCP/UDP destination port
+ ACTION_ENQUEUE ((short)0xb), // Output to queue on port
+ ACTION_VENDOR ((short)0xffff); // Vendor-specific
+
+ private final short value; // The value
+
+ /**
+ * Constructor for a given value.
+ *
+ * @param value the value to use for the initialization.
+ */
+ private ActionValues(short value) {
+ this.value = value;
+ }
+ }
+
+ /**
+ * Action structure for ACTION_OUTPUT: Output to switch port.
+ */
+ public class ActionOutput {
+ private Port port; // Output port
+ private short maxLen; // Max. length (in bytes) to send to controller
+ // if the port is set to PORT_CONTROLLER
+
+ /**
+ * Constructor for a given output port and maximum length.
+ *
+ * @param port the output port to set.
+ * @param maxLen the maximum length (in bytes) to send to controller
+ * if the port is set to PORT_CONTROLLER.
+ */
+ public ActionOutput(Port port, short maxLen) {
+ this.port = port;
+ this.maxLen = maxLen;
+ }
+
+ /**
+ * Get the output port.
+ *
+ * @return the output port.
+ */
+ @JsonProperty("port")
+ public Port port() {
+ return this.port;
+ }
+
+ /**
+ * Get the maximum length (in bytes) to send to controller if the
+ * port is set to PORT_CONTROLLER.
+ *
+ * @return the maximum length (in bytes) to send to controller if the
+ * port is set to PORT_CONTROLLER.
+ */
+ @JsonProperty("maxLen")
+ public short maxLen() {
+ return this.maxLen;
+ }
+
+ /**
+ * Convert the action to a string.
+ *
+ * The string has the following form:
+ * [port=XXX maxLen=XXX]
+ *
+ * @return the action as a string.
+ */
+ @Override
+ public String toString() {
+ String ret = "[";
+ ret += "port=" + port.toString();
+ ret += " maxLen=" + maxLen;
+ ret += "]";
+
+ return ret;
+ }
+ }
+
+ /**
+ * Action structure for ACTION_SET_VLAN_VID: Set the 802.1q VLAN id
+ */
+ public class ActionSetVlanId {
+ private short vlanId; // The VLAN ID to set
+
+ /**
+ * Constructor for a given VLAN ID.
+ *
+ * @param vlanId the VLAN ID to set.
+ */
+ ActionSetVlanId(short vlanId) {
+ this.vlanId = vlanId;
+ }
+
+ /**
+ * Get the VLAN ID.
+ *
+ * @return the VLAN ID.
+ */
+ @JsonProperty("vlanId")
+ public short vlanId() {
+ return this.vlanId;
+ }
+
+ /**
+ * Convert the action to a string.
+ *
+ * The string has the following form:
+ * [vlanId=XXX]
+ *
+ * @return the action as a string.
+ */
+ @Override
+ public String toString() {
+ String ret = "[";
+ ret += "vlanId=" + this.vlanId;
+ ret += "]";
+
+ return ret;
+ }
+ }
+
+ /**
+ * Action structure for ACTION_SET_VLAN_PCP: Set the 802.1q priority
+ */
+ public class ActionSetVlanPriority {
+ private byte vlanPriority; // The VLAN priority to set
+
+ /**
+ * Constructor for a given VLAN priority.
+ *
+ * @param vlanPriority the VLAN priority to set.
+ */
+ ActionSetVlanPriority(byte vlanPriority) {
+ this.vlanPriority = vlanPriority;
+ }
+
+ /**
+ * Get the VLAN priority.
+ *
+ * @return the VLAN priority.
+ */
+ @JsonProperty("vlanPriority")
+ public byte vlanPriority() {
+ return this.vlanPriority;
+ }
+
+ /**
+ * Convert the action to a string.
+ *
+ * The string has the following form:
+ * [vlanPriority=XXX]
+ *
+ * @return the action as a string.
+ */
+ @Override
+ public String toString() {
+ String ret = "[";
+ ret += "vlanPriority=" + this.vlanPriority;
+ ret += "]";
+
+ return ret;
+ }
+ }
+
+ /**
+ * Action structure for ACTION_STRIP_VLAN: Strip the 802.1q header
+ */
+ public class ActionStripVlan {
+ private boolean stripVlan; // If true, strip the VLAN header
+
+ /**
+ * Constructor for a given boolean flag.
+ *
+ * @param stripVlan if true, strip the VLAN header.
+ */
+ ActionStripVlan(boolean stripVlan) {
+ this.stripVlan = stripVlan;
+ }
+
+ /**
+ * Get the boolean flag whether the VLAN header should be stripped.
+ *
+ * @return the boolean flag whether the VLAN header should be stripped.
+ */
+ @JsonProperty("stripVlan")
+ public boolean stripVlan() {
+ return this.stripVlan;
+ }
+
+ /**
+ * Convert the action to a string.
+ *
+ * The string has the following form:
+ * [stripVlan=XXX]
+ *
+ * @return the action as a string.
+ */
+ @Override
+ public String toString() {
+ String ret = "[";
+ ret += "stripVlan=" + this.stripVlan;
+ ret += "]";
+
+ return ret;
+ }
+ }
+
+ /**
+ * Action structure for ACTION_SET_DL_SRC and ACTION_SET_DL_DST:
+ * Set the Ethernet source/destination address.
+ */
+ public class ActionSetEthernetAddr {
+ private MACAddress addr; // The MAC address to set
+
+ /**
+ * Constructor for a given MAC address.
+ *
+ * @param addr the MAC address to set.
+ */
+ ActionSetEthernetAddr(MACAddress addr) {
+ this.addr = addr;
+ }
+
+ /**
+ * Get the MAC address.
+ *
+ * @return the MAC address.
+ */
+ @JsonProperty("addr")
+ public MACAddress addr() {
+ return this.addr;
+ }
+
+ /**
+ * Convert the action to a string.
+ *
+ * The string has the following form:
+ * [addr=XXX]
+ *
+ * @return the action as a string.
+ */
+ @Override
+ public String toString() {
+ String ret = "[";
+ ret += "addr=" + addr.toString();
+ ret += "]";
+
+ return ret;
+ }
+ }
+
+ /**
+ * Action structure for ACTION_SET_NW_SRC and ACTION_SET_NW_DST:
+ * Set the IPv4 source/destination address.
+ */
+ public class ActionSetIPv4Addr {
+ private IPv4 addr; // The IPv4 address to set
+
+ /**
+ * Constructor for a given IPv4 address.
+ *
+ * @param addr the IPv4 address to set.
+ */
+ ActionSetIPv4Addr(IPv4 addr) {
+ this.addr = addr;
+ }
+
+ /**
+ * Get the IPv4 address.
+ *
+ * @return the IPv4 address.
+ */
+ @JsonProperty("addr")
+ public IPv4 addr() {
+ return this.addr;
+ }
+
+ /**
+ * Convert the action to a string.
+ *
+ * The string has the following form:
+ * [addr=XXX]
+ *
+ * @return the action as a string.
+ */
+ @Override
+ public String toString() {
+ String ret = "[";
+ ret += "addr=" + addr.toString();
+ ret += "]";
+
+ return ret;
+ }
+ }
+
+ /**
+ * Action structure for ACTION_SET_NW_TOS:
+ * Set the IP ToS (DSCP field, 6 bits).
+ */
+ public class ActionSetIpToS {
+ private byte ipToS; // The IP ToS to set DSCP field, 6 bits)
+
+ /**
+ * Constructor for a given IP ToS (DSCP field, 6 bits).
+ *
+ * @param ipToS the IP ToS (DSCP field, 6 bits) to set.
+ */
+ ActionSetIpToS(byte ipToS) {
+ this.ipToS = ipToS;
+ }
+
+ /**
+ * Get the IP ToS (DSCP field, 6 bits).
+ *
+ * @return the IP ToS (DSCP field, 6 bits).
+ */
+ @JsonProperty("ipToS")
+ public byte ipToS() {
+ return this.ipToS;
+ }
+
+ /**
+ * Convert the action to a string.
+ *
+ * The string has the following form:
+ * [ipToS=XXX]
+ *
+ * @return the action as a string.
+ */
+ @Override
+ public String toString() {
+ String ret = "[";
+ ret += "ipToS=" + ipToS;
+ ret += "]";
+
+ return ret;
+ }
+ }
+
+ /**
+ * Action structure for ACTION_SET_TP_SRC and ACTION_SET_TP_DST:
+ * Set the TCP/UDP source/destination port.
+ */
+ public class ActionSetTcpUdpPort {
+ private short port; // The TCP/UDP port to set
+
+ /**
+ * Constructor for a given TCP/UDP port.
+ *
+ * @param port the TCP/UDP port to set.
+ */
+ ActionSetTcpUdpPort(short port) {
+ this.port = port;
+ }
+
+ /**
+ * Get the TCP/UDP port.
+ *
+ * @return the TCP/UDP port.
+ */
+ @JsonProperty("port")
+ public short port() {
+ return this.port;
+ }
+
+ /**
+ * Convert the action to a string.
+ *
+ * The string has the following form:
+ * [port=XXX]
+ *
+ * @return the action as a string.
+ */
+ @Override
+ public String toString() {
+ String ret = "[";
+ ret += "port=" + port;
+ ret += "]";
+
+ return ret;
+ }
+ }
+
+ /**
+ * Action structure for ACTION_ENQUEUE: Output to queue on port.
+ */
+ public class ActionEnqueue {
+ private Port port; // Port that queue belongs. Should
+ // refer to a valid physical port
+ // (i.e. < PORT_MAX) or PORT_IN_PORT
+ private int queueId; // Where to enqueue the packets
+
+ /**
+ * Constructor for a given port and queue ID.
+ *
+ * @param port the port to set.
+ * @param queueId the queue ID on the port.
+ */
+ public ActionEnqueue(Port port, int queueId) {
+ this.port = port;
+ this.queueId = queueId;
+ }
+
+ /**
+ * Get the port.
+ *
+ * @return the port.
+ */
+ @JsonProperty("port")
+ public Port port() {
+ return this.port;
+ }
+
+ /**
+ * Get the queue ID.
+ *
+ * @return the queue ID.
+ */
+ @JsonProperty("queueId")
+ public int queueId() {
+ return this.queueId;
+ }
+
+ /**
+ * Convert the action to a string.
+ *
+ * The string has the following form:
+ * [port=XXX queueId=XXX]
+ *
+ * @return the action as a string.
+ */
+ @Override
+ public String toString() {
+ String ret = "[";
+ ret += "port=" + port.toString();
+ ret += " queueId=" + queueId;
+ ret += "]";
+
+ return ret;
+ }
+ }
+
+ private ActionValues actionType; // The action type
+
+ //
+ // The actions.
+ // NOTE: Only one action should be set.
+ //
+ private ActionOutput actionOutput;
+ private ActionSetVlanId actionSetVlanId;
+ private ActionSetVlanPriority actionSetVlanPriority;
+ private ActionStripVlan actionStripVlan;
+ private ActionSetEthernetAddr actionSetEthernetSrcAddr;
+ private ActionSetEthernetAddr actionSetEthernetDstAddr;
+ private ActionSetIPv4Addr actionSetIPv4SrcAddr;
+ private ActionSetIPv4Addr actionSetIPv4DstAddr;
+ private ActionSetIpToS actionSetIpToS;
+ private ActionSetTcpUdpPort actionSetTcpUdpSrcPort;
+ private ActionSetTcpUdpPort actionSetTcpUdpDstPort;
+ private ActionEnqueue actionEnqueue;
+
+ /**
+ * Default constructor.
+ */
+ public FlowEntryAction() {
+ actionType = ActionValues.ACTION_VENDOR; // XXX: Initial value
+ }
+
+ /**
+ * Get the action type.
+ *
+ * @return the action type.
+ */
+ @JsonProperty("actionType")
+ public ActionValues actionType() { return actionType; }
+
+ /**
+ * Get the output action.
+ *
+ * @return the output action.
+ */
+ @JsonProperty("actionOutput")
+ public ActionOutput actionOutput() { return actionOutput; }
+
+ /**
+ * Set the output action on a port.
+ *
+ * @param port the output port to set.
+ */
+ @JsonProperty("actionOutput")
+ public void setActionOutput(Port port) {
+ actionOutput = new ActionOutput(port, (short)0);
+ actionType = ActionValues.ACTION_OUTPUT;
+ }
+
+ /**
+ * Set the output action to controller.
+ *
+ * @param maxLen the maximum length (in bytes) to send to controller.
+ */
+ @JsonProperty("actionOutputToController")
+ public void setActionOutputToController(short maxLen) {
+ Port port = new Port(Port.PortValues.PORT_CONTROLLER);
+ actionOutput = new ActionOutput(port, maxLen);
+ actionType = ActionValues.ACTION_OUTPUT;
+ }
+
+ /**
+ * Get the action to set the VLAN ID.
+ *
+ * @return the action to set the VLAN ID.
+ */
+ @JsonProperty("actionSetVlanId")
+ public ActionSetVlanId actionSetVlanId() { return actionSetVlanId; }
+
+ /**
+ * Set the action to set the VLAN ID.
+ *
+ * @param vlanId the VLAN ID to set.
+ */
+ @JsonProperty("actionSetVlanId")
+ public void setActionSetVlanId(short vlanId) {
+ actionSetVlanId = new ActionSetVlanId(vlanId);
+ actionType = ActionValues.ACTION_SET_VLAN_VID;
+ }
+
+ /**
+ * Get the action to set the VLAN priority.
+ *
+ * @return the action to set the VLAN priority.
+ */
+ @JsonProperty("actionSetVlanPriority")
+ public ActionSetVlanPriority actionSetVlanPriority() {
+ return actionSetVlanPriority;
+ }
+
+ /**
+ * Set the action to set the VLAN priority.
+ *
+ * @param vlanPriority the VLAN priority to set.
+ */
+ @JsonProperty("actionSetVlanPriority")
+ public void setActionSetVlanPriority(byte vlanPriority) {
+ actionSetVlanPriority = new ActionSetVlanPriority(vlanPriority);
+ actionType = ActionValues.ACTION_SET_VLAN_PCP;
+ }
+
+ /**
+ * Get the action to strip the VLAN header.
+ *
+ * @return the action to strip the VLAN header.
+ */
+ @JsonProperty("actionStripVlan")
+ public ActionStripVlan actionStripVlan() {
+ return actionStripVlan;
+ }
+
+ /**
+ * Set the action to strip the VLAN header.
+ *
+ * @param stripVlan if true, strip the VLAN header.
+ */
+ @JsonProperty("actionStripVlan")
+ public void setActionStripVlan(boolean stripVlan) {
+ actionStripVlan = new ActionStripVlan(stripVlan);
+ actionType = ActionValues.ACTION_STRIP_VLAN;
+ }
+
+ /**
+ * Get the action to set the Ethernet source address.
+ *
+ * @return the action to set the Ethernet source address.
+ */
+ @JsonProperty("actionSetEthernetSrcAddr")
+ public ActionSetEthernetAddr actionSetEthernetSrcAddr() {
+ return actionSetEthernetSrcAddr;
+ }
+
+ /**
+ * Set the action to set the Ethernet source address.
+ *
+ * @param addr the MAC address to set as the Ethernet source address.
+ */
+ @JsonProperty("actionSetEthernetSrcAddr")
+ public void setActionSetEthernetSrcAddr(MACAddress addr) {
+ actionSetEthernetSrcAddr = new ActionSetEthernetAddr(addr);
+ actionType = ActionValues.ACTION_SET_DL_SRC;
+ }
+
+ /**
+ * Get the action to set the Ethernet destination address.
+ *
+ * @return the action to set the Ethernet destination address.
+ */
+ @JsonProperty("actionSetEthernetDstAddr")
+ public ActionSetEthernetAddr actionSetEthernetDstAddr() {
+ return actionSetEthernetDstAddr;
+ }
+
+ /**
+ * Set the action to set the Ethernet destination address.
+ *
+ * @param addr the MAC address to set as the Ethernet destination address.
+ */
+ @JsonProperty("actionSetEthernetDstAddr")
+ public void setActionSetEthernetDstAddr(MACAddress addr) {
+ actionSetEthernetDstAddr = new ActionSetEthernetAddr(addr);
+ actionType = ActionValues.ACTION_SET_DL_DST;
+ }
+
+ /**
+ * Get the action to set the IPv4 source address.
+ *
+ * @return the action to set the IPv4 source address.
+ */
+ @JsonProperty("actionSetIPv4SrcAddr")
+ public ActionSetIPv4Addr actionSetIPv4SrcAddr() {
+ return actionSetIPv4SrcAddr;
+ }
+
+ /**
+ * Set the action to set the IPv4 source address.
+ *
+ * @param addr the IPv4 address to set as the IPv4 source address.
+ */
+ @JsonProperty("actionSetIPv4SrcAddr")
+ public void setActionSetIPv4SrcAddr(IPv4 addr) {
+ actionSetIPv4SrcAddr = new ActionSetIPv4Addr(addr);
+ actionType = ActionValues.ACTION_SET_NW_SRC;
+ }
+
+ /**
+ * Get the action to set the IPv4 destination address.
+ *
+ * @return the action to set the IPv4 destination address.
+ */
+ @JsonProperty("actionSetIPv4DstAddr")
+ public ActionSetIPv4Addr actionSetIPv4DstAddr() {
+ return actionSetIPv4DstAddr;
+ }
+
+ /**
+ * Set the action to set the IPv4 destination address.
+ *
+ * @param addr the IPv4 address to set as the IPv4 destination address.
+ */
+ @JsonProperty("actionSetIPv4DstAddr")
+ public void setActionSetIPv4DstAddr(IPv4 addr) {
+ actionSetIPv4DstAddr = new ActionSetIPv4Addr(addr);
+ actionType = ActionValues.ACTION_SET_NW_DST;
+ }
+
+ /**
+ * Get the action to set the IP ToS (DSCP field, 6 bits).
+ *
+ * @return the action to set the IP ToS (DSCP field, 6 bits).
+ */
+ @JsonProperty("actionSetIpToS")
+ public ActionSetIpToS actionSetIpToS() {
+ return actionSetIpToS;
+ }
+
+ /**
+ * Set the action to set the IP ToS (DSCP field, 6 bits).
+ *
+ * @param ipToS the IP ToS (DSCP field, 6 bits) to set.
+ */
+ @JsonProperty("actionSetIpToS")
+ public void setActionSetIpToS(byte ipToS) {
+ actionSetIpToS = new ActionSetIpToS(ipToS);
+ actionType = ActionValues.ACTION_SET_NW_TOS;
+ }
+
+ /**
+ * Get the action to set the TCP/UDP source port.
+ *
+ * @return the action to set the TCP/UDP source port.
+ */
+ @JsonProperty("actionSetTcpUdpSrcPort")
+ public ActionSetTcpUdpPort actionSetTcpUdpSrcPort() {
+ return actionSetTcpUdpSrcPort;
+ }
+
+ /**
+ * Set the action to set the TCP/UDP source port.
+ *
+ * @param port the TCP/UDP port to set as the TCP/UDP source port.
+ */
+ @JsonProperty("actionSetTcpUdpSrcPort")
+ public void setActionSetTcpUdpSrcPort(short port) {
+ actionSetTcpUdpSrcPort = new ActionSetTcpUdpPort(port);
+ actionType = ActionValues.ACTION_SET_TP_SRC;
+ }
+
+ /**
+ * Get the action to set the TCP/UDP destination port.
+ *
+ * @return the action to set the TCP/UDP destination port.
+ */
+ @JsonProperty("actionSetTcpUdpDstPort")
+ public ActionSetTcpUdpPort actionSetTcpUdpDstPort() {
+ return actionSetTcpUdpDstPort;
+ }
+
+ /**
+ * Set the action to set the TCP/UDP destination port.
+ *
+ * @param port the TCP/UDP port to set as the TCP/UDP destination port.
+ */
+ @JsonProperty("actionSetTcpUdpDstPort")
+ public void setActionSetTcpUdpDstPort(short port) {
+ actionSetTcpUdpDstPort = new ActionSetTcpUdpPort(port);
+ actionType = ActionValues.ACTION_SET_TP_DST;
+ }
+
+ /**
+ * Get the action to output to queue on a port.
+ *
+ * @return the action to output to queue on a port.
+ */
+ @JsonProperty("actionEnqueue")
+ public ActionEnqueue actionEnqueue() { return actionEnqueue; }
+
+ /**
+ * Set the action to output to queue on a port.
+ *
+ * @param port the port to set.
+ * @param int queueId the queue ID to set.
+ */
+ @JsonProperty("actionEnqueue")
+ public void setActionEnqueue(Port port, int queueId) {
+ actionEnqueue = new ActionEnqueue(port, queueId);
+ actionType = ActionValues.ACTION_ENQUEUE;
+ }
+
+ /**
+ * Convert the set of actions to a string.
+ *
+ * The string has the following form:
+ * [type=XXX action=XXX]
+ *
+ * @return the set of actions as a string.
+ */
+ @Override
+ public String toString() {
+ String ret = "[";
+ ret += "type=" + actionType;
+ switch (actionType) {
+ case ACTION_OUTPUT:
+ ret += " action=" + actionOutput.toString();
+ break;
+ case ACTION_SET_VLAN_VID:
+ ret += " action=" + actionSetVlanId.toString();
+ break;
+ case ACTION_SET_VLAN_PCP:
+ ret += " action=" + actionSetVlanPriority.toString();
+ break;
+ case ACTION_STRIP_VLAN:
+ ret += " action=" + actionStripVlan.toString();
+ break;
+ case ACTION_SET_DL_SRC:
+ ret += " action=" + actionSetEthernetSrcAddr.toString();
+ break;
+ case ACTION_SET_DL_DST:
+ ret += " action=" + actionSetEthernetDstAddr.toString();
+ break;
+ case ACTION_SET_NW_SRC:
+ ret += " action=" + actionSetIPv4SrcAddr.toString();
+ break;
+ case ACTION_SET_NW_DST:
+ ret += " action=" + actionSetIPv4DstAddr.toString();
+ break;
+ case ACTION_SET_NW_TOS:
+ ret += " action=" + actionSetIpToS.toString();
+ break;
+ case ACTION_SET_TP_SRC:
+ ret += " action=" + actionSetTcpUdpSrcPort.toString();
+ break;
+ case ACTION_SET_TP_DST:
+ ret += " action=" + actionSetTcpUdpDstPort.toString();
+ break;
+ case ACTION_ENQUEUE:
+ ret += " action=" + actionEnqueue.toString();
+ break;
+ }
+ ret += "]";
+
+ return ret;
+ }
+}
diff --git a/src/main/java/net/floodlightcontroller/util/FlowEntryActions.java b/src/main/java/net/floodlightcontroller/util/FlowEntryActions.java
deleted file mode 100644
index 4d17de8..0000000
--- a/src/main/java/net/floodlightcontroller/util/FlowEntryActions.java
+++ /dev/null
@@ -1,32 +0,0 @@
-package net.floodlightcontroller.util;
-
-import org.codehaus.jackson.annotate.JsonProperty;
-
-/**
- * The class representing the Flow Entry set of actions.
- *
- * The Flow Entry set of actions need to be applied to each packet.
- *
- * NOTE: This is just an empty placeholder (for now). The implied action is
- * forwarding on a single port.
- */
-public class FlowEntryActions {
-
- /**
- * Default constructor.
- */
- public FlowEntryActions() {
- }
-
- /**
- * Convert the set of actions to a string.
- *
- * @return the set of actions as a string.
- */
- @Override
- public String toString() {
- String ret = "";
- // TODO: Implement it!
- return ret;
- }
-}
diff --git a/src/main/java/net/floodlightcontroller/util/FlowEntryId.java b/src/main/java/net/floodlightcontroller/util/FlowEntryId.java
index 0874bdb..d322f5e 100644
--- a/src/main/java/net/floodlightcontroller/util/FlowEntryId.java
+++ b/src/main/java/net/floodlightcontroller/util/FlowEntryId.java
@@ -63,6 +63,6 @@
*/
@Override
public String toString() {
- return Long.toHexString(this.value);
+ return "0x" + Long.toHexString(this.value);
}
}
diff --git a/src/main/java/net/floodlightcontroller/util/FlowEntryMatch.java b/src/main/java/net/floodlightcontroller/util/FlowEntryMatch.java
index 9bd3bea..64527c5 100644
--- a/src/main/java/net/floodlightcontroller/util/FlowEntryMatch.java
+++ b/src/main/java/net/floodlightcontroller/util/FlowEntryMatch.java
@@ -10,18 +10,76 @@
*
* The Flow Entry matching filter that is used to specify
* the network data that would be forwarded on the data path from
- * the source to the destination. Examples: MAC address (of the
- * sender), IP prefix that includes the destination's IP address, etc.
- *
- * NOTE: The FlowEntryMatch specification below is incomplete: we need
- * more matching fields, we need to indicate which fields need to be
- * matched, etc.
+ * the source to the destination. Examples: source or destination MAC address,
+ * IP prefix that includes the destination's IP address, etc.
*/
public class FlowEntryMatch {
- private MACAddress srcMac; // Matching source MAC address
- private MACAddress dstMac; // Matching destination MAC address
- private IPv4Net srcIPv4Net; // Matching source IPv4 prefix
- private IPv4Net dstIPv4Net; // Matching destination IPv4 prefix
+ /**
+ * A class for storing a value to match.
+ */
+ class Field<T> {
+ /**
+ * Default constructor.
+ */
+ public Field() {
+ this.enabled = false;
+ }
+
+ /**
+ * Constructor for a given value to match.
+ */
+ public Field(T value) {
+ this.value = value;
+ this.enabled = true;
+ }
+
+ /**
+ * Get the value.
+ *
+ * @return the value.
+ */
+ public T value() { return this.value; }
+
+ /**
+ * Enable the matching for a given value.
+ *
+ * @param value the value to set.
+ */
+ public void enableMatch(T value) {
+ this.value = value;
+ this.enabled = true;
+ }
+
+ /**
+ * Disable the matching.
+ */
+ public void disableMatch() {
+ this.enabled = false;
+ }
+
+ /**
+ * Test whether matching is enabled.
+ *
+ * @return true if matching is enabled, otherwise false.
+ */
+ public boolean enabled() { return this.enabled; }
+
+ private T value; // The value to match
+ private boolean enabled; // Set to true, if matching is enabled
+ }
+
+ private Field<Port> inPort; // Matching input switch port
+ private Field<MACAddress> srcMac; // Matching source MAC address
+ private Field<MACAddress> dstMac; // Matching destination MAC address
+ private Field<Short> vlanId; // Matching VLAN ID
+ private Field<Byte> vlanPriority; // Matching VLAN priority
+ private Field<Short> ethernetFrameType; // Matching Ethernet frame type
+ private Field<Byte> ipToS; // Matching IP ToS (DSCP field, 6 bits)
+ private Field<Byte> ipProto; // Matching IP protocol
+ private Field<IPv4Net> srcIPv4Net; // Matching source IPv4 prefix
+ private Field<IPv4Net> dstIPv4Net; // Matching destination IPv4 prefix
+ private Field<Short> srcTcpUdpPort; // Matching source TCP/UDP port
+ private Field<Short> dstTcpUdpPort; // Matching destination TCP/UDP port
/**
* Default constructor.
@@ -30,21 +88,85 @@
}
/**
+ * Get the matching input switch port.
+ *
+ * @return the matching input switch port.
+ */
+ @JsonProperty("inPort")
+ public Port inPort() {
+ if (inPort != null)
+ return inPort.value();
+ return null;
+ }
+
+ /**
+ * Enable the matching on input switch port.
+ *
+ * @param inPort the input switch port value to enable for matching.
+ */
+ @JsonProperty("inPort")
+ public void enableInPort(Port inPort) {
+ this.inPort = new Field<Port>(inPort);
+ }
+
+ /**
+ * Disable the matching on input switch port.
+ */
+ public void disableInPort() {
+ this.inPort = null;
+ }
+
+ /**
+ * Test if matching on input switch port is enabled.
+ *
+ * @return true if matching on input switch port is enabled.
+ */
+ @JsonProperty("matchInPort")
+ public boolean matchInPort() {
+ if (inPort != null)
+ return inPort.enabled();
+ return false;
+ }
+
+ /**
* Get the matching source MAC address.
*
* @return the matching source MAC address.
*/
@JsonProperty("srcMac")
- public MACAddress srcMac() { return srcMac; }
+ public MACAddress srcMac() {
+ if (srcMac != null)
+ return srcMac.value();
+ return null;
+ }
/**
- * Set the matching source MAC address.
+ * Enable the matching on source MAC address.
*
- * @param srcMac the matching source MAC address to set.
+ * @param srcMac the source MAC address value to enable for matching.
*/
@JsonProperty("srcMac")
- public void setSrcMac(MACAddress srcMac) {
- this.srcMac = srcMac;
+ public void enableSrcMac(MACAddress srcMac) {
+ this.srcMac = new Field<MACAddress>(srcMac);
+ }
+
+ /**
+ * Disable the matching on source MAC address.
+ */
+ public void disableSrcMac() {
+ this.srcMac = null;
+ }
+
+ /**
+ * Test if matching on source MAC address is enabled.
+ *
+ * @return true if matching on source MAC address is enabled.
+ */
+ @JsonProperty("matchSrcMac")
+ public boolean matchSrcMac() {
+ if (srcMac != null)
+ return srcMac.enabled();
+ return false;
}
/**
@@ -53,16 +175,245 @@
* @return the matching destination MAC address.
*/
@JsonProperty("dstMac")
- public MACAddress dstMac() { return dstMac; }
+ public MACAddress dstMac() {
+ if (dstMac != null)
+ return dstMac.value();
+ return null;
+ }
/**
- * Set the matching destination MAC address.
+ * Enable the matching on destination MAC address.
*
- * @param dstMac the matching destination MAC address to set.
+ * @param dstMac the destination MAC address value to enable for matching.
*/
@JsonProperty("dstMac")
- public void setDstMac(MACAddress dstMac) {
- this.dstMac = dstMac;
+ public void enableDstMac(MACAddress dstMac) {
+ this.dstMac = new Field<MACAddress>(dstMac);
+ }
+
+ /**
+ * Disable the matching on destination MAC address.
+ */
+ public void disableDstMac() {
+ this.dstMac = null;
+ }
+
+ /**
+ * Test if matching on destination MAC address is enabled.
+ *
+ * @return true if matching on destination MAC address is enabled.
+ */
+ @JsonProperty("matchDstMac")
+ public boolean matchDstMac() {
+ if (dstMac != null)
+ return dstMac.enabled();
+ return false;
+ }
+
+ /**
+ * Get the matching VLAN ID.
+ *
+ * @return the matching VLAN ID.
+ */
+ @JsonProperty("vlanId")
+ public Short vlanId() {
+ if (vlanId != null)
+ return vlanId.value();
+ return null;
+ }
+
+ /**
+ * Enable the matching on VLAN ID.
+ *
+ * @param vlanId the VLAN ID value to enable for matching.
+ */
+ @JsonProperty("vlanId")
+ public void enableVlanId(Short vlanId) {
+ this.vlanId = new Field<Short>(vlanId);
+ }
+
+ /**
+ * Disable the matching on VLAN ID.
+ */
+ public void disableVlanId() {
+ this.vlanId = null;
+ }
+
+ /**
+ * Test if matching on VLAN ID is enabled.
+ *
+ * @return true if matching on VLAN ID is enabled.
+ */
+ @JsonProperty("matchVlanId")
+ public boolean matchVlanId() {
+ if (vlanId != null)
+ return vlanId.enabled();
+ return false;
+ }
+
+ /**
+ * Get the matching VLAN priority.
+ *
+ * @return the matching VLAN priority.
+ */
+ @JsonProperty("vlanPriority")
+ public Byte vlanPriority() {
+ if (vlanPriority != null)
+ return vlanPriority.value();
+ return null;
+ }
+
+ /**
+ * Enable the matching on VLAN priority.
+ *
+ * @param vlanPriority the VLAN priority value to enable for matching.
+ */
+ @JsonProperty("vlanPriority")
+ public void enableVlanPriority(Byte vlanPriority) {
+ this.vlanPriority = new Field<Byte>(vlanPriority);
+ }
+
+ /**
+ * Disable the matching on VLAN priority.
+ */
+ public void disableVlanPriority() {
+ this.vlanPriority = null;
+ }
+
+ /**
+ * Test if matching on VLAN priority is enabled.
+ *
+ * @return true if matching on VLAN priority is enabled.
+ */
+ @JsonProperty("matchVlanPriority")
+ public boolean matchVlanPriority() {
+ if (vlanPriority != null)
+ return vlanPriority.enabled();
+ return false;
+ }
+
+ /**
+ * Get the matching Ethernet frame type.
+ *
+ * @return the matching Ethernet frame type.
+ */
+ @JsonProperty("ethernetFrameType")
+ public Short ethernetFrameType() {
+ if (ethernetFrameType != null)
+ return ethernetFrameType.value();
+ return null;
+ }
+
+ /**
+ * Enable the matching on Ethernet frame type.
+ *
+ * @param ethernetFrameType the Ethernet frame type value to enable for
+ * matching.
+ */
+ @JsonProperty("ethernetFrameType")
+ public void enableEthernetFrameType(Short ethernetFrameType) {
+ this.ethernetFrameType = new Field<Short>(ethernetFrameType);
+ }
+
+ /**
+ * Disable the matching on Ethernet frame type.
+ */
+ public void disableEthernetFrameType() {
+ this.ethernetFrameType = null;
+ }
+
+ /**
+ * Test if matching on Ethernet frame type is enabled.
+ *
+ * @return true if matching on Ethernet frame type is enabled.
+ */
+ @JsonProperty("matchEthernetFrameType")
+ public boolean matchEthernetFrameType() {
+ if (ethernetFrameType != null)
+ return ethernetFrameType.enabled();
+ return false;
+ }
+
+ /**
+ * Get the matching IP ToS (DSCP field, 6 bits)
+ *
+ * @return the matching IP ToS.
+ */
+ @JsonProperty("ipToS")
+ public Byte ipToS() {
+ if (ipToS != null)
+ return ipToS.value();
+ return null;
+ }
+
+ /**
+ * Enable the matching on IP ToS (DSCP field, 6 bits).
+ *
+ * @param ipToS the IP ToS value to enable for matching.
+ */
+ @JsonProperty("ipToS")
+ public void enableIpToS(Byte ipToS) {
+ this.ipToS = new Field<Byte>(ipToS);
+ }
+
+ /**
+ * Disable the matching on IP ToS (DSCP field, 6 bits).
+ */
+ public void disableIpToS() {
+ this.ipToS = null;
+ }
+
+ /**
+ * Test if matching on IP ToS (DSCP field, 6 bits) is enabled.
+ *
+ * @return true if matching on IP ToS is enabled.
+ */
+ @JsonProperty("matchIpToS")
+ public boolean matchIpToS() {
+ if (ipToS != null)
+ return ipToS.enabled();
+ return false;
+ }
+
+ /**
+ * Get the matching IP protocol.
+ *
+ * @return the matching IP protocol.
+ */
+ @JsonProperty("ipProto")
+ public Byte ipProto() {
+ if (ipProto != null)
+ return ipProto.value();
+ return null;
+ }
+
+ /**
+ * Enable the matching on IP protocol.
+ *
+ * @param ipProto the IP protocol value to enable for matching.
+ */
+ @JsonProperty("ipProto")
+ public void enableIpProto(Byte ipProto) {
+ this.ipProto = new Field<Byte>(ipProto);
+ }
+
+ /**
+ * Disable the matching on IP protocol.
+ */
+ public void disableIpProto() {
+ this.ipProto = null;
+ }
+
+ /**
+ * Test if matching on IP protocol is enabled.
+ *
+ * @return true if matching on IP protocol is enabled.
+ */
+ @JsonProperty("matchIpProto")
+ public boolean matchIpProto() {
+ if (ipProto != null)
+ return ipProto.enabled();
+ return false;
}
/**
@@ -71,16 +422,39 @@
* @return the matching source IPv4 prefix.
*/
@JsonProperty("srcIPv4Net")
- public IPv4Net srcIPv4Net() { return srcIPv4Net; }
+ public IPv4Net srcIPv4Net() {
+ if (srcIPv4Net != null)
+ return srcIPv4Net.value();
+ return null;
+ }
/**
- * Set the matching source IPv4 prefix.
+ * Enable the matching on source IPv4 prefix.
*
- * @param srcIPv4Net the matching source IPv4 prefix to set.
+ * @param srcIPv4Net the source IPv4 prefix value to enable for matching.
*/
@JsonProperty("srcIPv4Net")
- public void setSrcIPv4Net(IPv4Net srcIPv4Net) {
- this.srcIPv4Net = srcIPv4Net;
+ public void enableSrcIPv4Net(IPv4Net srcIPv4Net) {
+ this.srcIPv4Net = new Field<IPv4Net>(srcIPv4Net);
+ }
+
+ /**
+ * Disable the matching on source IPv4 prefix.
+ */
+ public void disableSrcIPv4Net() {
+ this.srcIPv4Net = null;
+ }
+
+ /**
+ * Test if matching on source IPv4 prefix is enabled.
+ *
+ * @return true if matching on source IPv4 prefix is enabled.
+ */
+ @JsonProperty("matchSrcIPv4Net")
+ public boolean matchSrcIPv4Net() {
+ if (srcIPv4Net != null)
+ return srcIPv4Net.enabled();
+ return false;
}
/**
@@ -89,16 +463,123 @@
* @return the matching destination IPv4 prefix.
*/
@JsonProperty("dstIPv4Net")
- public IPv4Net dstIPv4Net() { return dstIPv4Net; }
+ public IPv4Net dstIPv4Net() {
+ if (dstIPv4Net != null)
+ return dstIPv4Net.value();
+ return null;
+ }
/**
- * Set the matching destination IPv4 prefix.
+ * Enable the matching on destination IPv4 prefix.
*
- * @param srcIPv4Net the matching destination IPv4 prefix to set.
+ * @param dstIPv4Net the destination IPv4 prefix value to enable for
+ * matching.
*/
@JsonProperty("dstIPv4Net")
- public void setDstIPv4Net(IPv4Net dstIPv4Net) {
- this.dstIPv4Net = dstIPv4Net;
+ public void enableDstIPv4Net(IPv4Net dstIPv4Net) {
+ this.dstIPv4Net = new Field<IPv4Net>(dstIPv4Net);
+ }
+
+ /**
+ * Disable the matching on destination IPv4 prefix.
+ */
+ public void disableDstIPv4Net() {
+ this.dstIPv4Net = null;
+ }
+
+ /**
+ * Test if matching on destination IPv4 prefix is enabled.
+ *
+ * @return true if matching on destination IPv4 prefix is enabled.
+ */
+ @JsonProperty("matchDstIPv4Net")
+ public boolean matchDstIPv4Net() {
+ if (dstIPv4Net != null)
+ return dstIPv4Net.enabled();
+ return false;
+ }
+
+ /**
+ * Get the matching source TCP/UDP port.
+ *
+ * @return the matching source TCP/UDP port.
+ */
+ @JsonProperty("srcTcpUdpPort")
+ public Short srcTcpUdpPort() {
+ if (srcTcpUdpPort != null)
+ return srcTcpUdpPort.value();
+ return null;
+ }
+
+ /**
+ * Enable the matching on source TCP/UDP port.
+ *
+ * @param srcTcpUdpPort the source TCP/UDP port to enable for matching.
+ */
+ @JsonProperty("srcTcpUdpPort")
+ public void enableSrcTcpUdpPort(Short srcTcpUdpPort) {
+ this.srcTcpUdpPort = new Field<Short>(srcTcpUdpPort);
+ }
+
+ /**
+ * Disable the matching on source TCP/UDP port.
+ */
+ public void disableSrcTcpUdpPort() {
+ this.srcTcpUdpPort = null;
+ }
+
+ /**
+ * Test if matching on source TCP/UDP port is enabled.
+ *
+ * @return true if matching on source TCP/UDP port is enabled.
+ */
+ @JsonProperty("matchSrcTcpUdpPort")
+ public boolean matchSrcTcpUdpPort() {
+ if (srcTcpUdpPort != null)
+ return srcTcpUdpPort.enabled();
+ return false;
+ }
+
+ /**
+ * Get the matching destination TCP/UDP port.
+ *
+ * @return the matching destination TCP/UDP port.
+ */
+ @JsonProperty("dstTcpUdpPort")
+ public Short dstTcpUdpPort() {
+ if (dstTcpUdpPort != null)
+ return dstTcpUdpPort.value();
+ return null;
+ }
+
+ /**
+ * Enable the matching on destination TCP/UDP port.
+ *
+ * @param dstTcpUdpPort the destination TCP/UDP port to enable for
+ * matching.
+ */
+ @JsonProperty("dstTcpUdpPort")
+ public void enableDstTcpUdpPort(Short dstTcpUdpPort) {
+ this.dstTcpUdpPort = new Field<Short>(dstTcpUdpPort);
+ }
+
+ /**
+ * Disable the matching on destination TCP/UDP port.
+ */
+ public void disableDstTcpUdpPort() {
+ this.dstTcpUdpPort = null;
+ }
+
+ /**
+ * Test if matching on destination TCP/UDP port is enabled.
+ *
+ * @return true if matching on destination TCP/UDP port is enabled.
+ */
+ @JsonProperty("matchDstTcpUdpPort")
+ public boolean matchDstTcpUdpPort() {
+ if (dstTcpUdpPort != null)
+ return dstTcpUdpPort.enabled();
+ return false;
}
/**
@@ -111,11 +592,87 @@
*/
@Override
public String toString() {
- String ret = "[srcMac=" + this.srcMac.toString();
- ret += " dstMac=" + this.dstMac.toString();
- ret += " srcIPv4Net=" + this.srcIPv4Net.toString();
- ret += " dstIPv4Net=" + this.dstIPv4Net.toString();
+ String ret = "[";
+ boolean addSpace = false;
+
+ //
+ // Conditionally add only those matching fields that are enabled
+ //
+ if (matchInPort()) {
+ if (addSpace)
+ ret += " ";
+ addSpace = true;
+ ret += "inPort=" + this.inPort().toString();
+ }
+ if (matchSrcMac()) {
+ if (addSpace)
+ ret += " ";
+ addSpace = true;
+ ret += "srcMac=" + this.srcMac().toString();
+ }
+ if (matchDstMac()) {
+ if (addSpace)
+ ret += " ";
+ addSpace = true;
+ ret += "dstMac=" + this.dstMac().toString();
+ }
+ if (matchVlanId()) {
+ if (addSpace)
+ ret += " ";
+ addSpace = true;
+ ret += "vlanId=" + this.vlanId().toString();
+ }
+ if (matchVlanPriority()) {
+ if (addSpace)
+ ret += " ";
+ addSpace = true;
+ ret += "vlanPriority=" + this.vlanPriority().toString();
+ }
+ if (matchEthernetFrameType()) {
+ if (addSpace)
+ ret += " ";
+ addSpace = true;
+ ret += "ethernetFrameType=" + this.ethernetFrameType().toString();
+ }
+ if (matchIpToS()) {
+ if (addSpace)
+ ret += " ";
+ addSpace = true;
+ ret += "ipToS=" + this.ipToS().toString();
+ }
+ if (matchIpProto()) {
+ if (addSpace)
+ ret += " ";
+ addSpace = true;
+ ret += "ipProto=" + this.ipProto().toString();
+ }
+ if (matchSrcIPv4Net()) {
+ if (addSpace)
+ ret += " ";
+ addSpace = true;
+ ret += "srcIPv4Net=" + this.srcIPv4Net().toString();
+ }
+ if (matchDstIPv4Net()) {
+ if (addSpace)
+ ret += " ";
+ addSpace = true;
+ ret += "dstIPv4Net=" + this.dstIPv4Net().toString();
+ }
+ if (matchSrcTcpUdpPort()) {
+ if (addSpace)
+ ret += " ";
+ addSpace = true;
+ ret += "srcTcpUdpPort=" + this.srcTcpUdpPort().toString();
+ }
+ if (matchDstTcpUdpPort()) {
+ if (addSpace)
+ ret += " ";
+ addSpace = true;
+ ret += "dstTcpUdpPort=" + this.dstTcpUdpPort().toString();
+ }
+
ret += "]";
+
return ret;
}
}
diff --git a/src/main/java/net/floodlightcontroller/util/FlowEntrySwitchState.java b/src/main/java/net/floodlightcontroller/util/FlowEntrySwitchState.java
new file mode 100644
index 0000000..4f9882a
--- /dev/null
+++ b/src/main/java/net/floodlightcontroller/util/FlowEntrySwitchState.java
@@ -0,0 +1,12 @@
+package net.floodlightcontroller.util;
+
+/**
+ * The Flow Entry state as set by the controller.
+ */
+public enum FlowEntrySwitchState {
+ FE_SWITCH_UNKNOWN, // Initialization value: state unknown
+ FE_SWITCH_NOT_UPDATED, // Switch not updated with this entry
+ FE_SWITCH_UPDATE_IN_PROGRESS, // Switch update in progress
+ FE_SWITCH_UPDATED, // Switch updated with this entry
+ FE_SWITCH_UPDATE_FAILED // Error updating the switch with this entry
+}
diff --git a/src/main/java/net/floodlightcontroller/util/FlowEntryUserState.java b/src/main/java/net/floodlightcontroller/util/FlowEntryUserState.java
new file mode 100644
index 0000000..8637b4f
--- /dev/null
+++ b/src/main/java/net/floodlightcontroller/util/FlowEntryUserState.java
@@ -0,0 +1,11 @@
+package net.floodlightcontroller.util;
+
+/**
+ * The Flow Entry state as set by the user (via the ONOS API).
+ */
+public enum FlowEntryUserState {
+ FE_USER_UNKNOWN, // Initialization value: state unknown
+ FE_USER_ADD, // Flow entry that is added
+ FE_USER_MODIFY, // Flow entry that is modified
+ FE_USER_DELETE // Flow entry that is deleted
+}
diff --git a/src/main/java/net/floodlightcontroller/util/FlowPath.java b/src/main/java/net/floodlightcontroller/util/FlowPath.java
index 5b3bbd1..11f23fe 100644
--- a/src/main/java/net/floodlightcontroller/util/FlowPath.java
+++ b/src/main/java/net/floodlightcontroller/util/FlowPath.java
@@ -18,6 +18,7 @@
* Default constructor.
*/
public FlowPath() {
+ dataPath = new DataPath();
}
/**
diff --git a/src/main/java/net/floodlightcontroller/util/MACAddress.java b/src/main/java/net/floodlightcontroller/util/MACAddress.java
index 4ba9dad..743dc5b 100644
--- a/src/main/java/net/floodlightcontroller/util/MACAddress.java
+++ b/src/main/java/net/floodlightcontroller/util/MACAddress.java
@@ -2,11 +2,20 @@
import java.util.Arrays;
+import net.floodlightcontroller.util.serializers.MACAddressDeserializer;
+import net.floodlightcontroller.util.serializers.MACAddressSerializer;
+
+import org.codehaus.jackson.annotate.JsonProperty;
+import org.codehaus.jackson.map.annotate.JsonDeserialize;
+import org.codehaus.jackson.map.annotate.JsonSerialize;
+
/**
* The class representing MAC address.
*
* @author Sho Shimizu (sho.shimizu@gmail.com)
*/
+@JsonDeserialize(using=MACAddressDeserializer.class)
+@JsonSerialize(using=MACAddressSerializer.class)
public class MACAddress {
public static final int MAC_ADDRESS_LENGTH = 6;
private byte[] address = new byte[MAC_ADDRESS_LENGTH];
diff --git a/src/main/java/net/floodlightcontroller/util/Port.java b/src/main/java/net/floodlightcontroller/util/Port.java
index 19bbf8f..41f0d55 100644
--- a/src/main/java/net/floodlightcontroller/util/Port.java
+++ b/src/main/java/net/floodlightcontroller/util/Port.java
@@ -5,7 +5,69 @@
/**
* The class representing a network port of a switch.
*/
+
public class Port {
+ /**
+ * Special port values.
+ *
+ * Those values are taken as-is from the OpenFlow-v1.0.0 specification
+ * (pp 18-19).
+ */
+ public enum PortValues {
+ /* Maximum number of physical switch ports. */
+ PORT_MAX ((short)0xff00),
+
+ /* Fake output "ports". */
+
+ /* Send the packet out the input port. This
+ virtual port must be explicitly used
+ in order to send back out of the input
+ port. */
+ PORT_IN_PORT ((short)0xfff8),
+
+ /* Perform actions in flow table.
+ NB: This can only be the destination
+ port for packet-out messages. */
+ PORT_TABLE ((short)0xfff9),
+
+ /* Process with normal L2/L3 switching. */
+ PORT_NORMAL ((short)0xfffa),
+
+ /* All physical ports except input port and
+ those disabled by STP. */
+ PORT_FLOOD ((short)0xfffb),
+
+ /* All physical ports except input port. */
+ PORT_ALL ((short)0xfffc),
+
+ /* Send to controller. */
+ PORT_CONTROLLER ((short)0xfffd),
+
+ /* Local openflow "port". */
+ PORT_LOCAL ((short)0xfffe),
+
+ /* Not associated with a physical port. */
+ PORT_NONE ((short)0xffff);
+
+ private final short value; // The value
+
+ /**
+ * Constructor for a given value.
+ *
+ * @param value the value to use for the initialization.
+ */
+ private PortValues(short value) {
+ this.value = value;
+ }
+
+ /**
+ * Get the value as a short integer.
+ *
+ * @return the value as a short integer.
+ */
+ private short value() { return this.value; }
+ }
+
private short value;
/**
@@ -25,7 +87,7 @@
}
/**
- * Constructor from a long value.
+ * Constructor from a short integer value.
*
* @param value the value to use.
*/
@@ -34,6 +96,15 @@
}
/**
+ * Constructor from a PortValues enum value.
+ *
+ * @param value the value to use.
+ */
+ public Port(PortValues value) {
+ this.value = value.value();
+ }
+
+ /**
* Get the value of the port.
*
* @return the value of the port.
diff --git a/src/main/java/net/floodlightcontroller/util/serializers/IPv4Deserializer.java b/src/main/java/net/floodlightcontroller/util/serializers/IPv4Deserializer.java
index 7ce7d5c..275f9f0 100644
--- a/src/main/java/net/floodlightcontroller/util/serializers/IPv4Deserializer.java
+++ b/src/main/java/net/floodlightcontroller/util/serializers/IPv4Deserializer.java
@@ -16,7 +16,7 @@
import org.slf4j.LoggerFactory;
/**
- * Deserialize an IPv4 from a string.
+ * Deserialize an IPv4 address from a string.
*/
public class IPv4Deserializer extends JsonDeserializer<IPv4> {
diff --git a/src/main/java/net/floodlightcontroller/util/serializers/IPv4NetDeserializer.java b/src/main/java/net/floodlightcontroller/util/serializers/IPv4NetDeserializer.java
index e35fc80..3c36870 100644
--- a/src/main/java/net/floodlightcontroller/util/serializers/IPv4NetDeserializer.java
+++ b/src/main/java/net/floodlightcontroller/util/serializers/IPv4NetDeserializer.java
@@ -16,7 +16,7 @@
import org.slf4j.LoggerFactory;
/**
- * Deserialize an IPv4Net from a string.
+ * Deserialize an IPv4Net address from a string.
*/
public class IPv4NetDeserializer extends JsonDeserializer<IPv4Net> {
diff --git a/src/main/java/net/floodlightcontroller/util/serializers/IPv6Deserializer.java b/src/main/java/net/floodlightcontroller/util/serializers/IPv6Deserializer.java
index 6713f93..818de30 100644
--- a/src/main/java/net/floodlightcontroller/util/serializers/IPv6Deserializer.java
+++ b/src/main/java/net/floodlightcontroller/util/serializers/IPv6Deserializer.java
@@ -16,7 +16,7 @@
import org.slf4j.LoggerFactory;
/**
- * Deserialize an IPv6 from a string.
+ * Deserialize an IPv6 address from a string.
*/
public class IPv6Deserializer extends JsonDeserializer<IPv6> {
diff --git a/src/main/java/net/floodlightcontroller/util/serializers/IPv6NetDeserializer.java b/src/main/java/net/floodlightcontroller/util/serializers/IPv6NetDeserializer.java
index 596ee50..375dc26 100644
--- a/src/main/java/net/floodlightcontroller/util/serializers/IPv6NetDeserializer.java
+++ b/src/main/java/net/floodlightcontroller/util/serializers/IPv6NetDeserializer.java
@@ -16,7 +16,7 @@
import org.slf4j.LoggerFactory;
/**
- * Deserialize an IPv6Net from a string.
+ * Deserialize an IPv6Net address from a string.
*/
public class IPv6NetDeserializer extends JsonDeserializer<IPv6Net> {
diff --git a/src/main/java/net/floodlightcontroller/util/serializers/MACAddressDeserializer.java b/src/main/java/net/floodlightcontroller/util/serializers/MACAddressDeserializer.java
new file mode 100644
index 0000000..35b384d
--- /dev/null
+++ b/src/main/java/net/floodlightcontroller/util/serializers/MACAddressDeserializer.java
@@ -0,0 +1,43 @@
+package net.floodlightcontroller.util.serializers;
+
+import java.io.IOException;
+
+import org.codehaus.jackson.JsonNode;
+import org.codehaus.jackson.JsonParser;
+import org.codehaus.jackson.JsonToken;
+import org.codehaus.jackson.JsonProcessingException;
+import org.codehaus.jackson.ObjectCodec;
+import org.codehaus.jackson.map.JsonDeserializer;
+import org.codehaus.jackson.map.DeserializationContext;
+
+import net.floodlightcontroller.util.MACAddress;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Deserialize a MAC address from a string.
+ */
+public class MACAddressDeserializer extends JsonDeserializer<MACAddress> {
+
+ protected static Logger log = LoggerFactory.getLogger(MACAddressDeserializer.class);
+
+ @Override
+ public MACAddress deserialize(JsonParser jp,
+ DeserializationContext ctxt)
+ throws IOException, JsonProcessingException {
+
+ MACAddress mac = null;
+
+ jp.nextToken(); // Move to JsonToken.START_OBJECT
+ while (jp.nextToken() != JsonToken.END_OBJECT) {
+ String fieldname = jp.getCurrentName();
+ if ("value".equals(fieldname)) {
+ String value = jp.getText();
+ log.debug("Fieldname: " + fieldname + " Value: " + value);
+ mac = MACAddress.valueOf(value);
+ }
+ }
+ return mac;
+ }
+}
diff --git a/src/main/java/net/floodlightcontroller/util/serializers/MACAddressSerializer.java b/src/main/java/net/floodlightcontroller/util/serializers/MACAddressSerializer.java
new file mode 100644
index 0000000..dec2596
--- /dev/null
+++ b/src/main/java/net/floodlightcontroller/util/serializers/MACAddressSerializer.java
@@ -0,0 +1,25 @@
+package net.floodlightcontroller.util.serializers;
+
+import java.io.IOException;
+
+import org.codehaus.jackson.JsonGenerator;
+import org.codehaus.jackson.JsonProcessingException;
+import org.codehaus.jackson.map.JsonSerializer;
+import org.codehaus.jackson.map.SerializerProvider;
+
+import net.floodlightcontroller.util.MACAddress;
+
+/**
+ * Serialize a MAC address as a string.
+ */
+public class MACAddressSerializer extends JsonSerializer<MACAddress> {
+
+ @Override
+ public void serialize(MACAddress mac, JsonGenerator jGen,
+ SerializerProvider serializer)
+ throws IOException, JsonProcessingException {
+ jGen.writeStartObject();
+ jGen.writeStringField("value", mac.toString());
+ jGen.writeEndObject();
+ }
+}
diff --git a/src/main/java/net/onrc/onos/util/GraphDBConnection.java b/src/main/java/net/onrc/onos/util/GraphDBConnection.java
index a2a8689..724095b 100644
--- a/src/main/java/net/onrc/onos/util/GraphDBConnection.java
+++ b/src/main/java/net/onrc/onos/util/GraphDBConnection.java
@@ -37,6 +37,13 @@
if (!s.contains("dl_address")) {
graph.createKeyIndex("dl_address", Vertex.class);
}
+ if (!s.contains("flow_id")) {
+ graph.createKeyIndex("flow_id", Vertex.class);
+ }
+ if (!s.contains("flow_entry_id")) {
+ graph.createKeyIndex("flow_entry_id",
+ Vertex.class);
+ }
}
graph.stopTransaction(Conclusion.SUCCESS);
if (utils == null) {
diff --git a/src/main/java/net/onrc/onos/util/GraphDBUtils.java b/src/main/java/net/onrc/onos/util/GraphDBUtils.java
index d01de21..097cfa0 100644
--- a/src/main/java/net/onrc/onos/util/GraphDBUtils.java
+++ b/src/main/java/net/onrc/onos/util/GraphDBUtils.java
@@ -7,8 +7,12 @@
import com.tinkerpop.gremlin.java.GremlinPipeline;
import net.floodlightcontroller.core.INetMapTopologyObjects.IDeviceObject;
+import net.floodlightcontroller.core.INetMapTopologyObjects.IFlowEntry;
+import net.floodlightcontroller.core.INetMapTopologyObjects.IFlowPath;
import net.floodlightcontroller.core.INetMapTopologyObjects.IPortObject;
import net.floodlightcontroller.core.INetMapTopologyObjects.ISwitchObject;
+import net.floodlightcontroller.util.FlowEntryId;
+import net.floodlightcontroller.util.FlowId;
public class GraphDBUtils implements IDBUtils {
@@ -60,4 +64,76 @@
return fg.getVertices("type","device",IDeviceObject.class);
}
+ @Override
+ public IFlowPath searchFlowPath(GraphDBConnection conn,
+ FlowId flowId) {
+ FramedGraph<TitanGraph> fg = conn.getFramedGraph();
+
+ return fg.getVertices("flow_id", flowId.toString()).iterator().hasNext() ?
+ fg.getVertices("flow_id", flowId.toString(),
+ IFlowPath.class).iterator().next() : null;
+ }
+
+ @Override
+ public IFlowPath newFlowPath(GraphDBConnection conn) {
+ FramedGraph<TitanGraph> fg = conn.getFramedGraph();
+ IFlowPath flowPath = fg.addVertex(null, IFlowPath.class);
+ return flowPath;
+ }
+
+ @Override
+ public void removeFlowPath(GraphDBConnection conn,
+ IFlowPath flowPath) {
+ FramedGraph<TitanGraph> fg = conn.getFramedGraph();
+ fg.removeVertex(flowPath.asVertex());
+ }
+
+ @Override
+ public IFlowPath getFlowPathByFlowEntry(GraphDBConnection conn,
+ IFlowEntry flowEntry) {
+ FramedGraph<TitanGraph> fg = conn.getFramedGraph();
+ GremlinPipeline<Vertex, IFlowPath> pipe = new GremlinPipeline<Vertex, IFlowPath>();
+ pipe.start(flowEntry.asVertex());
+ pipe.out("flow");
+ FramedVertexIterable<IFlowPath> r = new FramedVertexIterable(conn.getFramedGraph(), pipe, IFlowPath.class);
+ return r.iterator().hasNext() ? r.iterator().next() : null;
+ }
+
+ @Override
+ public Iterable<IFlowPath> getAllFlowPaths(GraphDBConnection conn) {
+ FramedGraph<TitanGraph> fg = conn.getFramedGraph();
+
+ return fg.getVertices("type", "flow", IFlowPath.class);
+ }
+
+ @Override
+ public IFlowEntry searchFlowEntry(GraphDBConnection conn,
+ FlowEntryId flowEntryId) {
+ FramedGraph<TitanGraph> fg = conn.getFramedGraph();
+
+ return fg.getVertices("flow_entry_id", flowEntryId.toString()).iterator().hasNext() ?
+ fg.getVertices("flow_entry_id", flowEntryId.toString(),
+ IFlowEntry.class).iterator().next() : null;
+ }
+
+ @Override
+ public IFlowEntry newFlowEntry(GraphDBConnection conn) {
+ FramedGraph<TitanGraph> fg = conn.getFramedGraph();
+ IFlowEntry flowEntry = fg.addVertex(null, IFlowEntry.class);
+ return flowEntry;
+ }
+
+ @Override
+ public void removeFlowEntry(GraphDBConnection conn,
+ IFlowEntry flowEntry) {
+ FramedGraph<TitanGraph> fg = conn.getFramedGraph();
+ fg.removeVertex(flowEntry.asVertex());
+ }
+
+ @Override
+ public Iterable<IFlowEntry> getAllFlowEntries(GraphDBConnection conn) {
+ FramedGraph<TitanGraph> fg = conn.getFramedGraph();
+
+ return fg.getVertices("type", "flow_entry", IFlowEntry.class);
+ }
}
diff --git a/src/main/java/net/onrc/onos/util/IDBUtils.java b/src/main/java/net/onrc/onos/util/IDBUtils.java
index a27a261..48d5946 100644
--- a/src/main/java/net/onrc/onos/util/IDBUtils.java
+++ b/src/main/java/net/onrc/onos/util/IDBUtils.java
@@ -1,8 +1,12 @@
package net.onrc.onos.util;
import net.floodlightcontroller.core.INetMapTopologyObjects.IDeviceObject;
+import net.floodlightcontroller.core.INetMapTopologyObjects.IFlowEntry;
+import net.floodlightcontroller.core.INetMapTopologyObjects.IFlowPath;
import net.floodlightcontroller.core.INetMapTopologyObjects.IPortObject;
import net.floodlightcontroller.core.INetMapTopologyObjects.ISwitchObject;
+import net.floodlightcontroller.util.FlowEntryId;
+import net.floodlightcontroller.util.FlowId;
public interface IDBUtils {
public ISwitchObject searchSwitch(GraphDBConnection conn, String dpid);
@@ -11,4 +15,16 @@
public void removeDevice(GraphDBConnection conn, IDeviceObject dev);
public IPortObject searchPort(GraphDBConnection conn, String dpid, short number);
public Iterable<IDeviceObject> getDevices(GraphDBConnection conn);
+ public IFlowPath searchFlowPath(GraphDBConnection conn, FlowId flowId);
+ public IFlowPath newFlowPath(GraphDBConnection conn);
+ public void removeFlowPath(GraphDBConnection conn, IFlowPath flowPath);
+ public IFlowPath getFlowPathByFlowEntry(GraphDBConnection conn,
+ IFlowEntry flowEntry);
+ public Iterable<IFlowPath> getAllFlowPaths(GraphDBConnection conn);
+ public IFlowEntry searchFlowEntry(GraphDBConnection conn,
+ FlowEntryId flowEntryId);
+ public IFlowEntry newFlowEntry(GraphDBConnection conn);
+ public void removeFlowEntry(GraphDBConnection conn,
+ IFlowEntry flowEntry);
+ public Iterable<IFlowEntry> getAllFlowEntries(GraphDBConnection conn);
}
diff --git a/src/main/resources/cassandra.titan b/src/main/resources/cassandra.titan
index 8846963..ef6f3ae 100644
--- a/src/main/resources/cassandra.titan
+++ b/src/main/resources/cassandra.titan
@@ -1,3 +1,3 @@
storage.backend=cassandra
-storage.hostname=onos8vpc
+storage.hostname=localhost
storage.keyspace=onos
diff --git a/web/add_flow.py b/web/add_flow.py
new file mode 100755
index 0000000..18846b7
--- /dev/null
+++ b/web/add_flow.py
@@ -0,0 +1,121 @@
+#! /usr/bin/env python
+# -*- Mode: python; py-indent-offset: 4; tab-width: 8; indent-tabs-mode: t; -*-
+
+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
+
+#
+# curl http://127.0.0.1:8080/wm/topology/route/00:00:00:00:00:00:0a:01/1/00:00:00:00:00:00:0a:04/1/json
+#
+
+## Global Var ##
+ControllerIP="127.0.0.1"
+ControllerPort=8080
+
+DEBUG=0
+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)
+
+# @app.route("/wm/topology/route/<srcdpid>/<srcport>/<destdpid>/<destport>/json")
+#
+# Sample output:
+# {'dstPort': {'port': {'value': 0}, 'dpid': {'value': '00:00:00:00:00:00:00:02'}}, 'srcPort': {'port': {'value': 0}, 'dpid': {'value': '00:00:00:00:00:00:00:01'}}, 'flowEntries': [{'outPort': {'value': 1}, 'flowEntryErrorState': None, 'flowEntryMatch': None, 'flowEntryActions': None, 'inPort': {'value': 0}, 'flowEntryId': None, 'flowEntryUserState': 'FE_USER_UNKNOWN', 'dpid': {'value': '00:00:00:00:00:00:00:01'}, 'flowEntrySwitchState': 'FE_SWITCH_UNKNOWN'}, {'outPort': {'value': 0}, 'flowEntryErrorState': None, 'flowEntryMatch': None, 'flowEntryActions': None, 'inPort': {'value': 9}, 'flowEntryId': None, 'flowEntryUserState': 'FE_USER_UNKNOWN', 'dpid': {'value': '00:00:00:00:00:00:00:02'}, 'flowEntrySwitchState': 'FE_SWITCH_UNKNOWN'}]}
+#
+def shortest_path(v1, p1, v2, p2):
+ try:
+ command = "curl -s http://%s:%s/wm/topology/route/%s/%s/%s/%s/json" % (ControllerIP, ControllerPort, v1, p1, v2, p2)
+ debug("shortest_path %s" % command)
+
+ result = os.popen(command).read()
+ debug("result %s" % result)
+ if len(result) == 0:
+ log_error("No Path found")
+ exit(1);
+
+ parsedResult = json.loads(result)
+ debug("parsed %s" % parsedResult)
+
+ except:
+ log_error("Controller IF has issue")
+ exit(1)
+
+ srcSwitch = parsedResult['srcPort']['dpid']['value'];
+ srcPort = parsedResult['srcPort']['port']['value'];
+ dstSwitch = parsedResult['dstPort']['dpid']['value'];
+ dstPort = parsedResult['dstPort']['port']['value'];
+
+ print "DataPath: (src = %s/%s dst = %s/%s)" % (srcSwitch, srcPort, dstSwitch, dstPort);
+
+ for f in parsedResult['flowEntries']:
+ inPort = f['inPort']['value'];
+ outPort = f['outPort']['value'];
+ dpid = f['dpid']['value']
+ print "FlowEntry: (%s, %s, %s)" % (inPort, dpid, outPort)
+
+ return parsedResult
+
+def add_flow_path(flow_path):
+ try:
+ command = "curl -s -H 'Content-Type: application/json' -d '%s' http://%s:%s/wm/flow/add/json" % (flow_path, ControllerIP, ControllerPort)
+ debug("add_flow_path %s" % command)
+ result = os.popen(command).read()
+ debug("result %s" % result)
+ # parsedResult = json.loads(result)
+ # debug("parsed %s" % parsedResult)
+ except:
+ log_error("Controller IF has issue")
+ exit(1)
+
+if __name__ == "__main__":
+ usage_msg = "Usage: %s <flow-id> <installer-id> <src-dpid> <src-port> <dest-dpid> <dest-port>" % (sys.argv[0])
+
+ # app.debug = False;
+
+ # Usage info
+ if len(sys.argv) > 1 and (sys.argv[1] == "-h" or sys.argv[1] == "--help"):
+ print(usage_msg)
+ exit(0)
+
+ # Check arguments
+ if len(sys.argv) < 7:
+ log_error(usage_msg)
+ exit(1)
+
+ # Do the work
+ my_flow_id = sys.argv[1]
+ my_installer_id = sys.argv[2]; # 'ONOS-Path-Computation-Python'
+ data_path = shortest_path(sys.argv[3], sys.argv[4], sys.argv[5], sys.argv[6])
+
+ debug("Data Path: %s" % data_path)
+
+ flow_id = {}
+ flow_id['value'] = my_flow_id
+ installer_id = {}
+ installer_id['value'] = my_installer_id
+
+ flow_path = {}
+ flow_path['flowId'] = flow_id
+ flow_path['installerId'] = installer_id
+ flow_path['dataPath'] = data_path
+
+ flow_path_json = json.dumps(flow_path)
+ debug("Flow Path: %s" % flow_path_json)
+
+ add_flow_path(flow_path_json)
diff --git a/web/delete_flow.py b/web/delete_flow.py
new file mode 100755
index 0000000..f6f3d39
--- /dev/null
+++ b/web/delete_flow.py
@@ -0,0 +1,62 @@
+#! /usr/bin/env python
+# -*- Mode: python; py-indent-offset: 4; tab-width: 8; indent-tabs-mode: t; -*-
+
+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
+
+#
+# TODO: remove this! We don't use JSON argument here!
+# curl http://127.0.0.1:8080/wm/flow/delete/{"value":"0xf"}/json'
+#
+
+## Global Var ##
+ControllerIP="127.0.0.1"
+ControllerPort=8080
+
+DEBUG=0
+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)
+
+# @app.route("/wm/flow/delete/<flow-id>/json")
+def delete_flow_path(flow_id):
+ command = "curl -s \"http://%s:%s/wm/flow/delete/%s/json\"" % (ControllerIP, ControllerPort, flow_id)
+ debug("delete_flow_path %s" % command)
+ result = os.popen(command).read()
+ debug("result %s" % result)
+ # parsedResult = json.loads(result)
+ # debug("parsed %s" % parsedResult)
+
+if __name__ == "__main__":
+ usage_msg = "Usage: %s <flow_id>" % (sys.argv[0])
+
+ # app.debug = False;
+
+ # Usage info
+ if len(sys.argv) > 1 and (sys.argv[1] == "-h" or sys.argv[1] == "--help"):
+ print(usage_msg)
+ exit(0)
+
+ # Check arguments
+ if len(sys.argv) < 2:
+ log_error(usage_msg)
+ exit(1)
+
+ # Do the work
+ delete_flow_path(sys.argv[1]);
diff --git a/web/get_flow.py b/web/get_flow.py
new file mode 100755
index 0000000..dd6a8b6
--- /dev/null
+++ b/web/get_flow.py
@@ -0,0 +1,264 @@
+#! /usr/bin/env python
+# -*- Mode: python; py-indent-offset: 4; tab-width: 8; indent-tabs-mode: t; -*-
+
+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 ##
+ControllerIP="127.0.0.1"
+ControllerPort=8080
+
+DEBUG=0
+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)
+
+# @app.route("/wm/flow/get/<flow-id>/json")
+# Sample output:
+# {"flowId":{"value":"0x5"},"installerId":{"value":"FOOBAR"},"dataPath":{"srcPort":{"dpid":{"value":"00:00:00:00:00:00:00:01"},"port":{"value":0}},"dstPort":{"dpid":{"value":"00:00:00:00:00:00:00:02"},"port":{"value":0}},"flowEntries":[{"flowEntryId":"0x1389","flowEntryMatch":null,"flowEntryActions":null,"dpid":{"value":"00:00:00:00:00:00:00:01"},"inPort":{"value":0},"outPort":{"value":1},"flowEntryUserState":"FE_USER_DELETE","flowEntrySwitchState":"FE_SWITCH_NOT_UPDATED","flowEntryErrorState":null},{"flowEntryId":"0x138a","flowEntryMatch":null,"flowEntryActions":null,"dpid":{"value":"00:00:00:00:00:00:00:02"},"inPort":{"value":9},"outPort":{"value":0},"flowEntryUserState":"FE_USER_DELETE","flowEntrySwitchState":"FE_SWITCH_NOT_UPDATED","flowEntryErrorState":null}]}}
+
+def print_flow_path(parsedResult):
+ flowId = parsedResult['flowId']['value']
+ installerId = parsedResult['installerId']['value']
+ srcSwitch = parsedResult['dataPath']['srcPort']['dpid']['value']
+ srcPort = parsedResult['dataPath']['srcPort']['port']['value']
+ dstSwitch = parsedResult['dataPath']['dstPort']['dpid']['value']
+ dstPort = parsedResult['dataPath']['dstPort']['port']['value']
+
+ print "FlowPath: (flowId = %s installerId = %s src = %s/%s dst = %s/%s)" % (flowId, installerId, srcSwitch, srcPort, dstSwitch, dstPort)
+
+ for f in parsedResult['dataPath']['flowEntries']:
+ inPort = f['inPort']['value']
+ outPort = f['outPort']['value']
+ dpid = f['dpid']['value']
+ userState = f['flowEntryUserState']
+ switchState = f['flowEntrySwitchState']
+ match = f['flowEntryMatch'];
+ actions = f['flowEntryActions']
+ print " FlowEntry: (%s, %s, %s, %s, %s)" % (inPort, dpid, outPort, userState, switchState)
+
+ inPort = match['inPort']
+ matchInPort = match['matchInPort']
+ srcMac = match['srcMac']
+ matchSrcMac = match['matchSrcMac']
+ dstMac = match['dstMac']
+ matchDstMac = match['matchDstMac']
+ vlanId = match['vlanId']
+ matchVlanId = match['matchVlanId']
+ vlanPriority = match['vlanPriority']
+ matchVlanPriority = match['matchVlanPriority']
+ ethernetFrameType = match['ethernetFrameType']
+ matchEthernetFrameType = match['matchEthernetFrameType']
+ ipToS = match['ipToS']
+ matchIpToS = match['matchIpToS']
+ ipProto = match['ipProto']
+ matchIpProto = match['matchIpProto']
+ srcIPv4Net = match['srcIPv4Net']
+ matchSrcIPv4Net = match['matchSrcIPv4Net']
+ dstIPv4Net = match['dstIPv4Net']
+ matchDstIPv4Net = match['matchDstIPv4Net']
+ srcTcpUdpPort = match['srcTcpUdpPort']
+ matchSrcTcpUdpPort = match['matchSrcTcpUdpPort']
+ dstTcpUdpPort = match['dstTcpUdpPort']
+ matchDstTcpUdpPort = match['matchDstTcpUdpPort']
+ if matchInPort == True:
+ print " inPort: %s" % inPort['value']
+ if matchSrcMac == True:
+ print " srcMac: %s" % srcMac['value']
+ if matchDstMac == True:
+ print " dstMac: %s" % dstMac['value']
+ if matchVlanId == True:
+ print " vlanId: %s" % vlanId
+ if matchVlanPriority == True:
+ print " vlanPriority: %s" % vlanPriority
+ if matchEthernetFrameType == True:
+ print " ethernetFrameType: %s" % ethernetFrameType
+ if matchIpToS == True:
+ print " ipToS: %s" % ipToS
+ if matchIpProto == True:
+ print " ipProto: %s" % ipProto
+ if matchSrcIPv4Net == True:
+ print " srcIPv4Net: %s" % srcIPv4Net['value']
+ if matchDstIPv4Net == True:
+ print " dstIPv4Net: %s" % dstIPv4Net['value']
+ if matchSrcTcpUdpPort == True:
+ print " srcTcpUdpPort: %s" % srcTcpUdpPort
+ if matchDstTcpUdpPort == True:
+ print " dstTcpUdpPort: %s" % dstTcpUdpPort
+
+ for a in actions:
+ actionType = a['actionType']
+ if actionType == "ACTION_OUTPUT":
+ port = a['actionOutput']['port']
+ maxLen = a['actionOutput']['maxLen']
+ print " actionType: %s port: %s maxLen: %s" % (actionType, port, maxLen)
+ if actionType == "ACTION_SET_VLAN_VID":
+ vlanId = a['actionSetVlanId']['vlanId']
+ print " actionType: %s vlanId: %s" % (actionType, vlanId)
+ if actionType == "ACTION_SET_VLAN_PCP":
+ vlanPriority = a['actionSetVlanPriority']['vlanPriority']
+ print " actionType: %s vlanPriority: %s" % (actionType, vlanPriority)
+ if actionType == "ACTION_STRIP_VLAN":
+ stripVlan = a['actionStripVlan']['stripVlan']
+ print " actionType: %s stripVlan: %s" % (actionType, stripVlan)
+ if actionType == "ACTION_SET_DL_SRC":
+ setEthernetSrcAddr = a['actionSetEthernetSrcAddr']['addr']['value']
+ print " actionType: %s setEthernetSrcAddr: %s" % (actionType, setEthernetSrcAddr)
+ if actionType == "ACTION_SET_DL_DST":
+ setEthernetDstAddr = a['actionSetEthernetDstAddr']['addr']['value']
+ print " actionType: %s setEthernetDstAddr: %s" % (actionType, setEthernetDstAddr)
+ if actionType == "ACTION_SET_NW_SRC":
+ setIPv4SrcAddr = a['actionSetIPv4SrcAddr']['addr']['value']
+ print " actionType: %s setIPv4SrcAddr: %s" % (actionType, setIPv4SrcAddr)
+ if actionType == "ACTION_SET_NW_DST":
+ setIPv4DstAddr = a['actionSetIPv4DstAddr']['addr']['value']
+ print " actionType: %s setIPv4DstAddr: %s" % (actionType, setIPv4DstAddr)
+ if actionType == "ACTION_SET_NW_TOS":
+ setIpToS = a['actionSetIpToS']['ipToS']
+ print " actionType: %s setIpToS: %s" % (actionType, setIpToS)
+ if actionType == "ACTION_SET_TP_SRC":
+ setTcpUdpSrcPort = a['actionSetTcpUdpSrcPort']['port']
+ print " actionType: %s setTcpUdpSrcPort: %s" % (actionType, setTcpUdpSrcPort)
+ if actionType == "ACTION_SET_TP_DST":
+ setTcpUdpDstPort = a['actionSetTcpUdpDstPort']['port']
+ print " actionType: %s setTcpUdpDstPort: %s" % (actionType, setTcpUdpDstPort)
+ if actionType == "ACTION_ENQUEUE":
+ port = a['actionEnqueue']['port']['value']
+ queueId = a['actionEnqueue']['queueId']
+ print " actionType: %s port: %s queueId: %s" % (actionType, port, queueId)
+
+def get_flow_path(flow_id):
+ try:
+ command = "curl -s \"http://%s:%s/wm/flow/get/%s/json\"" % (ControllerIP, ControllerPort, flow_id)
+ debug("get_flow_path %s" % command)
+
+ result = os.popen(command).read()
+ debug("result %s" % result)
+ if len(result) == 0:
+ print "No Flow found"
+ return;
+
+ parsedResult = json.loads(result)
+ debug("parsed %s" % parsedResult)
+ except:
+ log_error("Controller IF has issue")
+ exit(1)
+
+ print_flow_path(parsedResult)
+
+
+def get_installer_flow_paths(installer_id, v1, p1, v2, p2):
+ try:
+ command = "curl -s \"http://%s:%s/wm/flow/getall-by-installer-id/%s/%s/%s/%s/%s/json\"" % (ControllerIP, ControllerPort, installer_id, v1, p1, v2, p2)
+ debug("get_installer_flow_paths %s" % command)
+
+ result = os.popen(command).read()
+ debug("result %s" % result)
+ if len(result) == 0:
+ print "No Flows found"
+ return;
+
+ parsedResult = json.loads(result)
+ debug("parsed %s" % parsedResult)
+ except:
+ log_error("Controller IF has issue")
+ exit(1)
+
+ for flowPath in parsedResult:
+ print_flow_path(flowPath)
+
+
+def get_endpoints_flow_paths(v1, p1, v2, p2):
+ try:
+ command = "curl -s \"http://%s:%s/wm/flow/getall-by-endpoints/%s/%s/%s/%s/json\"" % (ControllerIP, ControllerPort, v1, p1, v2, p2)
+ debug("get_endpoints_flow_paths %s" % command)
+
+ result = os.popen(command).read()
+ debug("result %s" % result)
+ if len(result) == 0:
+ print "No Flows found"
+ return;
+
+ parsedResult = json.loads(result)
+ debug("parsed %s" % parsedResult)
+ except:
+ log_error("Controller IF has issue")
+ exit(1)
+
+ for flowPath in parsedResult:
+ print_flow_path(flowPath)
+
+
+def get_all_flow_paths():
+ try:
+ command = "curl -s \"http://%s:%s/wm/flow/getall/json\"" % (ControllerIP, ControllerPort)
+ debug("get_all_flow_paths %s" % command)
+
+ result = os.popen(command).read()
+ debug("result %s" % result)
+ if len(result) == 0:
+ print "No Flows found"
+ return;
+
+ parsedResult = json.loads(result)
+ debug("parsed %s" % parsedResult)
+ except:
+ log_error("Controller IF has issue")
+ exit(1)
+
+ for flowPath in parsedResult:
+ print_flow_path(flowPath)
+
+if __name__ == "__main__":
+ usage_msg1 = "Usage:\n"
+ usage_msg2 = "%s <flow_id> : Print flow with Flow ID of <flow_id>\n" % (sys.argv[0])
+ usage_msg3 = " all : Print all flows\n"
+ usage_msg4 = " installer <installer-id> <src-dpid> <src-port> <dest-dpid> <dest-port>\n"
+ usage_msg5 = " endpoints <src-dpid> <src-port> <dest-dpid> <dest-port>"
+ usage_msg = usage_msg1 + usage_msg2 + usage_msg3 + usage_msg4 + usage_msg5;
+
+ # app.debug = False;
+
+ # Usage info
+ if len(sys.argv) > 1 and (sys.argv[1] == "-h" or sys.argv[1] == "--help"):
+ print(usage_msg)
+ exit(0)
+
+ # Check arguments
+ if len(sys.argv) < 2:
+ log_error(usage_msg)
+ exit(1)
+
+ # Do the work
+ if sys.argv[1] == "all":
+ get_all_flow_paths()
+ elif sys.argv[1] == "installer":
+ if len(sys.argv) < 7:
+ log_error(usage_msg)
+ exit(1)
+ get_installer_flow_paths(sys.argv[2], sys.argv[3], sys.argv[4],
+ sys.argv[5], sys.argv[6])
+ elif sys.argv[1] == "endpoints":
+ if len(sys.argv) < 6:
+ log_error(usage_msg)
+ exit(1)
+ get_endpoints_flow_paths(sys.argv[2], sys.argv[3], sys.argv[4],
+ sys.argv[5])
+ else:
+ get_flow_path(sys.argv[1])
diff --git a/web/js/controller-status.js b/web/js/controller-status.js
index 75cc5cc..1cca38f 100644
--- a/web/js/controller-status.js
+++ b/web/js/controller-status.js
@@ -24,6 +24,15 @@
attr("height", 50);
d3.json(data_source, draw);
+ setInterval(function() {
+ $.ajax({
+ url: data_source,
+ success: function(json) {
+ draw(json)
+ },
+ dataType: "json"
+ });
+ }, 5000);
function draw(json){
// var data = json.data;
@@ -159,15 +168,6 @@
cassandra_rect.exit().remove();
cassandra_text.exit().remove();
- setInterval(function() {
- $.ajax({
- url: data_source,
- success: function(json) {
- draw(json)
- },
- dataType: "json"
- });
- }, 3000);
}
/*
$("#more").click( function() {
diff --git a/web/js/onos-topology-route.js b/web/js/onos-topology-route.js
index 3b13b9c..cca644b 100644
--- a/web/js/onos-topology-route.js
+++ b/web/js/onos-topology-route.js
@@ -374,8 +374,7 @@
return "M" + d.source.x + "," + d.source.y + "A" + dr + "," + dr + " 0 0,1 " + d.target.x + "," + d.target.y;
});
- path
- .attr("stroke", function(d) {
+ path.attr("stroke", function(d) {
if(d.type == 1){
return "red"
} else {
diff --git a/web/js/onos-topology.js b/web/js/onos-topology.js
index 8573f7c..062d37b 100644
--- a/web/js/onos-topology.js
+++ b/web/js/onos-topology.js
@@ -208,23 +208,50 @@
}
}
}
+ for (var i = 0; i < links.length; i++) {
+ for (var j = 0; j < json.links.length; j++) {
+ if (links[i].target.name == json.nodes[json.links[j].target].name &&
+ links[i].source.name == json.nodes[json.links[j].source].name ){
+ if (links[i].type != json.links[j].type){
+ links[i].type = json.links[j].type;
+ changed = true;
+ }
+ }
+ }
+ }
return changed
}
function draw(force, path, circle, text){
force.stop();
path.enter().append("svg:path")
- .attr("class", function(d) { return "link"; });
+ .attr("class", function(d) { return "link"; })
+ .attr("marker-end", function(d) {
+ if(d.type == 1){
+ return "url(#TriangleRed)";
+ } else {
+ return "url(#Triangle)";
+ }
+ });
circle.enter().append("svg:circle")
- .attr("r", radius)
+ .attr("r", function(d) {
+ if (d.group == 1000){
+ return radius/2;
+ }else{
+ return radius;
+ }
+ })
.call(node_drag);
// .call(force.drag);
text.enter().append("svg:text")
.attr("x", radius)
.attr("y", ".31em")
- .text(function(d) { return d.name.split(":")[5] + d.name.split(":")[6] + d.name.split(":")[7] });
+ .text(function(d) {
+ l=d.name.split(":").length
+ return d.name.split(":")[l-3] + ":" + d.name.split(":")[l-2] + ":" + d.name.split(":")[l-1]
+ });
circle.append("title")
.text(function(d) { return d.name; });
@@ -234,6 +261,7 @@
else if (d.group == 2){return "blue";}
else if (d.group == 3){return "green";}
else if (d.group == 4){return "orange";}
+ else if (d.group == 1000){return "black";}
else{ return "gray"; }
});
@@ -245,7 +273,7 @@
}
}).attr("stroke-width", function(d) {
if(d.type == 1){
- return "4px";
+ return "2px";
} else {
return "1.5px";
}
@@ -271,13 +299,13 @@
var changed = cdiff(json);
console.log("changed? " + changed);
+ path = svg.selectAll("path").data(links)
+ circle = svg.selectAll("circle").data(nodes);
+ text = svg.selectAll("text").data(nodes);
+ console.log(path)
if (changed){
- path = svg.selectAll("path").data(links)
- circle = svg.selectAll("circle").data(nodes);
- text = svg.selectAll("text").data(nodes);
-
draw(force, path, circle, text);
}
}
@@ -299,8 +327,6 @@
});
}, 3000);
}
-
-
function tick() {
path.attr("d", function(d) {
var dx = d.target.x - d.source.x,
@@ -309,6 +335,26 @@
dr = 0; // 0 for direct line
return "M" + d.source.x + "," + d.source.y + "A" + dr + "," + dr + " 0 0,1 " + d.target.x + "," + d.target.y;
});
+ path.attr("stroke", function(d) {
+ if(d.type == 1){
+ return "red"
+ } else {
+ return "black"
+ }
+ }).attr("stroke-width", function(d) {
+ if(d.type == 1){
+ return "3px";
+ } else {
+ return "1.5px";
+ }
+ }).attr("marker-end", function(d) {
+ if(d.type == 1){
+ return "url(#TriangleRed)";
+ } else {
+ return "url(#Triangle)";
+ }
+ });
+
// circle.attr("cx", function(d) { return d.x; }).attr("cy", function(d) { return d.y; });
circle.attr("transform", function(d) {
x = Math.max(radius, Math.min(width - radius, d.x));
@@ -322,6 +368,7 @@
else if (d.group == 2){return "blue";}
else if (d.group == 3){return "green";}
else if (d.group == 4){return "orange";}
+ else if (d.group == 1000){return "black";}
else{ return "gray"; }
});
// text.attr("x", function(d) { return d.x; }).attr("y", function(d) { return d.y; });
diff --git a/web/onos-topology-route.html b/web/onos-topology-route.html
index 860a096..6c167c7 100644
--- a/web/onos-topology-route.html
+++ b/web/onos-topology-route.html
@@ -42,7 +42,7 @@
</marker>
</defs>
<script type="text/javascript">
-gui("http://onosnat.onlab.us:8080/wm/topology/toporoute/00:00:00:00:00:a1/2/00:00:00:00:00:c1/3/json");
+gui("http://onosnat.onlab.us:8080/wm/topology/toporoute/00:00:00:0d:00:d1/2/00:00:00:0d:00:d3/3/json");
</script>
</svg>
</body>
diff --git a/web/onos-topology.html b/web/onos-topology.html
index b0614c9..9d90ea4 100644
--- a/web/onos-topology.html
+++ b/web/onos-topology.html
@@ -2,11 +2,6 @@
<html>
<meta charset="utf-8">
<style>
-path.link {
- fill: none;
- stroke: #666;
- stroke-width: 1.5px;
-}
circle {
stroke: #333;
stroke-width: 1.5px;
@@ -40,6 +35,13 @@
orient="auto">
<path d="M0,-5L10,0L0,5"/>
</marker>
+ <marker id="TriangleRed"
+ viewBox="0 -5 10 10" refX="10" refY="-0.2"
+ markerUnits="strokeWidth"
+ markerWidth="6" markerHeight="6"
+ orient="auto">
+ <path d="M0,-5L10,0L0,5" fill="red" stroke="red"/>
+ </marker>
</defs>
<h1>ONOS Sprint 4 Demo GUI</h1>
<h2>Controller Status</h2>
diff --git a/web/shortest_path.py b/web/shortest_path.py
index 381d052..8a77d61 100755
--- a/web/shortest_path.py
+++ b/web/shortest_path.py
@@ -41,15 +41,21 @@
def shortest_path(v1, p1, v2, p2):
try:
command = "curl -s http://%s:%s/wm/topology/route/%s/%s/%s/%s/json" % (ControllerIP, ControllerPort, v1, p1, v2, p2)
+ debug("shortest_path %s" % command)
+
result = os.popen(command).read()
+ debug("result %s" % result)
+
+ if len(result) == 0:
+ print "No Path found"
+ return;
+
parsedResult = json.loads(result)
+ debug("parsed %s" % parsedResult)
except:
log_error("Controller IF has issue")
exit(1)
- debug("shortest_path %s" % command)
- debug("parsed %s" % parsedResult)
-
srcSwitch = parsedResult['srcPort']['dpid']['value'];
srcPort = parsedResult['srcPort']['port']['value'];
dstSwitch = parsedResult['dstPort']['dpid']['value'];
diff --git a/web/topology_rest.py b/web/topology_rest.py
index 5eaed03..b109ccc 100755
--- a/web/topology_rest.py
+++ b/web/topology_rest.py
@@ -114,6 +114,7 @@
topo = {}
switches = []
links = []
+ devices = []
for v in parsedResult:
if v.has_key('dpid'):
@@ -123,19 +124,45 @@
sw = {}
sw['name']=dpid
sw['group']= -1
- if state == "ACTIVE":
- if dpid.split(":")[5] == "0a":
- sw['group']=1
- if dpid.split(":")[5] == "0b":
- sw['group']=2
- if dpid.split(":")[5] == "0c":
- sw['group']=3
- if dpid.split(":")[5] == "0d":
- sw['group']=4
+
+# if state == "ACTIVE":
+# if dpid.split(":")[5] == "0a":
+# sw['group']=1
+# if dpid.split(":")[5] == "0b":
+# sw['group']=2
+# if dpid.split(":")[5] == "0c":
+# sw['group']=3
+# if dpid.split(":")[5] == "0d":
+# sw['group']=4
if state == "INACTIVE":
sw['group']=0
switches.append(sw)
+## Comment in if we need devies
+# sw_index = len(switches) - 1
+# for p in v['ports']:
+# for d in p['devices']:
+# device = {}
+# device['attached_switch']=dpid
+# device['name']=d['mac']
+# if d['state'] == "ACTIVE":
+# device['group']=1000
+# else:
+# device['group']=1001
+#
+# switches.append(device)
+# device_index = len (switches) -1
+# link = {}
+# link['source'] = device_index
+# link['target'] = sw_index
+# link['type'] = -1
+# links.append(link)
+# link = {}
+# link['source'] = sw_index
+# link['target'] = device_index
+# link['type'] = -1
+# links.append(link)
+
# try:
# command = "curl -s \'http://%s:%s/wm/registry/controllers/json\'" % (RestIP, RestPort)
# result = os.popen(command).read()
@@ -162,23 +189,26 @@
try:
v1 = "00:00:00:00:00:0a:0d:00"
+# v1 = "00:00:00:00:00:0d:00:d1"
p1=1
v2 = "00:00:00:00:00:0b:0d:03"
- p1=2
+# v2 = "00:00:00:00:00:0d:00:d3"
+ p2=1
command = "curl -s http://%s:%s/wm/topology/route/%s/%s/%s/%s/json" % (RestIP, RestPort, v1, p1, v2, p2)
result = os.popen(command).read()
parsedResult = json.loads(result)
except:
log_error("No route")
- parsedResult = []
+ parsedResult = {}
- path = [];
- for i, v in enumerate(parsedResult):
- if i < len(parsedResult) - 1:
- sdpid= parsedResult[i]['switch']
- ddpid = parsedResult[i+1]['switch']
- path.append( (sdpid, ddpid))
-
+ path = []
+ if parsedResult.has_key('flowEntries'):
+ flowEntries= parsedResult['flowEntries']
+ for i, v in enumerate(flowEntries):
+ if i < len(flowEntries) - 1:
+ sdpid= flowEntries[i]['dpid']['value']
+ ddpid = flowEntries[i+1]['dpid']['value']
+ path.append( (sdpid, ddpid))
try:
command = "curl -s \'http://%s:%s/wm/core/topology/links/json\'" % (RestIP, RestPort)
@@ -212,12 +242,11 @@
topo['nodes'] = switches
topo['links'] = links
-# pp.pprint(topo)
+ pp.pprint(topo)
js = json.dumps(topo)
resp = Response(js, status=200, mimetype='application/json')
return resp
-
#@app.route("/wm/topology/toporoute/00:00:00:00:00:a1/2/00:00:00:00:00:c1/3/json")
#@app.route("/wm/topology/toporoute/<srcdpid>/<srcport>/<destdpid>/<destport>/json")
@app.route("/wm/topology/toporoute/<v1>/<p1>/<v2>/<p2>/json")