Merge branch 'master' of github.com:OPENNETWORKINGLAB/ONOS
diff --git a/.gitignore b/.gitignore
index 3c5748b..f8dcd13 100644
--- a/.gitignore
+++ b/.gitignore
@@ -2,6 +2,7 @@
*.class
.classpath
.project
+.pydevproject
target
onos-logs
onos.log
\ No newline at end of file
diff --git a/pom.xml b/pom.xml
new file mode 100644
index 0000000..0d03e90
--- /dev/null
+++ b/pom.xml
@@ -0,0 +1,289 @@
+<?xml version="1.0"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+ <prerequisites>
+ <maven>3.0.4</maven>
+ </prerequisites>
+ <groupId>net.onrc.onos</groupId>
+ <artifactId>ONOS</artifactId>
+ <version>0.1.0</version>
+ <packaging>jar</packaging>
+ <name>ONOS</name>
+ <url>http://onlab.us/</url>
+ <repositories>
+ <repository>
+ <id>central</id>
+ <name>Maven Central repository</name>
+ <url>http://repo1.maven.org/maven2</url>
+ </repository>
+ <repository>
+ <id>maven-restlet</id>
+ <name>Public online Restlet repository</name>
+ <url>http://maven.restlet.org</url>
+ </repository>
+ <repository>
+ <id>tinkerpop-repository</id>
+ <name>TinkerPop Maven2 Repository</name>
+ <url>http://tinkerpop.com/maven2</url>
+ </repository>
+ </repositories>
+ <properties>
+ <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+ </properties>
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-install-plugin</artifactId>
+ <version>2.3.1</version>
+ <executions>
+ </executions>
+ </plugin>
+ <!-- guice maven plugin for dependency injection inside maven -->
+ <plugin>
+ <groupId>org.apache.camel</groupId>
+ <artifactId>guice-maven-plugin</artifactId>
+ <version>2.11.0</version>
+ </plugin>
+ <!-- compile -->
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-compiler-plugin</artifactId>
+ <version>2.3.2</version>
+ <configuration>
+ <source>1.6</source>
+ <target>1.6</target>
+ <encoding>UTF-8</encoding>
+ </configuration>
+ <executions>
+ </executions>
+ </plugin>
+ <!-- test -->
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-surefire-plugin</artifactId>
+ <version>2.12</version>
+ <configuration>
+ <excludes>
+ <!-- exclude all test cases for now -->
+ <!-- <exclude>**/storage/tests/StorageTest.java</exclude> -->
+ <!-- <exclude>**/test/*</exclude> -->
+ <exclude>**/test/*</exclude>
+ <exclude>**/Test*.java</exclude>
+ <exclude>**/*Test.java</exclude>
+ <exclude>**/*TestCase.java</exclude>
+ </excludes>
+ </configuration>
+ </plugin>
+ <!-- exec:java -->
+ <plugin>
+ <groupId>org.codehaus.mojo</groupId>
+ <artifactId>exec-maven-plugin</artifactId>
+ <version>1.2.1</version>
+ <configuration>
+ <mainClass>net.floodlightcontroller.core.Main</mainClass>
+ </configuration>
+ <executions>
+ </executions>
+ </plugin>
+ <plugin>
+ <groupId>org.codehaus.mojo</groupId>
+ <artifactId>build-helper-maven-plugin</artifactId>
+ <version>1.7</version>
+ <executions>
+ <execution>
+ <id>add-source</id>
+ <phase>generate-sources</phase>
+ <goals>
+ <goal>add-source</goal>
+ </goals>
+ <configuration>
+ <sources>
+ <source>lib/gen-java</source>
+ </sources>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+ </build>
+ <!-- for getting visualization reporting -->
+ <reporting>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.camel</groupId>
+ <artifactId>guice-maven-plugin</artifactId>
+ <version>2.11.0</version>
+ </plugin>
+ </plugins>
+ </reporting>
+ <dependencies>
+ <dependency>
+ <groupId>asm</groupId>
+ <artifactId>asm-tree</artifactId>
+ <version>3.0</version>
+ </dependency>
+ <dependency>
+ <groupId>net.sourceforge.cobertura</groupId>
+ <artifactId>cobertura</artifactId>
+ <version>1.9.4.1</version>
+ </dependency>
+ <dependency>
+ <groupId>ch.qos.logback</groupId>
+ <artifactId>logback-core</artifactId>
+ <version>1.0.0</version>
+ </dependency>
+ <dependency>
+ <groupId>org.objenesis</groupId>
+ <artifactId>objenesis</artifactId>
+ <version>1.2</version>
+ </dependency>
+ <dependency>
+ <groupId>com.google.guava</groupId>
+ <artifactId>guava</artifactId>
+ <version>13.0.1</version>
+ </dependency>
+ <dependency>
+ <groupId>ch.qos.logback</groupId>
+ <artifactId>logback-classic</artifactId>
+ <version>1.0.0</version>
+ <scope>runtime</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.codehaus.jackson</groupId>
+ <artifactId>jackson-core-asl</artifactId>
+ <version>1.9.11</version>
+ </dependency>
+ <dependency>
+ <groupId>org.codehaus.jackson</groupId>
+ <artifactId>jackson-mapper-asl</artifactId>
+ <version>1.9.11</version>
+ </dependency>
+ <dependency>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-api</artifactId>
+ <version>1.6.4</version>
+ </dependency>
+ <dependency>
+ <groupId>org.restlet.jse</groupId>
+ <artifactId>org.restlet</artifactId>
+ <version>2.1-RC1</version>
+ </dependency>
+ <dependency>
+ <groupId>org.restlet.jse</groupId>
+ <artifactId>org.restlet.ext.jackson</artifactId>
+ <version>2.1-RC1</version>
+ </dependency>
+ <dependency>
+ <groupId>org.restlet.jse</groupId>
+ <artifactId>org.restlet.ext.simple</artifactId>
+ <version>2.1-RC1</version>
+ </dependency>
+ <dependency>
+ <groupId>org.restlet.jse</groupId>
+ <artifactId>org.restlet.ext.slf4j</artifactId>
+ <version>2.1-RC1</version>
+ </dependency>
+ <dependency>
+ <groupId>org.simpleframework</groupId>
+ <artifactId>simple</artifactId>
+ <version>4.1.21</version>
+ </dependency>
+ <dependency>
+ <groupId>org.jboss.netty</groupId>
+ <artifactId>netty</artifactId>
+ <version>3.2.6.Final</version>
+ </dependency>
+ <dependency>
+ <groupId>args4j</groupId>
+ <artifactId>args4j</artifactId>
+ <version>2.0.16</version>
+ </dependency>
+ <dependency>
+ <groupId>com.googlecode.concurrentlinkedhashmap</groupId>
+ <artifactId>concurrentlinkedhashmap-lru</artifactId>
+ <version>1.2</version>
+ </dependency>
+ <dependency>
+ <groupId>org.python</groupId>
+ <artifactId>jython-standalone</artifactId>
+ <version>2.5.2</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.thrift</groupId>
+ <artifactId>libthrift</artifactId>
+ <version>0.7.0</version>
+ </dependency>
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ <version>4.8.2</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.easymock</groupId>
+ <artifactId>easymock</artifactId>
+ <version>3.1</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>cglib</groupId>
+ <artifactId>cglib-nodep</artifactId>
+ <version>2.2.2</version>
+ </dependency>
+ <dependency>
+ <groupId>com.thinkaurelius.titan</groupId>
+ <artifactId>titan-all</artifactId>
+ <version>0.2.1</version>
+ </dependency>
+ <dependency>
+ <groupId>com.google.inject</groupId>
+ <artifactId>guice</artifactId>
+ <version>3.0</version>
+ </dependency>
+ <dependency>
+ <groupId>com.tinkerpop</groupId>
+ <artifactId>frames</artifactId>
+ <version>2.3.0</version>
+ </dependency>
+ <dependency>
+ <groupId>com.tinkerpop.blueprints</groupId>
+ <artifactId>blueprints-core</artifactId>
+ <version>2.3.0</version>
+ </dependency>
+ <!-- dependency to locally modified version -->
+ <dependency>
+ <groupId>com.netflix.curator</groupId>
+ <artifactId>curator-framework</artifactId>
+ <version>1.3.5-SNAPSHOT</version>
+ </dependency>
+ <dependency>
+ <groupId>com.netflix.curator</groupId>
+ <artifactId>curator-client</artifactId>
+ <version>1.3.5-SNAPSHOT</version>
+ </dependency>
+ <dependency>
+ <groupId>com.netflix.curator</groupId>
+ <artifactId>curator-recipes</artifactId>
+ <version>1.3.5-SNAPSHOT</version>
+ </dependency>
+ <dependency>
+ <groupId>com.netflix.curator</groupId>
+ <artifactId>curator-x-discovery</artifactId>
+ <version>1.3.5-SNAPSHOT</version>
+ </dependency>
+ <!--
+ <dependency>
+ <groupId>net.floodlightcontroller</groupId>
+ <artifactId>packetstreamer-thrift</artifactId>
+ <version>0.1.0</version>
+ </dependency>
+ -->
+ <dependency>
+ <groupId>net.sf.json-lib</groupId>
+ <artifactId>json-lib</artifactId>
+ <version>2.4</version>
+ <classifier>jdk15</classifier>
+ </dependency>
+ </dependencies>
+</project>
diff --git a/scripts/all-linkup-hw.sh b/scripts/all-linkup-hw.sh
new file mode 100755
index 0000000..41d6be8
--- /dev/null
+++ b/scripts/all-linkup-hw.sh
@@ -0,0 +1,21 @@
+#!/bin/bash
+# BRINGS BACK UP ALL THE LINKS FOR THE HARDWARE DEMO AS SHOWN IN ONS 2013
+# link-hw.sh %s %s %s % (src_dpid, port1, cmd)
+
+./link-hw.sh 00:00:00:00:ba:5e:ba:11 24 up
+./link-hw.sh 00:00:00:00:ba:5e:ba:11 23 up
+
+./link-hw.sh 00:00:00:00:ba:5e:ba:13 22 up
+./link-hw.sh 00:00:00:00:ba:5e:ba:13 23 up
+
+./link-hw.sh 00:00:00:00:00:00:ba:12 23 up
+./link-hw.sh 00:00:00:00:00:00:ba:12 22 up
+./link-hw.sh 00:00:00:00:00:00:ba:12 24 up
+
+./link-hw.sh 00:01:00:16:97:08:9a:46 23 up
+./link-hw.sh 00:01:00:16:97:08:9a:46 24 up
+
+./link-hw.sh 00:00:20:4e:7f:51:8a:35 21 up
+./link-hw.sh 00:00:20:4e:7f:51:8a:35 22 up
+./link-hw.sh 00:00:20:4e:7f:51:8a:35 24 up
+./link-hw.sh 00:00:20:4e:7f:51:8a:35 23 up
diff --git a/scripts/neclink.exp b/scripts/neclink.exp
index a09ed11..0815843 100755
--- a/scripts/neclink.exp
+++ b/scripts/neclink.exp
@@ -1,6 +1,6 @@
#!/usr/bin/expect -f
# ./neclink.exp <ip of port> <'no' or blank>
-set timeout -1
+set timeout 5
set port [lindex $argv 0]
set no [lindex $argv 1]
diff --git a/scripts/prontolink.exp b/scripts/prontolink.exp
index 7786701..501cccd 100755
--- a/scripts/prontolink.exp
+++ b/scripts/prontolink.exp
@@ -1,6 +1,6 @@
#!/usr/bin/expect -f
# ./prontolink.exp <ip of switch> <port> <0 or 1 (on or off)>
-set timeout -1
+set timeout 5
set arg0 [lindex $argv 0]
set port [lindex $argv 1]
set onoff [lindex $argv 2]
diff --git a/scripts/run-onos-simple.sh b/scripts/run-onos-simple.sh
deleted file mode 100755
index 41b2cc6..0000000
--- a/scripts/run-onos-simple.sh
+++ /dev/null
@@ -1,13 +0,0 @@
-#!/bin/sh
-
-basedir=`dirname $0`
-onosdir="${basedir}/.."
-
-logfile="${onosdir}/logback.xml"
-jarfile="${onosdir}/target/floodlight-only.jar"
-classpath="${jarfile}:${onosdir}/lib/*:${onosdir}/lib/titan/*"
-mainclass="net.floodlightcontroller.core.Main"
-propfile="${onosdir}/onos.properties"
-
-#java -Dlogback.configurationFile=logback.xml -cp target/floodlight-only.jar:lib/*:lib/titan/* net.floodlightcontroller.core.Main -cf onos.properties
-java -Dlogback.configurationFile=${logfile} -cp ${classpath} ${mainclass} -cf ${propfile}
diff --git a/scripts/runiperf.sh b/scripts/runiperf.sh
index 7066bc6..4e26bd2 100755
--- a/scripts/runiperf.sh
+++ b/scripts/runiperf.sh
@@ -45,7 +45,7 @@
if (testbed == "SW"):
cmd="ssh -o StrictHostKeyChecking=no 1.1.%d.1 '/home/ubuntu/ONOS/scripts/iperf -u -t%s -i%s -k%s -yJ -o /home/ubuntu/ONOS/web/log/iperfclient_%s.out -c 192.168.%d.%d 2>&1 &' &" % (src_hostid, duration, interval, samples, flowid, dst_nwid, dst_hostid)
else:
- cmd="~/mininet/util/m g%sh%02d '/home/ubuntu/ONOS/scripts/iperf -u -t%s -i%s -k%s -yJ -o /home/ubuntu/ONOS/web/log/iperfclient_%s.out -c 192.168.%d.%d 2>&1 &' &" % (src_nwid, src_hostid, duration, interval, samples, flowid, dst_nwid, dst_hostid + 1)
+ cmd="~/mininet/util/m g%sh%02d '/home/ubuntu/ONOS/scripts/iperf -u -t%s -i%s -k%s -yJ -o /home/ubuntu/ONOS/web/log/iperfclient_%s.out -c 192.168.%d.%d 2>&1 &' &" % (src_nwid, src_hostid, duration, interval, samples, flowid, dst_nwid, dst_hostid )
killcmd='sudo pkill -KILL -f \"iperf .* -o .*/iperfclient_%s.out\"' % (flowid)
print killcmd
print cmd
diff --git a/setup-eclipse.sh b/setup-eclipse.sh
index a39dc62..f88377e 100755
--- a/setup-eclipse.sh
+++ b/setup-eclipse.sh
@@ -1,50 +1,4 @@
#!/bin/bash
-d=$(dirname $0)
-MAIN_CLASS=$1
-LIBRARIES=$2
-[ "${MAIN_CLASS}" ] || { echo "Run 'ant eclipse' to generate Eclipse project files"; exit 1; }
+mvn eclipse:eclipse
-
-cat >$d/.project <<EOF
-<?xml version="1.0" encoding="UTF-8"?>
-<projectDescription>
- <name>floodlight</name>
- <comment></comment>
- <projects>
- </projects>
- <buildSpec>
- <buildCommand>
- <name>org.eclipse.jdt.core.javabuilder</name>
- <arguments>
- </arguments>
- </buildCommand>
- </buildSpec>
- <natures>
- <nature>org.eclipse.jdt.core.javanature</nature>
- </natures>
-</projectDescription>
-EOF
-
-
-cat >$d/.classpath <<EOF
-<?xml version="1.0" encoding="UTF-8"?>
-<classpath>
- <classpathentry kind="src" path="src/main/java" output="target/bin"/>
- <classpathentry kind="src" path="src/main/resources"/>
- <classpathentry kind="src" path="src/test/java" output="target/bin-test"/>
- <classpathentry kind="src" path="lib/gen-java" output="target/bin"/>
-EOF
-(
-IFS=":"
-for l in ${LIBRARIES}; do
-cat >>$d/.classpath <<EOF
- <classpathentry exported="true" kind="lib" path="$l"/>
-EOF
-done
-)
-cat >>$d/.classpath <<EOF
- <classpathentry exported="true" kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
- <classpathentry kind="output" path="target/bin"/>
-</classpath>
-EOF
diff --git a/setup-local-jar.sh b/setup-local-jar.sh
new file mode 100755
index 0000000..1f72c44
--- /dev/null
+++ b/setup-local-jar.sh
@@ -0,0 +1,7 @@
+#!/bin/bash
+
+ mvn install:install-file -Dfile=./lib/curator-framework-1.3.5-SNAPSHOT.jar -DgroupId=com.netflix.curator -DartifactId=curator-framework -Dversion=1.3.5-SNAPSHOT -Dpackaging=jar -DgeneratePom=true
+ mvn install:install-file -Dfile=./lib/curator-client-1.3.5-SNAPSHOT.jar -DgroupId=com.netflix.curator -DartifactId=curator-client -Dversion=1.3.5-SNAPSHOT -Dpackaging=jar -DgeneratePom=true
+ mvn install:install-file -Dfile=./lib/curator-recipes-1.3.5-SNAPSHOT.jar -DgroupId=com.netflix.curator -DartifactId=curator-recipes -Dversion=1.3.5-SNAPSHOT -Dpackaging=jar -DgeneratePom=true
+ mvn install:install-file -Dfile=./lib/curator-x-discovery-1.3.5-SNAPSHOT.jar -DgroupId=com.netflix.curator -DartifactId=curator-x-discovery -Dversion=1.3.5-SNAPSHOT -Dpackaging=jar -DgeneratePom=true
+# mvn install:install-file -Dfile=./lib/packetstreamer-thrift-0.1.0.jar -DgroupId=net.floodlightcontroller -DartifactId=packetstreamer-thrift -Dversion=0.1.0 -Dpackaging=jar -DgeneratePom=true
diff --git a/src/main/java/net/floodlightcontroller/bgproute/BgpRoute.java b/src/main/java/net/floodlightcontroller/bgproute/BgpRoute.java
index 47f3d1a..517126c 100644
--- a/src/main/java/net/floodlightcontroller/bgproute/BgpRoute.java
+++ b/src/main/java/net/floodlightcontroller/bgproute/BgpRoute.java
@@ -1,73 +1,144 @@
package net.floodlightcontroller.bgproute;
-import java.io.BufferedReader;
-import java.io.File;
-import java.io.FileReader;
import java.io.IOException;
-import java.io.InputStreamReader;
-import java.net.HttpURLConnection;
-import java.net.MalformedURLException;
-import java.net.URL;
-import java.util.Collection;
-import java.util.Map;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
import java.util.ArrayList;
+import java.util.Collection;
import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import net.floodlightcontroller.core.IFloodlightProviderService;
+import net.floodlightcontroller.core.INetMapTopologyService.ITopoRouteService;
+import net.floodlightcontroller.core.IOFSwitch;
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.devicemanager.IDeviceService;
import net.floodlightcontroller.linkdiscovery.ILinkDiscovery.LDUpdate;
+import net.floodlightcontroller.packet.Ethernet;
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 net.floodlightcontroller.util.DataPath;
+import net.floodlightcontroller.util.Dpid;
+import net.floodlightcontroller.util.FlowEntry;
+import net.floodlightcontroller.util.IPv4;
+import net.floodlightcontroller.util.MACAddress;
+import net.floodlightcontroller.util.Port;
+import net.floodlightcontroller.util.SwitchPort;
import net.sf.json.JSONArray;
import net.sf.json.JSONObject;
import net.sf.json.JSONSerializer;
+import org.openflow.protocol.OFFlowMod;
+import org.openflow.protocol.OFMatch;
+import org.openflow.protocol.OFMessage;
+import org.openflow.protocol.OFPacketOut;
+import org.openflow.protocol.OFType;
+import org.openflow.protocol.action.OFAction;
+import org.openflow.protocol.action.OFActionDataLayerDestination;
+import org.openflow.protocol.action.OFActionOutput;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import com.google.common.net.InetAddresses;
+
public class BgpRoute implements IFloodlightModule, IBgpRouteService, ITopologyListener {
protected static Logger log = LoggerFactory.getLogger(BgpRoute.class);
protected IFloodlightProviderService floodlightProvider;
protected ITopologyService topology;
+ protected ITopoRouteService topoRouteService;
+ protected IDeviceService devices;
+ protected IRestApiService restApi;
protected static Ptree ptree;
- protected static String BGPdRestIp;
- protected static String RouterId;
+ protected String bgpdRestIp;
+ protected String routerId;
+ protected Set<InetAddress> routerIpAddresses;
+ //We need to identify our flows somehow. But like it says in LearningSwitch.java,
+ //the controller/OS should hand out cookie IDs to prevent conflicts.
+ protected final long APP_COOKIE = 0xa0000000000000L;
+ //Cookie for flows that do L2 forwarding within SDN domain to egress routers
+ protected final long L2_FWD_COOKIE = APP_COOKIE + 1;
+ //Cookie for flows in ingress switches that rewrite the MAC address
+ protected final long MAC_RW_COOKIE = APP_COOKIE + 2;
+ //Forwarding uses priority 0, and the mac rewrite entries in ingress switches
+ //need to be higher priority than this otherwise the rewrite may not get done
+ protected final short SDNIP_PRIORITY = 10;
+
+ //TODO temporary
+ protected List<GatewayRouter> gatewayRouters;
+
+ private void initGateways(){
+ gatewayRouters = new ArrayList<GatewayRouter>();
+ //00:00:00:00:00:00:0s0:a3 port 1
+ gatewayRouters.add(
+ new GatewayRouter(new SwitchPort(new Dpid(163L), new Port((short)1)),
+ new MACAddress(new byte[] {0x00, 0x00, 0x00, 0x00, 0x02, 0x01}),
+ new IPv4("192.168.10.1"),
+ new MACAddress(new byte[] {0x00, 0x00, 0x00, 0x00, 0x00, 0x01}),
+ new IPv4("192.168.10.101")));
+ //00:00:00:00:00:00:00:a5 port 1
+ //gatewayRouters.add(new SwitchPort(new Dpid(165L), new Port((short)1)));
+ gatewayRouters.add(
+ new GatewayRouter(new SwitchPort(new Dpid(165L), new Port((short)1)),
+ new MACAddress(new byte[] {0x00, 0x00, 0x00, 0x00, 0x02, 0x02}),
+ new IPv4("192.168.20.1"),
+ new MACAddress(new byte[] {0x00, 0x00, 0x00, 0x00, 0x00, 0x02}),
+ new IPv4("192.168.20.101")));
+ //00:00:00:00:00:00:00:a2 port 1
+ //gatewayRouters.add(new SwitchPort(new Dpid(162L), new Port((short)1)));
+ gatewayRouters.add(
+ new GatewayRouter(new SwitchPort(new Dpid(162L), new Port((short)1)),
+ new MACAddress(new byte[] {0x00, 0x00, 0x00, 0x00, 0x03, 0x01}),
+ new IPv4("192.168.30.1"),
+ new MACAddress(new byte[] {0x00, 0x00, 0x00, 0x00, 0x00, 0x03}),
+ new IPv4("192.168.30.101")));
+ //00:00:00:00:00:00:00:a6
+ //gatewayRouters.add(new SwitchPort(new Dpid(166L), new Port((short)1)));
+ gatewayRouters.add(
+ new GatewayRouter(new SwitchPort(new Dpid(166L), new Port((short)1)),
+ new MACAddress(new byte[] {0x00, 0x00, 0x00, 0x00, 0x04, 0x01}),
+ new IPv4("192.168.40.1"),
+ new MACAddress(new byte[] {0x00, 0x00, 0x00, 0x00, 0x00, 0x04}),
+ new IPv4("192.168.40.101")));
+
+ }
@Override
public Collection<Class<? extends IFloodlightService>> getModuleServices() {
- Collection<Class<? extends IFloodlightService>> l = new ArrayList<Class<? extends IFloodlightService>>();
+ 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>();
+ 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>>();
+ Collection<Class<? extends IFloodlightService>> l
+ = new ArrayList<Class<? extends IFloodlightService>>();
l.add(IFloodlightProviderService.class);
l.add(ITopologyService.class);
- l.add(IBgpRouteService.class);
+ l.add(ITopoRouteService.class);
+ l.add(IDeviceService.class);
+ l.add(IRestApiService.class);
return l;
}
@@ -75,31 +146,56 @@
public void init(FloodlightModuleContext context)
throws FloodlightModuleException {
+ initGateways();
+
ptree = new Ptree(32);
+
+ routerIpAddresses = new HashSet<InetAddress>();
// Register floodlight provider and REST handler.
floodlightProvider = context.getServiceImpl(IFloodlightProviderService.class);
- restApi = context.getServiceImpl(IRestApiService.class);
topology = context.getServiceImpl(ITopologyService.class);
+ topoRouteService = context.getServiceImpl(ITopoRouteService.class);
+ devices = context.getServiceImpl(IDeviceService.class);
+ restApi = context.getServiceImpl(IRestApiService.class);
+ //Read in config values
+ bgpdRestIp = context.getConfigParams(this).get("BgpdRestIp");
+ if (bgpdRestIp == null){
+ log.error("BgpdRestIp property not found in config file");
+ System.exit(1);
+ }
+ else {
+ log.info("BgpdRestIp set to {}", bgpdRestIp);
+ }
+
+ routerId = context.getConfigParams(this).get("RouterId");
+ if (routerId == null){
+ log.error("RouterId property not found in config file");
+ System.exit(1);
+ }
+ else {
+ log.info("RouterId set to {}", routerId);
+ }
// Test.
//test();
-
}
public Ptree getPtree() {
return ptree;
}
- public void clearPtree() {
- ptree = null;
- ptree = new Ptree(32);
-
+
+ public void clearPtree() {
+ //ptree = null;
+ ptree = new Ptree(32);
}
+
public String getBGPdRestIp() {
- return BGPdRestIp;
+ return bgpdRestIp;
}
+
public String getRouterId() {
- return RouterId;
+ return routerId;
}
// Return nexthop address as byte array.
@@ -114,17 +210,20 @@
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;
}
+ //TODO looks like this should be a unit test
@SuppressWarnings("unused")
- private void test() {
+ private void test() throws UnknownHostException {
System.out.println("Here it is");
Prefix p = new Prefix("128.0.0.0", 8);
Prefix q = new Prefix("8.0.0.0", 8);
@@ -179,119 +278,308 @@
}
+ private void retrieveRib(){
+ String url = "http://" + bgpdRestIp + "/wm/bgp/" + routerId;
+ String response = RestClient.get(url);
+
+ if (response.equals("")){
+ return;
+ }
+
+ response = response.replaceAll("\"", "'");
+ JSONObject jsonObj = (JSONObject) JSONSerializer.toJSON(response);
+ JSONArray rib_json_array = jsonObj.getJSONArray("rib");
+ String router_id = jsonObj.getString("router-id");
+
+ int size = rib_json_array.size();
+
+ log.info("Retrived RIB of {} entries from BGPd", size);
+
+ for (int j = 0; j < size; j++) {
+ JSONObject second_json_object = rib_json_array.getJSONObject(j);
+ String prefix = second_json_object.getString("prefix");
+ String nexthop = second_json_object.getString("nexthop");
+
+ //insert each rib entry into the local rib;
+ String[] substring = prefix.split("/");
+ String prefix1 = substring[0];
+ String mask1 = substring[1];
+
+ Prefix p;
+ try {
+ p = new Prefix(prefix1, Integer.valueOf(mask1));
+ } catch (NumberFormatException e) {
+ log.warn("Wrong mask format in RIB JSON: {}", mask1);
+ continue;
+ } catch (UnknownHostException e1) {
+ log.warn("Wrong prefix format in RIB JSON: {}", prefix1);
+ continue;
+ }
+
+ 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;
+
+ prefixAdded(node);
+ }
+ }
+
+ public void prefixAdded(PtreeNode node) {
+ //Add a flow to rewrite mac for this prefix to all border switches
+ GatewayRouter thisRouter = null;
+ for (GatewayRouter router : gatewayRouters){
+ if (router.getRouterIp().value() ==
+ InetAddresses.coerceToInteger(node.rib.nextHop)){
+ thisRouter = router;
+ break;
+ }
+ }
+
+ if (thisRouter == null){
+ //TODO local router isn't in gateway list so this will get thrown
+ //Need to work out what to do about local prefixes with next hop 0.0.0.0.
+ log.error("Couldn't find next hop router in router {} in config"
+ , node.rib.nextHop.toString());
+ return; //just quit out here? This is probably a configuration error
+ }
+
+ for (GatewayRouter ingressRouter : gatewayRouters){
+ if (ingressRouter == thisRouter) {
+ continue;
+ }
+
+ DataPath shortestPath = topoRouteService.getShortestPath(
+ ingressRouter.getAttachmentPoint(),
+ thisRouter.getAttachmentPoint());
+
+ if (shortestPath == null){
+ log.debug("Shortest path between {} and {} not found",
+ ingressRouter.getAttachmentPoint(),
+ thisRouter.getAttachmentPoint());
+ return; // just quit here?
+ }
+
+ //TODO check the shortest path against the cached version we
+ //calculated before. If they don't match up that's a problem
+
+ //Set up the flow mod
+ OFFlowMod fm =
+ (OFFlowMod) floodlightProvider.getOFMessageFactory()
+ .getMessage(OFType.FLOW_MOD);
+
+ fm.setIdleTimeout((short)0)
+ .setHardTimeout((short)0)
+ .setBufferId(OFPacketOut.BUFFER_ID_NONE)
+ .setCookie(MAC_RW_COOKIE)
+ .setCommand(OFFlowMod.OFPFC_ADD)
+ //.setMatch(match)
+ //.setActions(actions)
+ .setPriority(SDNIP_PRIORITY)
+ .setLengthU(OFFlowMod.MINIMUM_LENGTH
+ + OFActionDataLayerDestination.MINIMUM_LENGTH
+ + OFActionOutput.MINIMUM_LENGTH);
+
+ OFMatch match = new OFMatch();
+ match.setDataLayerType(Ethernet.TYPE_IPv4);
+ match.setWildcards(match.getWildcards() & ~OFMatch.OFPFW_DL_TYPE);
+
+ match.setDataLayerSource(ingressRouter.getRouterMac().toBytes());
+ match.setWildcards(match.getWildcards() & ~OFMatch.OFPFW_DL_SRC);
+
+ //match.setDataLayerDestination(ingressRouter.getSdnRouterMac().toBytes());
+ //match.setWildcards(match.getWildcards() & ~OFMatch.OFPFW_DL_DST);
+
+ InetAddress address = null;
+ try {
+ address = InetAddress.getByAddress(node.key);
+ } catch (UnknownHostException e1) {
+ //Should never happen is the reverse conversion has already been done
+ log.error("Malformed IP address");
+ return;
+ }
+
+ match.setFromCIDR(address.getHostAddress() + "/" + node.rib.masklen, OFMatch.STR_NW_DST);
+ fm.setMatch(match);
+
+ //Set up MAC rewrite action
+ OFActionDataLayerDestination macRewriteAction = new OFActionDataLayerDestination();
+ macRewriteAction.setDataLayerAddress(thisRouter.getRouterMac().toBytes());
+
+ //Set up output action
+ OFActionOutput outputAction = new OFActionOutput();
+ outputAction.setMaxLength((short)0xffff); //TODO check what this is (and if needed for mac rewrite)
+
+ Port outputPort = shortestPath.flowEntries().get(0).outPort();
+ outputAction.setPort(outputPort.value());
+
+ List<OFAction> actions = new ArrayList<OFAction>();
+ actions.add(macRewriteAction);
+ actions.add(outputAction);
+ fm.setActions(actions);
+
+ //Write to switch
+ IOFSwitch sw = floodlightProvider.getSwitches()
+ .get(ingressRouter.getAttachmentPoint().dpid().value());
+
+ if (sw == null){
+ log.warn("Switch not found when pushing flow mod");
+ continue;
+ }
+
+ List<OFMessage> msglist = new ArrayList<OFMessage>();
+ msglist.add(fm);
+ try {
+ sw.write(msglist, null);
+ sw.flush();
+ } catch (IOException e) {
+ log.error("Failure writing flow mod", e);
+ }
+ }
+ }
+
+ public void prefixDeleted(PtreeNode node) {
+ //Remove MAC rewriting flows from other border switches
+
+ }
+
+ /*
+ * On startup we need to calculate a full mesh of paths between all gateway
+ * switches
+ */
+ private void calculateFullMesh(){
+ Map<IOFSwitch, SwitchPort> gatewaySwitches = new HashMap<IOFSwitch, SwitchPort>();
+
+ //have to account for switches not being there, paths not being found.
+
+ //for (SwitchPort switchPort : gatewayRouters){
+ for (GatewayRouter router : gatewayRouters){
+ SwitchPort switchPort = router.getAttachmentPoint();
+
+ IOFSwitch sw = floodlightProvider.getSwitches().get(switchPort.dpid().value());
+
+ if (sw == null){
+ log.debug("Gateway switch {} not here yet", switchPort.dpid().value());
+ return; // just quit here?
+ }
+
+ //Only need to know 1 external-facing port from each gateway switch
+ //which we can feed into shortest path calculation
+ if (!gatewaySwitches.containsKey(sw)){
+ gatewaySwitches.put(sw, switchPort);
+ }
+
+ }
+ log.debug("size {}", gatewaySwitches.size());
+
+ //For each border router, calculate and install a path from every other
+ //border switch to said border router. However, don't install the entry
+ //in to the first hop switch, as we need to install an entry to rewrite
+ //for each prefix received. This will be done later when prefixes have
+ //actually been received.
+
+ for (GatewayRouter dstRouter : gatewayRouters){
+ SwitchPort routerAttachmentPoint = dstRouter.getAttachmentPoint();
+ for (Map.Entry<IOFSwitch, SwitchPort> src : gatewaySwitches.entrySet()) {
+
+ if (routerAttachmentPoint.dpid().value() ==
+ src.getKey().getId()){
+ continue;
+ }
+
+ DataPath shortestPath = topoRouteService.getShortestPath(
+ src.getValue(), routerAttachmentPoint);
+
+ if (shortestPath == null){
+ log.debug("Shortest path between {} and {} not found",
+ src.getValue(), routerAttachmentPoint);
+ return; // just quit here?
+ }
+
+ //install flows
+ installPath(shortestPath.flowEntries(), dstRouter);
+ }
+ }
+ }
+
+ private void installPath(List<FlowEntry> flowEntries, GatewayRouter router){
+
+ //Set up the flow mod
+ OFFlowMod fm =
+ (OFFlowMod) floodlightProvider.getOFMessageFactory()
+ .getMessage(OFType.FLOW_MOD);
+
+ OFActionOutput action = new OFActionOutput();
+ action.setMaxLength((short)0xffff);
+ List<OFAction> actions = new ArrayList<OFAction>();
+ actions.add(action);
+
+ fm.setIdleTimeout((short)0)
+ .setHardTimeout((short)0)
+ .setBufferId(OFPacketOut.BUFFER_ID_NONE)
+ .setCookie(L2_FWD_COOKIE)
+ .setCommand(OFFlowMod.OFPFC_ADD)
+ //.setMatch(match)
+ .setActions(actions)
+ .setLengthU(OFFlowMod.MINIMUM_LENGTH+OFActionOutput.MINIMUM_LENGTH);
+
+ //Don't push the first flow entry. We need to push entries in the
+ //first switch based on IP prefix which we don't know yet.
+ for (int i = 1; i < flowEntries.size(); i++){
+ FlowEntry flowEntry = flowEntries.get(i);
+
+ OFMatch match = new OFMatch();
+ match.setDataLayerDestination(router.getRouterMac().toBytes());
+ match.setWildcards(match.getWildcards() & ~OFMatch.OFPFW_DL_DST);
+ ((OFActionOutput) fm.getActions().get(0)).setPort(flowEntry.outPort().value());
+
+ fm.setMatch(match);
+
+ IOFSwitch sw = floodlightProvider.getSwitches().get(flowEntry.dpid().value());
+
+ if (sw == null){
+ log.warn("Switch not found when pushing flow mod");
+ continue;
+ }
+
+ List<OFMessage> msglist = new ArrayList<OFMessage>();
+ msglist.add(fm);
+ try {
+ sw.write(msglist, null);
+ sw.flush();
+ } catch (IOException e) {
+ log.error("Failure writing flow mod", e);
+ }
+
+ try {
+ fm = fm.clone();
+ } catch (CloneNotSupportedException e1) {
+ log.error("Failure cloning flow mod", e1);
+ }
+ }
+ }
+
@Override
public void startUp(FloodlightModuleContext context) {
restApi.addRestletRoutable(new BgpRouteWebRoutable());
- topology.addListener((ITopologyListener) this);
+ topology.addListener(this);
- // get the BGPdRestIp and RouterId from transit-route-pusher.py
- File file = new File("/home/ubuntu/sdn/transit-route-pusher.py");
-
-
- try{
- BufferedReader input = new BufferedReader (new FileReader(file));
- String text;
- int is_BGPdRestIp=0;
- int is_RouterId=0;
-
- while((text = input.readLine()) != null && (is_BGPdRestIp == 0) || (is_RouterId == 0) ){
-
- if(is_BGPdRestIp == 1 && is_RouterId ==1)
- {break;}
-
- if(is_BGPdRestIp == 0 && text.contains("BGPdRestIp") ){
- String[] temp = text.split("\"");
- BGPdRestIp = temp[1];
- is_BGPdRestIp = 1;
-
-
- }else if (is_RouterId == 0 && text.contains("RouterId") ){
-
- String[] temp = text.split("\"");
- RouterId = temp[1];
- is_RouterId = 1;
-
-
- }
-
- }
-
-
- } catch(Exception e){
- e.printStackTrace();
- }
-
-
- // automatically get the rib from bgpd at the ONOS initiation process.
- String dest=RouterId;
- String str="http://"+BGPdRestIp+"/wm/bgp/"+dest;
-
-
- 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());
- }
-
- BufferedReader br = new BufferedReader(new InputStreamReader((conn.getInputStream())));
- StringBuffer res = new StringBuffer();
- String line;
- while ((line = br.readLine()) != null) {
- res.append(line);
- }
-
- String res2=res.toString().replaceAll("\"", "'");
- JSONObject jsonObj = (JSONObject) JSONSerializer.toJSON(res2);
- JSONArray rib_json_array = jsonObj.getJSONArray("rib");
- String router_id = jsonObj.getString("router-id");
-
- int size = rib_json_array.size();
- System.out.print("size:"+size+"\n");
- for (int j = 0; j < size; j++) {
- JSONObject second_json_object = rib_json_array.getJSONObject(j);
- String prefix = second_json_object.getString("prefix");
- String nexthop = second_json_object.getString("nexthop");
-
- //insert each rib entry into the local rib;
- String[] substring= prefix.split("/");
- String prefix1=substring[0];
- String mask1=substring[1];
-
- Prefix p = new Prefix(prefix1, Integer.valueOf(mask1));
- 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;
-
- }
- br.close();
- conn.disconnect();
-
- } catch (MalformedURLException e) {
-
- e.printStackTrace();
-
- } catch (IOException e) {
-
- e.printStackTrace();
-
- }
-
-
+ //Retrieve the RIB from BGPd during startup
+ retrieveRib();
+
+ //Don't have to do this as we'll never have switches connected here
+ //calculateFullMesh();
}
@Override
public void topologyChanged() {
+ //Probably need to look at all changes, not just port changes
+ /*
boolean change = false;
String changelog = "";
@@ -307,7 +595,15 @@
log.info ("received topo change" + changelog);
if (change) {
- RestClient.get ("http://localhost:5000/topo_change");
+ //RestClient.get ("http://localhost:5000/topo_change");
}
+ */
+
+ for (LDUpdate update : topology.getLastLinkUpdates()){
+ log.debug("{} event causing internal L2 path recalculation",
+ update.getOperation().toString());
+
+ }
+ calculateFullMesh();
}
}
diff --git a/src/main/java/net/floodlightcontroller/bgproute/BgpRouteResource.java b/src/main/java/net/floodlightcontroller/bgproute/BgpRouteResource.java
index 28d9621..4ad1e95 100644
--- a/src/main/java/net/floodlightcontroller/bgproute/BgpRouteResource.java
+++ b/src/main/java/net/floodlightcontroller/bgproute/BgpRouteResource.java
@@ -1,193 +1,212 @@
package net.floodlightcontroller.bgproute;
+import java.net.UnknownHostException;
+
+import org.restlet.resource.Delete;
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;
-import java.io.UnsupportedEncodingException;
-import java.nio.ByteBuffer;
public class BgpRouteResource extends ServerResource {
-
- protected static Logger log = LoggerFactory
- .getLogger(BgpRouteResource.class);
-
+
+ protected static Logger log = LoggerFactory.getLogger(BgpRouteResource.class);
+
private String addrToString(byte [] addr) {
- String str = "";
-
+ String str = "";
+
for (int i = 0; i < 4; i++) {
int val = (addr[i] & 0xff);
str += val;
if (i != 3)
str += ".";
}
-
+
return str;
}
-
- @SuppressWarnings("unused")
+
+ //@SuppressWarnings("unused")
@Get
- public String get(String fmJson) {
- String linpp=fmJson;
- String dest = (String) getRequestAttributes().get("dest");
- String output = "";
- IBgpRouteService bgpRoute = (IBgpRouteService)getContext().getAttributes().
- get(IBgpRouteService.class.getCanonicalName());
+ 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) {
+ //TODO Needs to be changed to use the new RestClient.get().
- if (dest != null) {
- Prefix p = new Prefix(dest, 32);
- if (p == null) {
- return "[GET]: dest address format is wrong";
- }
-
- // the dest here refers to router-id
- //BGPdRestIp includes port number, such as 1.1.1.1:8080
- String BGPdRestIp = bgpRoute.getBGPdRestIp();
- String url="http://"+BGPdRestIp+"/wm/bgp/"+dest;
-
-
-
- RestClient.get(url);
- output="Get rib from bgpd finished!\n";
- return output;
- } 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";
-
- }
+ //Prefix p;
+ //try {
+ // p = new Prefix(dest, 32);
+ //} catch (UnknownHostException e) {
+ //if (p == null) {
+ // return "[GET]: dest address format is wrong";
+ //}
+
+ // the dest here refers to router-id
+ //bgpdRestIp includes port number, such as 1.1.1.1:8080
+ String BGPdRestIp = bgpRoute.getBGPdRestIp();
+ String url="http://"+BGPdRestIp+"/wm/bgp/"+dest;
+
+ RestClient.get(url);
+
+ output="Get rib from bgpd finished!\n";
return output;
+ }
+ 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;
+ }
- public static ByteBuffer toByteBuffer(String value) throws UnsupportedEncodingException
- {
- return ByteBuffer.wrap(value.getBytes("UTF-8"));
- }
+ //unused?
+ /*
+ public static ByteBuffer toByteBuffer(String value) throws UnsupportedEncodingException {
+ return ByteBuffer.wrap(value.getBytes("UTF-8"));
+ }
+ */
-public static String toString(ByteBuffer buffer) throws UnsupportedEncodingException
- {
- byte[] bytes = new byte[buffer.remaining()];
- buffer.get(bytes);
- return new String(bytes, "UTF-8");
-
- }
+ //unused?
+ /*
+ public static String toString(ByteBuffer buffer) throws UnsupportedEncodingException {
+ byte[] bytes = new byte[buffer.remaining()];
+ buffer.get(bytes);
+ return new String(bytes, "UTF-8");
+ }
+ */
-
@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");
+ 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 = "";
-
+
if (capability == null) {
-
// this is a prefix add
- Prefix p = new Prefix(prefix, Integer.valueOf(mask));
+ Prefix p;
+ try {
+ p = new Prefix(prefix, Integer.valueOf(mask));
+ } catch (NumberFormatException e) {
+ reply = "[POST: mask format is wrong]";
+ log.info(reply);
+ return reply + "\n";
+ } catch (UnknownHostException e1) {
+ reply = "[POST: prefix format is wrong]";
+ log.info(reply);
+ return reply + "\n";
+ }
+
PtreeNode node = ptree.acquire(p.getAddress(), p.masklen);
- Rib rib = new Rib(router_id, nexthop, p.masklen);
+ Rib rib = new Rib(routerId, nexthop, p.masklen);
if (node.rib != null) {
node.rib = null;
ptree.delReference(node);
}
node.rib = rib;
+
+ bgpRoute.prefixAdded(node);
reply = "[POST: " + prefix + "/" + mask + ":" + nexthop + "]";
log.info(reply);
-
-
- }else if(capability.equals("1")){
- reply = "[POST-capability: " + capability + "]\n";
- log.info(reply);
- // to store the number in the top node of the Ptree
-
- }else{
- reply = "[POST-capability: " + capability + "]\n";
- log.info(reply);
- // to store the number in the top node of the Ptree
-
}
-
-
+ else if(capability.equals("1")) {
+ reply = "[POST-capability: " + capability + "]\n";
+ log.info(reply);
+ // to store the number in the top node of the Ptree
+ }
+ else {
+ reply = "[POST-capability: " + capability + "]\n";
+ log.info(reply);
+ // to store the number in the top node of the Ptree
+ }
+
return reply + "\n";
-
-
}
-
+
@Delete
public String delete(String fmJson) {
IBgpRouteService bgpRoute = (IBgpRouteService)getContext().getAttributes().
- get(IBgpRouteService.class.getCanonicalName());
+ get(IBgpRouteService.class.getCanonicalName());
- Ptree ptree = bgpRoute.getPtree();
-
+ 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 = "";
-
+
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 =reply + "[DELE: " + prefix + "/" + mask + ":" + nextHop + "]";
-
- }else {
+ // this is a prefix delete
+ Prefix p;
+ try {
+ p = new Prefix(prefix, Integer.valueOf(mask));
+ } catch (NumberFormatException e) {
+ reply = "[DELE: mask format is wrong]";
+ log.info(reply);
+ return reply + "\n";
+ } catch (UnknownHostException e1) {
+ reply = "[DELE: prefix format is wrong]";
+ log.info(reply);
+ return reply + "\n";
+ }
+
+ 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);
+ }
+ }
+
+ bgpRoute.prefixDeleted(node);
+ reply =reply + "[DELE: " + prefix + "/" + mask + ":" + nextHop + "]";
+ }
+ else {
// clear the local rib: Ptree
bgpRoute.clearPtree();
reply = "[DELE-capability: " + capability + "; The local Rib is cleared!]\n";
-
-
+
// to store the number in the top node of the Ptree
-
- }
+ }
+
log.info(reply);
-
return reply + "\n";
}
}
diff --git a/src/main/java/net/floodlightcontroller/bgproute/BgpRouteResourceSynch.java b/src/main/java/net/floodlightcontroller/bgproute/BgpRouteResourceSynch.java
index d0c337a..ff7aea6 100644
--- a/src/main/java/net/floodlightcontroller/bgproute/BgpRouteResourceSynch.java
+++ b/src/main/java/net/floodlightcontroller/bgproute/BgpRouteResourceSynch.java
@@ -6,7 +6,6 @@
import org.restlet.resource.ServerResource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import net.floodlightcontroller.restclient.RestClient;
public class BgpRouteResourceSynch extends ServerResource {
@@ -29,7 +28,7 @@
String BGPdRestIp = bgpRoute.getBGPdRestIp();
- //BGPdRestIp includes port number, such as 1.1.1.1:8080
+ //bgpdRestIp includes port number, such as 1.1.1.1:8080
RestClient.post("http://"+BGPdRestIp+"/wm/bgp/"+router_id+"/"+prefix+"/"+mask+"/"+nexthop);
}catch(Exception e)
{e.printStackTrace();}
diff --git a/src/main/java/net/floodlightcontroller/bgproute/GatewayRouter.java b/src/main/java/net/floodlightcontroller/bgproute/GatewayRouter.java
new file mode 100644
index 0000000..56d5243
--- /dev/null
+++ b/src/main/java/net/floodlightcontroller/bgproute/GatewayRouter.java
@@ -0,0 +1,46 @@
+package net.floodlightcontroller.bgproute;
+
+import net.floodlightcontroller.util.IPv4;
+import net.floodlightcontroller.util.MACAddress;
+import net.floodlightcontroller.util.SwitchPort;
+
+public class GatewayRouter {
+ private SwitchPort attachmentPoint;
+ private MACAddress routerMac;
+ private IPv4 routerIp;
+
+ //For now, put in the IP and MAC of the SDN domain's router that this
+ //gateway will be communicating with
+ private MACAddress sdnRouterMac;
+ private IPv4 sdnRouterIp;
+
+ public GatewayRouter(SwitchPort attachmentPoint, MACAddress routerMac,
+ IPv4 routerIp, MACAddress sdnRouterMac, IPv4 sdnRouterIp) {
+ this.attachmentPoint = attachmentPoint;
+ this.routerMac = routerMac;
+ this.routerIp = routerIp;
+ this.sdnRouterIp = sdnRouterIp;
+ this.sdnRouterMac = sdnRouterMac;
+ }
+
+ public SwitchPort getAttachmentPoint() {
+ return attachmentPoint;
+ }
+
+ public MACAddress getRouterMac() {
+ return routerMac;
+ }
+
+ public IPv4 getRouterIp() {
+ return routerIp;
+ }
+
+ //TODO delete if not needed
+ public MACAddress getSdnRouterMac() {
+ return sdnRouterMac;
+ }
+
+ public IPv4 getSdnRouterIp() {
+ return sdnRouterIp;
+ }
+}
diff --git a/src/main/java/net/floodlightcontroller/bgproute/IBgpRouteService.java b/src/main/java/net/floodlightcontroller/bgproute/IBgpRouteService.java
index a6025ef..0e95d9e 100644
--- a/src/main/java/net/floodlightcontroller/bgproute/IBgpRouteService.java
+++ b/src/main/java/net/floodlightcontroller/bgproute/IBgpRouteService.java
@@ -4,15 +4,17 @@
public interface IBgpRouteService extends IFloodlightService {
- public Rib lookupRib(byte[] dest);
-
- public Ptree getPtree();
-
- public String getBGPdRestIp();
-
- public String getRouterId();
-
- public void clearPtree() ;
-
-
+ public Rib lookupRib(byte[] dest);
+
+ public Ptree getPtree();
+
+ public String getBGPdRestIp();
+
+ public String getRouterId();
+
+ public void clearPtree();
+
+ //TODO This functionality should be provided by some sort of Ptree listener framework
+ public void prefixAdded(PtreeNode node);
+ public void prefixDeleted(PtreeNode node);
}
diff --git a/src/main/java/net/floodlightcontroller/bgproute/Prefix.java b/src/main/java/net/floodlightcontroller/bgproute/Prefix.java
index 7ba014b..980646f 100644
--- a/src/main/java/net/floodlightcontroller/bgproute/Prefix.java
+++ b/src/main/java/net/floodlightcontroller/bgproute/Prefix.java
@@ -7,28 +7,27 @@
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;
- }
+ public Prefix(byte[] addr, int masklen) throws UnknownHostException {
+ //try {
+ address = InetAddress.getByAddress(addr);
+ //} catch (UnknownHostException e) {
+ // System.out.println("InetAddress exception");
+ // return;
+ //}
this.masklen = masklen;
- System.out.println(address.toString() + "/" + 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;
- }
+
+ public Prefix(String str, int masklen) throws UnknownHostException {
+ //try {
+ address = InetAddress.getByName(str);
+ //} 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/RestClient.java b/src/main/java/net/floodlightcontroller/bgproute/RestClient.java
new file mode 100644
index 0000000..ef5e38b
--- /dev/null
+++ b/src/main/java/net/floodlightcontroller/bgproute/RestClient.java
@@ -0,0 +1,104 @@
+package net.floodlightcontroller.bgproute;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.net.HttpURLConnection;
+import java.net.MalformedURLException;
+import java.net.URL;
+
+import org.apache.commons.httpclient.ConnectTimeoutException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+
+public class RestClient {
+ protected static Logger log = LoggerFactory.getLogger(RestClient.class);
+
+ public static String get(String str) {
+ StringBuilder response = new StringBuilder();
+
+ try {
+
+ URL url = new URL(str);
+ HttpURLConnection conn = (HttpURLConnection) url.openConnection();
+ conn.setConnectTimeout(2 * 1000); //2 seconds
+ conn.setRequestMethod("GET");
+ conn.setRequestProperty("Accept", "application/json");
+
+ if (conn.getResponseCode() != 200) {
+ throw new RuntimeException("Failed : HTTP error code : "
+ + conn.getResponseCode());
+ }
+
+ if (!conn.getContentType().equals("application/json")){
+ log.warn("The content received from {} is not json", str);
+ }
+
+ BufferedReader br = new BufferedReader(new InputStreamReader((conn.getInputStream())));
+ String line;
+ while ((line = br.readLine()) != null) {
+ response.append(line);
+ }
+
+ br.close();
+ conn.disconnect();
+
+ } catch (MalformedURLException e) {
+ log.error("Malformed URL for GET request", e);
+ } catch (ConnectTimeoutException e) {
+ log.warn("Couldn't connect remote REST server");
+ } catch (IOException e) {
+ log.warn("Couldn't connect remote REST server");
+ }
+
+ return response.toString();
+ }
+
+ public static void post (String str) {
+
+ try {
+ URL url = new URL(str);
+ HttpURLConnection conn = (HttpURLConnection) url.openConnection();
+ conn.setDoOutput(true);
+ conn.setRequestMethod("POST");
+ conn.setRequestProperty("Content-Type", "application/json");
+
+ if (conn.getResponseCode() != 200) {
+ throw new RuntimeException("Failed : HTTP error code : "
+ + conn.getResponseCode());
+ }
+
+ conn.disconnect();
+
+ } catch (MalformedURLException e) {
+ log.error("Malformed URL for GET request", e);
+ } catch (IOException e) {
+ log.warn("Couldn't connect remote REST server");
+ }
+ }
+
+
+ public static void delete (String str) {
+
+ try {
+ URL url = new URL(str);
+ HttpURLConnection conn = (HttpURLConnection) url.openConnection();
+ conn.setRequestMethod("DELETE");
+ conn.setRequestProperty("Accept", "application/json");
+
+
+ if (conn.getResponseCode() != 200) {
+ throw new RuntimeException("Failed : HTTP error code : "
+ + conn.getResponseCode());
+ }
+
+ conn.disconnect();
+
+ } catch (MalformedURLException e) {
+ log.error("Malformed URL for GET request", e);
+ } catch (IOException e) {
+ log.warn("Couldn't connect remote REST server");
+ }
+ }
+}
diff --git a/src/main/java/net/floodlightcontroller/core/INetMapTopologyService.java b/src/main/java/net/floodlightcontroller/core/INetMapTopologyService.java
index ecf217e..f217c25 100644
--- a/src/main/java/net/floodlightcontroller/core/INetMapTopologyService.java
+++ b/src/main/java/net/floodlightcontroller/core/INetMapTopologyService.java
@@ -1,6 +1,7 @@
package net.floodlightcontroller.core;
import java.util.List;
+import java.util.Map;
import net.floodlightcontroller.core.module.IFloodlightService;
import net.floodlightcontroller.core.INetMapTopologyObjects.IDeviceObject;
@@ -47,7 +48,7 @@
/**
* Fetch the Switch and Ports info from the Titan Graph
- * and store it locally for fast access during the shortest path
+ * and return it for fast access during the shortest path
* computation.
*
* After fetching the state, method @ref getTopoShortestPath()
@@ -63,14 +64,17 @@
* method @ref dropShortestPathTopo() should be used to release
* the internal state that is not needed anymore:
*
- * prepareShortestPathTopo();
+ * Map<Long, ?> shortestPathTopo;
+ * shortestPathTopo = prepareShortestPathTopo();
* for (int i = 0; i < 10000; i++) {
- * dataPath = getTopoShortestPath(...);
+ * dataPath = getTopoShortestPath(shortestPathTopo, ...);
* ...
* }
- * dropShortestPathTopo();
+ * dropShortestPathTopo(shortestPathTopo);
+ *
+ * @return the Shortest Path info handler stored in a map.
*/
- void prepareShortestPathTopo();
+ Map<Long, ?> prepareShortestPathTopo();
/**
* Release the state that was populated by
@@ -78,8 +82,10 @@
*
* See the documentation for method @ref prepareShortestPathTopo()
* for additional information and usage.
+ *
+ * @shortestPathTopo the Shortest Path info handler to release.
*/
- void dropShortestPathTopo();
+ void dropShortestPathTopo(Map<Long, ?> shortestPathTopo);
/**
* Get the shortest path from a source to a destination by
@@ -89,12 +95,15 @@
* See the documentation for method @ref prepareShortestPathTopo()
* for additional information and usage.
*
+ * @paran shortestPathTopoHandler the Shortest Path info handler
+ * to use.
* @param src the source in the shortest path computation.
* @param dest the destination in the shortest path computation.
* @return the data path with the computed shortest path if
* found, otherwise null.
*/
- DataPath getTopoShortestPath(SwitchPort src, SwitchPort dest);
+ DataPath getTopoShortestPath(Map<Long, ?> shortestPathTopo,
+ SwitchPort src, SwitchPort dest);
/**
* Test whether a route exists from a source to a destination.
diff --git a/src/main/java/net/floodlightcontroller/core/IOFSwitchListener.java b/src/main/java/net/floodlightcontroller/core/IOFSwitchListener.java
index 1bc258b..f8dd52c 100644
--- a/src/main/java/net/floodlightcontroller/core/IOFSwitchListener.java
+++ b/src/main/java/net/floodlightcontroller/core/IOFSwitchListener.java
@@ -17,6 +17,8 @@
package net.floodlightcontroller.core;
+import org.openflow.protocol.OFPhysicalPort;
+
/**
*
*
@@ -44,6 +46,16 @@
public void switchPortChanged(Long switchId);
/**
+ * Fired when ports on a switch area added
+ */
+ public void switchPortAdded(Long switchId, OFPhysicalPort port);
+
+ /**
+ * Fired when ports on a switch area removed
+ */
+ public void switchPortRemoved(Long switchId, OFPhysicalPort port);
+
+ /**
* The name assigned to this listener
* @return
*/
diff --git a/src/main/java/net/floodlightcontroller/core/internal/Controller.java b/src/main/java/net/floodlightcontroller/core/internal/Controller.java
index 0d49c03..842ef35 100644
--- a/src/main/java/net/floodlightcontroller/core/internal/Controller.java
+++ b/src/main/java/net/floodlightcontroller/core/internal/Controller.java
@@ -147,7 +147,6 @@
public class Controller implements IFloodlightProviderService,
IStorageSourceListener {
- protected SwitchStorageImpl swStore;;
protected static Logger log = LoggerFactory.getLogger(Controller.class);
@@ -265,18 +264,26 @@
public enum SwitchUpdateType {
ADDED,
REMOVED,
- PORTCHANGED
+ PORTCHANGED,
+ PORTADDED,
+ PORTREMOVED
}
/**
* Update message indicating a switch was added or removed
*/
protected class SwitchUpdate implements IUpdate {
public IOFSwitch sw;
+ public OFPhysicalPort port;
public SwitchUpdateType switchUpdateType;
public SwitchUpdate(IOFSwitch sw, SwitchUpdateType switchUpdateType) {
this.sw = sw;
this.switchUpdateType = switchUpdateType;
}
+ public SwitchUpdate(IOFSwitch sw, OFPhysicalPort port, SwitchUpdateType switchUpdateType) {
+ this.sw = sw;
+ this.port = port;
+ this.switchUpdateType = switchUpdateType;
+ }
public void dispatch() {
if (log.isTraceEnabled()) {
log.trace("Dispatching switch update {} {}",
@@ -294,6 +301,14 @@
case PORTCHANGED:
listener.switchPortChanged(sw.getId());
break;
+ case PORTADDED:
+ listener.switchPortAdded(sw.getId(), port);
+ break;
+ case PORTREMOVED:
+ listener.switchPortRemoved(sw.getId(), port);
+ break;
+ default:
+ break;
}
}
}
@@ -1269,23 +1284,43 @@
((OFPortState.OFPPS_LINK_DOWN.getValue() & port.getState()) > 0);
sw.setPort(port);
if (!portDown) {
- swStore.addPort(sw.getStringId(), port);
+ SwitchUpdate update = new SwitchUpdate(sw, port, SwitchUpdateType.PORTADDED);
+ try {
+ this.updates.put(update);
+ } catch (InterruptedException e) {
+ log.error("Failure adding update to queue", e);
+ }
} else {
- swStore.deletePort(sw.getStringId(), port.getPortNumber());
+ SwitchUpdate update = new SwitchUpdate(sw, port, SwitchUpdateType.PORTREMOVED);
+ try {
+ this.updates.put(update);
+ } catch (InterruptedException e) {
+ log.error("Failure adding update to queue", e);
+ }
}
if (updateStorage)
updatePortInfo(sw, port);
log.debug("Port #{} modified for {}", portNumber, sw);
} else if (m.getReason() == (byte)OFPortReason.OFPPR_ADD.ordinal()) {
sw.setPort(port);
- swStore.addPort(sw.getStringId(), port);
+ SwitchUpdate update = new SwitchUpdate(sw, port, SwitchUpdateType.PORTADDED);
+ try {
+ this.updates.put(update);
+ } catch (InterruptedException e) {
+ log.error("Failure adding update to queue", e);
+ }
if (updateStorage)
updatePortInfo(sw, port);
log.debug("Port #{} added for {}", portNumber, sw);
} else if (m.getReason() ==
(byte)OFPortReason.OFPPR_DELETE.ordinal()) {
sw.deletePort(portNumber);
- swStore.deletePort(sw.getStringId(), portNumber);
+ SwitchUpdate update = new SwitchUpdate(sw, port, SwitchUpdateType.PORTREMOVED);
+ try {
+ this.updates.put(update);
+ } catch (InterruptedException e) {
+ log.error("Failure adding update to queue", e);
+ }
if (updateStorage)
removePortInfo(sw, portNumber);
log.debug("Port #{} deleted for {}", portNumber, sw);
@@ -1559,12 +1594,6 @@
}
updateActiveSwitchInfo(sw);
- if (registryService.hasControl(sw.getId())) {
- swStore.update(sw.getStringId(), SwitchState.ACTIVE, DM_OPERATION.UPDATE);
- for (OFPhysicalPort port: sw.getPorts()) {
- swStore.addPort(sw.getStringId(), port);
- }
- }
SwitchUpdate update = new SwitchUpdate(sw, SwitchUpdateType.ADDED);
try {
this.updates.put(update);
@@ -1584,14 +1613,6 @@
// this method is only called after netty has processed all
// pending messages
log.debug("removeSwitch: {}", sw);
- //
- // Cannot set sw to inactive in network map due to race condition
- // Need a cleanup thread to periodically check switches not active in registry
- // and acquire control to set to inactive state in network map and release it
- //
- // if (registryService.hasControl(sw.getId())) {
- // swStore.update(sw.getStringId(), SwitchState.INACTIVE, DM_OPERATION.UPDATE);
- // }
if (!this.activeSwitches.remove(sw.getId(), sw) || !sw.isConnected()) {
log.debug("Not removing switch {}; already removed", sw);
return;
@@ -2220,8 +2241,6 @@
log.debug("did not get DB config setting using default {}", conf);
}
log.debug("setting DB config {}", conf);
- this.swStore = new SwitchStorageImpl();
- this.swStore.init(conf);
initVendorMessages();
this.systemStartTime = System.currentTimeMillis();
diff --git a/src/main/java/net/floodlightcontroller/flowcache/FlowManager.java b/src/main/java/net/floodlightcontroller/flowcache/FlowManager.java
index 307919a..8aec20f 100644
--- a/src/main/java/net/floodlightcontroller/flowcache/FlowManager.java
+++ b/src/main/java/net/floodlightcontroller/flowcache/FlowManager.java
@@ -1,6 +1,8 @@
package net.floodlightcontroller.flowcache;
import java.io.IOException;
+import java.io.PrintWriter;
+import java.io.StringWriter;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
@@ -11,6 +13,7 @@
import java.util.List;
import java.util.Map;
import java.util.Random;
+import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
@@ -87,9 +90,16 @@
private static int nextFlowEntryIdSuffix = 0;
private static long nextFlowEntryId = 0;
+ // State for measurement purpose
private static long measurementFlowId = 100000;
private static String measurementFlowIdStr = "0x186a0"; // 100000
private long modifiedMeasurementFlowTime = 0;
+ //
+ private LinkedList<FlowPath> measurementStoredPaths = new LinkedList<FlowPath>();
+ private long measurementStartTimeProcessingPaths = 0;
+ private long measurementEndTimeProcessingPaths = 0;
+ Map<Long, ?> measurementShortestPathTopo = null;
+ private String measurementPerFlowStr = new String();
/** The logger. */
private static Logger log = LoggerFactory.getLogger(FlowManager.class);
@@ -269,7 +279,8 @@
// Fetch and recompute the Shortest Path for those
// Flow Paths this controller is responsible for.
//
- topoRouteService.prepareShortestPathTopo();
+ Map<Long, ?> shortestPathTopo =
+ topoRouteService.prepareShortestPathTopo();
Iterable<IFlowPath> allFlowPaths = conn.utils().getAllFlowPaths(conn);
for (IFlowPath flowPathObj : allFlowPaths) {
counterAllFlowPaths++;
@@ -344,7 +355,8 @@
// to avoid closing the transaction.
//
DataPath dataPath =
- topoRouteService.getTopoShortestPath(srcSwitchPort,
+ topoRouteService.getTopoShortestPath(shortestPathTopo,
+ srcSwitchPort,
dstSwitchPort);
if (dataPath == null) {
// We need the DataPath to compare the paths
@@ -369,7 +381,7 @@
conn.utils().removeFlowPath(conn, flowPathObj);
}
- topoRouteService.dropShortestPathTopo();
+ topoRouteService.dropShortestPathTopo(shortestPathTopo);
conn.endTx(Transaction.COMMIT);
@@ -462,7 +474,7 @@
shortestPathReconcileScheduler = Executors.newScheduledThreadPool(1);
}
- private long getNextFlowEntryId() {
+ private synchronized long getNextFlowEntryId() {
//
// Generate the next Flow Entry ID.
// NOTE: For now, the higher 32 bits are random, and
@@ -531,8 +543,14 @@
} catch (Exception e) {
// TODO: handle exceptions
conn.endTx(Transaction.ROLLBACK);
- log.error(":addFlow FlowId:{} failed",
- flowPath.flowId().toString());
+
+ StringWriter sw = new StringWriter();
+ e.printStackTrace(new PrintWriter(sw));
+ String stacktrace = sw.toString();
+
+ log.error(":addFlow FlowId:{} failed: {}",
+ flowPath.flowId().toString(),
+ stacktrace);
}
if (flowObj == null) {
log.error(":addFlow FlowId:{} failed: Flow object not created",
@@ -745,6 +763,73 @@
}
/**
+ * Delete all previously added flows.
+ *
+ * @return true on success, otherwise false.
+ */
+ @Override
+ public boolean deleteAllFlows() {
+ List<Thread> threads = new LinkedList<Thread>();
+ final ConcurrentLinkedQueue<FlowId> concurrentAllFlowIds =
+ new ConcurrentLinkedQueue<FlowId>();
+
+ // Get all Flow IDs
+ Iterable<IFlowPath> allFlowPaths = conn.utils().getAllFlowPaths(conn);
+ for (IFlowPath flowPathObj : allFlowPaths) {
+ if (flowPathObj == null)
+ continue;
+ String flowIdStr = flowPathObj.getFlowId();
+ if (flowIdStr == null)
+ continue;
+ FlowId flowId = new FlowId(flowIdStr);
+ concurrentAllFlowIds.add(flowId);
+ }
+
+ // Delete all flows one-by-one
+ for (FlowId flowId : concurrentAllFlowIds)
+ deleteFlow(flowId);
+
+ /*
+ * TODO: A faster mechanism to delete the Flow Paths by using
+ * a number of threads. Commented-out for now.
+ */
+ /*
+ //
+ // Create the threads to delete the Flow Paths
+ //
+ for (int i = 0; i < 10; i++) {
+ Thread thread = new Thread(new Runnable() {
+ @Override
+ public void run() {
+ while (true) {
+ FlowId flowId = concurrentAllFlowIds.poll();
+ if (flowId == null)
+ return;
+ deleteFlow(flowId);
+ }
+ }}, "Delete All Flow Paths");
+ threads.add(thread);
+ }
+
+ // Start processing
+ for (Thread thread : threads) {
+ thread.start();
+ }
+
+ // Wait for all threads to complete
+ for (Thread thread : threads) {
+ try {
+ thread.join();
+ } catch (InterruptedException e) {
+ log.debug("Exception waiting for a thread to delete a Flow Path: ", e);
+ }
+ }
+ */
+
+ return true;
+ }
+
+ /**
* Delete a previously added flow.
*
* @param flowId the Flow ID of the flow to delete.
@@ -807,6 +892,35 @@
}
/**
+ * Clear the state for all previously added flows.
+ *
+ * @return true on success, otherwise false.
+ */
+ @Override
+ public boolean clearAllFlows() {
+ List<FlowId> allFlowIds = new LinkedList<FlowId>();
+
+ // Get all Flow IDs
+ Iterable<IFlowPath> allFlowPaths = conn.utils().getAllFlowPaths(conn);
+ for (IFlowPath flowPathObj : allFlowPaths) {
+ if (flowPathObj == null)
+ continue;
+ String flowIdStr = flowPathObj.getFlowId();
+ if (flowIdStr == null)
+ continue;
+ FlowId flowId = new FlowId(flowIdStr);
+ allFlowIds.add(flowId);
+ }
+
+ // Clear all flows one-by-one
+ for (FlowId flowId : allFlowIds) {
+ clearFlow(flowId);
+ }
+
+ return true;
+ }
+
+ /**
* Clear the state for a previously added flow.
*
* @param flowId the Flow ID of the flow to clear.
@@ -1763,4 +1877,209 @@
//
return (installRemoteFlowEntry(flowPath, flowEntry));
}
+
+ /**
+ * Store a path flow for measurement purpose.
+ *
+ * NOTE: The Flow Path argument does NOT contain flow entries.
+ * The Shortest Path is computed, and the corresponding Flow Entries
+ * are stored in the Flow Path.
+ *
+ * @param flowPath the Flow Path with the endpoints and the match
+ * conditions to store.
+ * @return the stored shortest-path flow on success, otherwise null.
+ */
+ @Override
+ public synchronized FlowPath measurementStorePathFlow(FlowPath flowPath) {
+ //
+ // Prepare the Shortest Path computation if the first Flow Path
+ //
+ if (measurementStoredPaths.isEmpty())
+ measurementShortestPathTopo = topoRouteService.prepareShortestPathTopo();
+
+ //
+ // Compute the Shortest Path
+ //
+ DataPath dataPath =
+ topoRouteService.getTopoShortestPath(measurementShortestPathTopo,
+ flowPath.dataPath().srcPort(),
+ flowPath.dataPath().dstPort());
+ if (dataPath == null) {
+ // We need the DataPath to populate the Network MAP
+ dataPath = new DataPath();
+ dataPath.setSrcPort(flowPath.dataPath().srcPort());
+ dataPath.setDstPort(flowPath.dataPath().dstPort());
+ }
+
+ //
+ // Set the incoming port matching and the outgoing port output
+ // actions for each flow entry.
+ //
+ for (FlowEntry flowEntry : dataPath.flowEntries()) {
+ // Set the incoming port matching
+ FlowEntryMatch flowEntryMatch = new FlowEntryMatch();
+ flowEntry.setFlowEntryMatch(flowEntryMatch);
+ flowEntryMatch.enableInPort(flowEntry.inPort());
+
+ // Set the outgoing port output action
+ ArrayList<FlowEntryAction> flowEntryActions = flowEntry.flowEntryActions();
+ if (flowEntryActions == null) {
+ flowEntryActions = new ArrayList<FlowEntryAction>();
+ flowEntry.setFlowEntryActions(flowEntryActions);
+ }
+ FlowEntryAction flowEntryAction = new FlowEntryAction();
+ flowEntryAction.setActionOutput(flowEntry.outPort());
+ flowEntryActions.add(flowEntryAction);
+ }
+
+ //
+ // Prepare the computed Flow Path
+ //
+ FlowPath computedFlowPath = new FlowPath();
+ computedFlowPath.setFlowId(new FlowId(flowPath.flowId().value()));
+ computedFlowPath.setInstallerId(new CallerId(flowPath.installerId().value()));
+ computedFlowPath.setDataPath(dataPath);
+ computedFlowPath.setFlowEntryMatch(new FlowEntryMatch(flowPath.flowEntryMatch()));
+
+ //
+ // Add the computed Flow Path to the internal storage
+ //
+ measurementStoredPaths.add(computedFlowPath);
+
+ log.debug("Measurement storing path {}",
+ computedFlowPath.flowId().toString());
+
+ return (computedFlowPath);
+ }
+
+ /**
+ * Install path flows for measurement purpose.
+ *
+ * @param numThreads the number of threads to use to install the path
+ * flows.
+ * @return true on success, otherwise false.
+ */
+ @Override
+ public boolean measurementInstallPaths(Integer numThreads) {
+ // Create a copy of the Flow Paths to install
+ final ConcurrentLinkedQueue<FlowPath> measurementProcessingPaths =
+ new ConcurrentLinkedQueue<FlowPath>(measurementStoredPaths);
+
+ /**
+ * A Thread-wrapper class for executing the threads and collecting
+ * the measurement data.
+ */
+ class MyThread extends Thread {
+ public long[] execTime = new long[2000];
+ public int samples = 0;
+ public int threadId = -1;
+ @Override
+ public void run() {
+ while (true) {
+ FlowPath flowPath = measurementProcessingPaths.poll();
+ if (flowPath == null)
+ return;
+ // Install the Flow Path
+ FlowId flowId = new FlowId();
+ String dataPathSummaryStr =
+ flowPath.dataPath().dataPathSummary();
+ long startTime = System.nanoTime();
+ addFlow(flowPath, flowId, dataPathSummaryStr);
+ long endTime = System.nanoTime();
+ execTime[samples] = endTime - startTime;
+ samples++;
+ }
+ }
+ };
+
+ List<MyThread> threads = new LinkedList<MyThread>();
+
+ log.debug("Measurement Installing {} flows",
+ measurementProcessingPaths.size());
+
+ //
+ // Create the threads to install the Flow Paths
+ //
+ for (int i = 0; i < numThreads; i++) {
+ MyThread thread = new MyThread();
+ thread.threadId = i;
+ threads.add(thread);
+ }
+
+ //
+ // Start processing
+ //
+ measurementEndTimeProcessingPaths = 0;
+ measurementStartTimeProcessingPaths = System.nanoTime();
+ for (Thread thread : threads) {
+ thread.start();
+ }
+
+ // Wait for all threads to complete
+ for (Thread thread : threads) {
+ try {
+ thread.join();
+ } catch (InterruptedException e) {
+ log.debug("Exception waiting for a thread to install a Flow Path: ", e);
+ }
+ }
+
+ // Record the end of processing
+ measurementEndTimeProcessingPaths = System.nanoTime();
+
+ //
+ // Prepare the string with measurement data per each Flow Path
+ // installation.
+ // The string is multiple lines: one line per Flow Path installation:
+ // ThreadAndTimePerFlow <ThreadId> <TotalThreads> <Time(ns)>
+ //
+ measurementPerFlowStr = new String();
+ String eol = System.getProperty("line.separator");
+ for (MyThread thread : threads) {
+ for (int i = 0; i < thread.samples; i++) {
+ measurementPerFlowStr += "ThreadAndTimePerFlow " + thread.threadId + " " + numThreads + " " + thread.execTime[i] + eol;
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * Get the measurement time that took to install the path flows.
+ *
+ * @return the measurement time (in nanoseconds) it took to install
+ * the path flows.
+ */
+ @Override
+ public Long measurementGetInstallPathsTimeNsec() {
+ return new Long(measurementEndTimeProcessingPaths -
+ measurementStartTimeProcessingPaths);
+ }
+
+ /**
+ * Get the measurement install time per Flow.
+ *
+ * @return a multi-line string with the following format per line:
+ * ThreadAndTimePerFlow <ThreadId> <TotalThreads> <Time(ns)>
+ */
+ @Override
+ public String measurementGetPerFlowInstallTime() {
+ return new String(measurementPerFlowStr);
+ }
+
+ /**
+ * Clear the path flows stored for measurement purpose.
+ *
+ * @return true on success, otherwise false.
+ */
+ @Override
+ public boolean measurementClearAllPaths() {
+ measurementStoredPaths.clear();
+ topoRouteService.dropShortestPathTopo(measurementShortestPathTopo);
+ measurementStartTimeProcessingPaths = 0;
+ measurementEndTimeProcessingPaths = 0;
+ measurementPerFlowStr = new String();
+
+ return true;
+ }
}
diff --git a/src/main/java/net/floodlightcontroller/flowcache/IFlowService.java b/src/main/java/net/floodlightcontroller/flowcache/IFlowService.java
index 855f064..6c19fd0 100644
--- a/src/main/java/net/floodlightcontroller/flowcache/IFlowService.java
+++ b/src/main/java/net/floodlightcontroller/flowcache/IFlowService.java
@@ -29,6 +29,13 @@
String dataPathSummaryStr);
/**
+ * Delete all previously added flows.
+ *
+ * @return true on success, otherwise false.
+ */
+ boolean deleteAllFlows();
+
+ /**
* Delete a previously added flow.
*
* @param flowId the Flow ID of the flow to delete.
@@ -37,6 +44,13 @@
boolean deleteFlow(FlowId flowId);
/**
+ * Clear the state for all previously added flows.
+ *
+ * @return true on success, otherwise false.
+ */
+ boolean clearAllFlows();
+
+ /**
* Clear the state for a previously added flow.
*
* @param flowId the Flow ID of the flow to clear.
@@ -101,4 +115,47 @@
* @return the added shortest-path flow on success, otherwise null.
*/
public FlowPath addAndMaintainShortestPathFlow(FlowPath flowPath);
+
+ /**
+ * Store a path flow for measurement purpose.
+ *
+ * NOTE: The Flow Path argument does NOT contain flow entries.
+ *
+ * @param flowPath the Flow Path with the endpoints and the match
+ * conditions to store.
+ * @return the stored shortest-path flow on success, otherwise null.
+ */
+ public FlowPath measurementStorePathFlow(FlowPath flowPath);
+
+ /**
+ * Install path flows for measurement purpose.
+ *
+ * @param numThreads the number of threads to use to install the path
+ * flows.
+ * @return true on success, otherwise false.
+ */
+ public boolean measurementInstallPaths(Integer numThreads);
+
+ /**
+ * Get the measurement time that took to install the path flows.
+ *
+ * @return the measurement time (in nanoseconds) it took to install
+ * the path flows.
+ */
+ public Long measurementGetInstallPathsTimeNsec();
+
+ /**
+ * Get the measurement install time per Flow.
+ *
+ * @return a multi-line string with the following format per line:
+ * ThreadAndTimePerFlow <ThreadId> <TotalThreads> <Time(ns)>
+ */
+ public String measurementGetPerFlowInstallTime();
+
+ /**
+ * Clear the path flows stored for measurement purpose.
+ *
+ * @return true on success, otherwise false.
+ */
+ public boolean measurementClearAllPaths();
}
diff --git a/src/main/java/net/floodlightcontroller/flowcache/web/ClearFlowResource.java b/src/main/java/net/floodlightcontroller/flowcache/web/ClearFlowResource.java
index 8fff358..7f3b589 100644
--- a/src/main/java/net/floodlightcontroller/flowcache/web/ClearFlowResource.java
+++ b/src/main/java/net/floodlightcontroller/flowcache/web/ClearFlowResource.java
@@ -27,11 +27,16 @@
// Extract the arguments
String flowIdStr = (String) getRequestAttributes().get("flow-id");
- FlowId flowId = new FlowId(flowIdStr);
- log.debug("Clear Flow Id: " + flowIdStr);
// Process the request
- result = flowService.clearFlow(flowId);
+ if (flowIdStr.equals("all")) {
+ log.debug("Clear All Flows");
+ result = flowService.clearAllFlows();
+ } else {
+ FlowId flowId = new FlowId(flowIdStr);
+ log.debug("Clear Flow Id: " + flowIdStr);
+ result = flowService.clearFlow(flowId);
+ }
return result;
}
}
diff --git a/src/main/java/net/floodlightcontroller/flowcache/web/DeleteFlowResource.java b/src/main/java/net/floodlightcontroller/flowcache/web/DeleteFlowResource.java
index f418c1e..ed6f0f7 100644
--- a/src/main/java/net/floodlightcontroller/flowcache/web/DeleteFlowResource.java
+++ b/src/main/java/net/floodlightcontroller/flowcache/web/DeleteFlowResource.java
@@ -27,11 +27,16 @@
// Extract the arguments
String flowIdStr = (String) getRequestAttributes().get("flow-id");
- FlowId flowId = new FlowId(flowIdStr);
- log.debug("Delete Flow Id: " + flowIdStr);
// Process the request
- result = flowService.deleteFlow(flowId);
+ if (flowIdStr.equals("all")) {
+ log.debug("Delete All Flows");
+ result = flowService.deleteAllFlows();
+ } else {
+ FlowId flowId = new FlowId(flowIdStr);
+ log.debug("Delete Flow Id: " + flowIdStr);
+ result = flowService.deleteFlow(flowId);
+ }
return result;
}
}
diff --git a/src/main/java/net/floodlightcontroller/flowcache/web/FlowWebRoutable.java b/src/main/java/net/floodlightcontroller/flowcache/web/FlowWebRoutable.java
index 962dbbb..fd9a319 100644
--- a/src/main/java/net/floodlightcontroller/flowcache/web/FlowWebRoutable.java
+++ b/src/main/java/net/floodlightcontroller/flowcache/web/FlowWebRoutable.java
@@ -22,6 +22,11 @@
router.attach("/getall-by-endpoints/{src-dpid}/{src-port}/{dst-dpid}/{dst-port}/json", GetAllFlowsByEndpointsResource.class);
router.attach("/getall/json", GetAllFlowsResource.class);
router.attach("/getsummary/{flow-id}/{max-flows}/json", GetSummaryFlowsResource.class);
+ router.attach("/measurement-store-path/json", MeasurementStorePathFlowResource.class);
+ router.attach("/measurement-install-paths/{num-threads}/json", MeasurementInstallPathsFlowResource.class);
+ router.attach("/measurement-get-install-paths-time-nsec/json", MeasurementGetInstallPathsTimeNsecFlowResource.class);
+ router.attach("/measurement-get-per-flow-install-time/json", MeasurementGetPerFlowInstallTimeFlowResource.class);
+ router.attach("/measurement-clear-all-paths/json", MeasurementClearAllPathsFlowResource.class);
return router;
}
diff --git a/src/main/java/net/floodlightcontroller/flowcache/web/MeasurementClearAllPathsFlowResource.java b/src/main/java/net/floodlightcontroller/flowcache/web/MeasurementClearAllPathsFlowResource.java
new file mode 100644
index 0000000..f69180d
--- /dev/null
+++ b/src/main/java/net/floodlightcontroller/flowcache/web/MeasurementClearAllPathsFlowResource.java
@@ -0,0 +1,35 @@
+package net.floodlightcontroller.flowcache.web;
+
+import net.floodlightcontroller.flowcache.IFlowService;
+import net.floodlightcontroller.util.FlowId;
+
+import org.openflow.util.HexString;
+import org.restlet.resource.Get;
+import org.restlet.resource.ServerResource;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class MeasurementClearAllPathsFlowResource extends ServerResource {
+ protected static Logger log = LoggerFactory.getLogger(MeasurementClearAllPathsFlowResource.class);
+
+ @Get("json")
+ public Boolean retrieve() {
+ Boolean result = false;
+
+ IFlowService flowService =
+ (IFlowService)getContext().getAttributes().
+ get(IFlowService.class.getCanonicalName());
+
+ if (flowService == null) {
+ log.debug("ONOS Flow Service not found");
+ return result;
+ }
+
+ // Extract the arguments
+ log.debug("Measurement Clear All Paths");
+
+ // Process the request
+ result = flowService.measurementClearAllPaths();
+ return result;
+ }
+}
diff --git a/src/main/java/net/floodlightcontroller/flowcache/web/MeasurementGetInstallPathsTimeNsecFlowResource.java b/src/main/java/net/floodlightcontroller/flowcache/web/MeasurementGetInstallPathsTimeNsecFlowResource.java
new file mode 100644
index 0000000..3dc1d08
--- /dev/null
+++ b/src/main/java/net/floodlightcontroller/flowcache/web/MeasurementGetInstallPathsTimeNsecFlowResource.java
@@ -0,0 +1,37 @@
+package net.floodlightcontroller.flowcache.web;
+
+import net.floodlightcontroller.flowcache.IFlowService;
+import net.floodlightcontroller.util.FlowId;
+
+import org.openflow.util.HexString;
+import org.restlet.resource.Get;
+import org.restlet.resource.ServerResource;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class MeasurementGetInstallPathsTimeNsecFlowResource extends ServerResource {
+ protected static Logger log = LoggerFactory.getLogger(MeasurementGetInstallPathsTimeNsecFlowResource.class);
+
+ @Get("json")
+ public Long retrieve() {
+ Long result = null;
+
+ IFlowService flowService =
+ (IFlowService)getContext().getAttributes().
+ get(IFlowService.class.getCanonicalName());
+
+ if (flowService == null) {
+ log.debug("ONOS Flow Service not found");
+ return result;
+ }
+
+ // Extract the arguments
+
+ // Process the request
+ result = flowService.measurementGetInstallPathsTimeNsec();
+
+ log.debug("Measurement Get Install Paths Time (nsec): " + result);
+
+ return result;
+ }
+}
diff --git a/src/main/java/net/floodlightcontroller/flowcache/web/MeasurementGetPerFlowInstallTimeFlowResource.java b/src/main/java/net/floodlightcontroller/flowcache/web/MeasurementGetPerFlowInstallTimeFlowResource.java
new file mode 100644
index 0000000..adaecc8
--- /dev/null
+++ b/src/main/java/net/floodlightcontroller/flowcache/web/MeasurementGetPerFlowInstallTimeFlowResource.java
@@ -0,0 +1,37 @@
+package net.floodlightcontroller.flowcache.web;
+
+import net.floodlightcontroller.flowcache.IFlowService;
+import net.floodlightcontroller.util.FlowId;
+
+import org.openflow.util.HexString;
+import org.restlet.resource.Get;
+import org.restlet.resource.ServerResource;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class MeasurementGetPerFlowInstallTimeFlowResource extends ServerResource {
+ protected static Logger log = LoggerFactory.getLogger(MeasurementGetPerFlowInstallTimeFlowResource.class);
+
+ @Get("json")
+ public String retrieve() {
+ String result = null;
+
+ IFlowService flowService =
+ (IFlowService)getContext().getAttributes().
+ get(IFlowService.class.getCanonicalName());
+
+ if (flowService == null) {
+ log.debug("ONOS Flow Service not found");
+ return result;
+ }
+
+ // Extract the arguments
+
+ // Process the request
+ result = flowService.measurementGetPerFlowInstallTime();
+
+ log.debug("Measurement Get Install Paths Time (nsec): " + result);
+
+ return result;
+ }
+}
diff --git a/src/main/java/net/floodlightcontroller/flowcache/web/MeasurementInstallPathsFlowResource.java b/src/main/java/net/floodlightcontroller/flowcache/web/MeasurementInstallPathsFlowResource.java
new file mode 100644
index 0000000..1bba4b1
--- /dev/null
+++ b/src/main/java/net/floodlightcontroller/flowcache/web/MeasurementInstallPathsFlowResource.java
@@ -0,0 +1,37 @@
+package net.floodlightcontroller.flowcache.web;
+
+import net.floodlightcontroller.flowcache.IFlowService;
+import net.floodlightcontroller.util.FlowId;
+
+import org.openflow.util.HexString;
+import org.restlet.resource.Get;
+import org.restlet.resource.ServerResource;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class MeasurementInstallPathsFlowResource extends ServerResource {
+ protected static Logger log = LoggerFactory.getLogger(MeasurementInstallPathsFlowResource.class);
+
+ @Get("json")
+ public Boolean retrieve() {
+ Boolean result = false;
+
+ IFlowService flowService =
+ (IFlowService)getContext().getAttributes().
+ get(IFlowService.class.getCanonicalName());
+
+ if (flowService == null) {
+ log.debug("ONOS Flow Service not found");
+ return result;
+ }
+
+ // Extract the arguments
+ String numThreadsStr = (String) getRequestAttributes().get("num-threads");
+ Integer numThreads = new Integer(numThreadsStr);
+ log.debug("Measurement Install Paths Number of Threads " + numThreadsStr);
+
+ // Process the request
+ result = flowService.measurementInstallPaths(numThreads);
+ return result;
+ }
+}
diff --git a/src/main/java/net/floodlightcontroller/flowcache/web/MeasurementStorePathFlowResource.java b/src/main/java/net/floodlightcontroller/flowcache/web/MeasurementStorePathFlowResource.java
new file mode 100644
index 0000000..e68ceb7
--- /dev/null
+++ b/src/main/java/net/floodlightcontroller/flowcache/web/MeasurementStorePathFlowResource.java
@@ -0,0 +1,65 @@
+package net.floodlightcontroller.flowcache.web;
+
+import java.io.IOException;
+
+import net.floodlightcontroller.flowcache.IFlowService;
+import net.floodlightcontroller.util.FlowId;
+import net.floodlightcontroller.util.FlowPath;
+
+import org.codehaus.jackson.JsonGenerationException;
+import org.codehaus.jackson.map.ObjectMapper;
+import org.codehaus.jackson.map.JsonMappingException;
+import org.codehaus.jackson.map.ObjectMapper;
+import org.restlet.resource.Post;
+import org.restlet.resource.ServerResource;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class MeasurementStorePathFlowResource extends ServerResource {
+
+ protected static Logger log = LoggerFactory.getLogger(MeasurementStorePathFlowResource.class);
+
+ @Post("json")
+ public FlowId store(String flowJson) {
+ FlowId result = new FlowId();
+
+ IFlowService flowService =
+ (IFlowService)getContext().getAttributes().
+ get(IFlowService.class.getCanonicalName());
+
+ if (flowService == null) {
+ log.debug("ONOS Flow Service not found");
+ return result;
+ }
+
+ //
+ // Extract the arguments
+ // NOTE: The "flow" is specified in JSON format.
+ //
+ ObjectMapper mapper = new ObjectMapper();
+ String flowPathStr = flowJson;
+ FlowPath flowPath = null;
+ log.debug("Measurement Store Flow Path: " + flowPathStr);
+ try {
+ flowPath = mapper.readValue(flowPathStr, FlowPath.class);
+ } catch (JsonGenerationException e) {
+ e.printStackTrace();
+ } catch (JsonMappingException e) {
+ e.printStackTrace();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+
+ // Process the request
+ if (flowPath != null) {
+ FlowPath addedFlowPath =
+ flowService.measurementStorePathFlow(flowPath);
+ if (addedFlowPath == null)
+ result = new FlowId(); // Error: Return empty Flow Id
+ else
+ result = addedFlowPath.flowId();
+ }
+
+ return result;
+ }
+}
diff --git a/src/main/java/net/floodlightcontroller/linkdiscovery/internal/LinkDiscoveryManager.java b/src/main/java/net/floodlightcontroller/linkdiscovery/internal/LinkDiscoveryManager.java
index 634f7eb..72b2988 100644
--- a/src/main/java/net/floodlightcontroller/linkdiscovery/internal/LinkDiscoveryManager.java
+++ b/src/main/java/net/floodlightcontroller/linkdiscovery/internal/LinkDiscoveryManager.java
@@ -2175,4 +2175,16 @@
public void setAutoPortFastFeature(boolean autoPortFastFeature) {
this.autoPortFastFeature = autoPortFastFeature;
}
+
+ @Override
+ public void switchPortAdded(Long switchId, OFPhysicalPort port) {
+ // TODO Auto-generated method stub
+
+ }
+
+ @Override
+ public void switchPortRemoved(Long switchId, OFPhysicalPort port) {
+ // TODO Auto-generated method stub
+
+ }
}
diff --git a/src/main/java/net/floodlightcontroller/onoslistener/OnosPublisher.java b/src/main/java/net/floodlightcontroller/onoslistener/OnosPublisher.java
index 723fe1c..d35a4f8 100644
--- a/src/main/java/net/floodlightcontroller/onoslistener/OnosPublisher.java
+++ b/src/main/java/net/floodlightcontroller/onoslistener/OnosPublisher.java
@@ -6,12 +6,14 @@
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
+import org.openflow.protocol.OFPhysicalPort;
import org.openflow.util.HexString;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import net.floodlightcontroller.core.IFloodlightProviderService;
import net.floodlightcontroller.core.INetMapStorage.DM_OPERATION;
+import net.floodlightcontroller.core.INetMapTopologyObjects.IPortObject;
import net.floodlightcontroller.core.INetMapTopologyObjects.ISwitchObject;
import net.floodlightcontroller.core.ISwitchStorage.SwitchState;
import net.floodlightcontroller.core.IOFSwitch;
@@ -50,6 +52,7 @@
protected static final String DBConfigFile = "dbconf";
protected static final String CleanupEnabled = "EnableCleanup";
protected IThreadPoolService threadPool;
+ protected IFloodlightProviderService floodlightProvider;
protected final int CLEANUP_TASK_INTERVAL = 60; // 1 min
protected SingletonTask cleanupTask;
@@ -116,12 +119,29 @@
@Override
public void linkDiscoveryUpdate(LDUpdate update) {
// TODO Auto-generated method stub
+ switch (update.getOperation()) {
+
+ case LINK_REMOVED:
+ // TODO: Move network map link removal here
+ // reconcile paths here
+// IPortObject srcPort = conn.utils().searchPort(conn, HexString.toHexString(update.getSrc()), update.getSrcPort());
+ break;
+
+ default:
+ break;
+ }
}
@Override
public void addedSwitch(IOFSwitch sw) {
- // TODO Auto-generated method stub
+
+ if (registryService.hasControl(sw.getId())) {
+ swStore.update(sw.getStringId(), SwitchState.ACTIVE, DM_OPERATION.UPDATE);
+ for (OFPhysicalPort port: sw.getPorts()) {
+ swStore.addPort(sw.getStringId(), port);
+ }
+ }
}
@@ -137,6 +157,19 @@
}
+
+ @Override
+ public void switchPortAdded(Long switchId, OFPhysicalPort port) {
+ // TODO Auto-generated method stub
+ swStore.addPort(HexString.toHexString(switchId), port);
+ }
+
+ @Override
+ public void switchPortRemoved(Long switchId, OFPhysicalPort port) {
+ // TODO Auto-generated method stub
+ swStore.deletePort(HexString.toHexString(switchId), port.getPortNumber());
+ }
+
@Override
public String getName() {
return "OnosPublisher";
@@ -206,6 +239,8 @@
conn = GraphDBConnection.getInstance(conf);
log = LoggerFactory.getLogger(OnosPublisher.class);
+ floodlightProvider =
+ context.getServiceImpl(IFloodlightProviderService.class);
deviceService = context.getServiceImpl(IDeviceService.class);
threadPool = context.getServiceImpl(IThreadPoolService.class);
registryService = context.getServiceImpl(IControllerRegistryService.class);
@@ -227,6 +262,7 @@
String cleanupNeeded = configMap.get(CleanupEnabled);
deviceService.addListener(this);
+ floodlightProvider.addOFSwitchListener(this);
log.debug("Adding EventListener");
conn.addEventListener(new LocalTopologyEventListener(conn));
diff --git a/src/main/java/net/floodlightcontroller/restclient/RestClient.java b/src/main/java/net/floodlightcontroller/restclient/RestClient.java
deleted file mode 100644
index 541b42d..0000000
--- a/src/main/java/net/floodlightcontroller/restclient/RestClient.java
+++ /dev/null
@@ -1,142 +0,0 @@
-package net.floodlightcontroller.restclient;
-
-import java.io.BufferedReader;
-import java.io.IOException;
-import java.io.InputStreamReader;
-import java.net.HttpURLConnection;
-import java.net.MalformedURLException;
-import java.net.URL;
-
-import net.sf.json.JSONArray;
-import net.sf.json.JSONObject;
-import net.sf.json.JSONSerializer;
-
-
-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());
- }
-
- if (conn.getContentType().equals("application/json"))
- { }else{
- System.out.print("The content received is not json format!");
- }
-
- BufferedReader br = new BufferedReader(new InputStreamReader((conn.getInputStream())));
- StringBuffer res = new StringBuffer();
- String line;
- while ((line = br.readLine()) != null) {
- res.append(line);
- }
-
- String res2=res.toString().replaceAll("\"", "'");
- JSONObject jsonObj = (JSONObject) JSONSerializer.toJSON(res2);
- JSONArray rib_json_array = jsonObj.getJSONArray("rib");
- String router_id = jsonObj.getString("router-id");
-
- int size = rib_json_array.size();
- System.out.print("size:"+size+"\n");
- for (int j = 0; j < size; j++) {
- JSONObject second_json_object = rib_json_array.getJSONObject(j);
- String prefix = second_json_object.getString("prefix");
- String nexthop = second_json_object.getString("nexthop");
-
- //insert each rib entry into the local rib;
- RestClient.post("http://127.0.0.1:8090/wm/bgp/"+router_id+"/"+prefix+"/"+nexthop);
-
-
-
- }
- br.close();
- conn.disconnect();
-
- } catch (MalformedURLException e) {
-
- e.printStackTrace();
-
- } catch (IOException e) {
-
- e.printStackTrace();
-
- }
- }
-
-public static void post (String str) {
-
- if (str == null)
- return;
-
- try {
-
- URL url = new URL(str);
- HttpURLConnection conn = (HttpURLConnection) url.openConnection();
- conn.setDoOutput(true);
- conn.setRequestMethod("POST");
- conn.setRequestProperty("Content-Type", "application/json");
-
- if (conn.getResponseCode() != 200) {
- throw new RuntimeException("Failed : HTTP error code : "
- + conn.getResponseCode());
- }
-
- conn.disconnect();
-
- } catch (MalformedURLException e) {
-
- e.printStackTrace();
-
- } catch (IOException e) {
-
- e.printStackTrace();
-
- }
- }
-
-
-public static void delete (String str) {
-
- if (str == null)
- return;
-
- try {
-
- URL url = new URL(str);
- HttpURLConnection conn = (HttpURLConnection) url.openConnection();
- conn.setRequestMethod("DELETE");
- conn.setRequestProperty("Accept", "application/json");
-
-
- if (conn.getResponseCode() != 200) {
- throw new RuntimeException("Failed : HTTP error code : "
- + conn.getResponseCode());
- }
-
- 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 95a3b19..1e002aa 100644
--- a/src/main/java/net/floodlightcontroller/routing/TopoRouteService.java
+++ b/src/main/java/net/floodlightcontroller/routing/TopoRouteService.java
@@ -101,14 +101,6 @@
GraphDBConnection conn;
- //
- // Topology state for storing (on demand) Switch and Ports info for
- // fast access during the shortest path computation.
- // It is explicitly populated by method @ref prepareShortestPathTopo().
- // See the documentation for that method for details.
- //
- HashMap<Long, Node> shortestPathTopo;
-
@Override
public Collection<Class<? extends IFloodlightService>> getModuleServices() {
Collection<Class<? extends IFloodlightService>> l =
@@ -168,7 +160,7 @@
/**
* Fetch the Switch and Ports info from the Titan Graph
- * and store it locally for fast access during the shortest path
+ * and return it for fast access during the shortest path
* computation.
*
* After fetching the state, method @ref getTopoShortestPath()
@@ -184,16 +176,18 @@
* method @ref dropShortestPathTopo() should be used to release
* the internal state that is not needed anymore:
*
- * prepareShortestPathTopo();
+ * Map<Long, ?> shortestPathTopo;
+ * shortestPathTopo = prepareShortestPathTopo();
* for (int i = 0; i < 10000; i++) {
- * dataPath = getTopoShortestPath(...);
+ * dataPath = getTopoShortestPath(shortestPathTopo, ...);
* ...
* }
- * dropShortestPathTopo();
+ * dropShortestPathTopo(shortestPathTopo);
+ *
+ * @return the Shortest Path info handler stored in a map.
*/
-
- public void prepareShortestPathTopo() {
- shortestPathTopo = new HashMap<Long, Node>();
+ public Map<Long, ?> prepareShortestPathTopo() {
+ Map<Long, Node> shortestPathTopo = new HashMap<Long, Node>();
//
// Fetch the relevant info from the Switch and Port vertices
@@ -260,6 +254,8 @@
}
}
conn.endTx(Transaction.COMMIT);
+
+ return shortestPathTopo;
}
/**
@@ -268,9 +264,10 @@
*
* See the documentation for method @ref prepareShortestPathTopo()
* for additional information and usage.
+ *
+ * @shortestPathTopo the Shortest Path info handler to release.
*/
-
- public void dropShortestPathTopo() {
+ public void dropShortestPathTopo(Map<Long, ?> shortestPathTopo) {
shortestPathTopo = null;
}
@@ -282,13 +279,17 @@
* See the documentation for method @ref prepareShortestPathTopo()
* for additional information and usage.
*
+ * @param shortestPathTopoHandler the Shortest Path info handler
+ * to use.
* @param src the source in the shortest path computation.
* @param dest the destination in the shortest path computation.
* @return the data path with the computed shortest path if
* found, otherwise null.
*/
-
- public DataPath getTopoShortestPath(SwitchPort src, SwitchPort dest) {
+ public DataPath getTopoShortestPath(Map<Long, ?> shortestPathTopoHandler,
+ SwitchPort src, SwitchPort dest) {
+ @SuppressWarnings("unchecked")
+ Map<Long, Node> shortestPathTopo = (Map)shortestPathTopoHandler;
DataPath result_data_path = new DataPath();
// Initialize the source and destination in the data path to return
diff --git a/src/main/java/net/floodlightcontroller/staticflowentry/StaticFlowEntryPusher.java b/src/main/java/net/floodlightcontroller/staticflowentry/StaticFlowEntryPusher.java
index 4ed59d7..371a479 100644
--- a/src/main/java/net/floodlightcontroller/staticflowentry/StaticFlowEntryPusher.java
+++ b/src/main/java/net/floodlightcontroller/staticflowentry/StaticFlowEntryPusher.java
@@ -39,6 +39,7 @@
import org.openflow.protocol.OFFlowRemoved;
import org.openflow.protocol.OFMatch;
import org.openflow.protocol.OFMessage;
+import org.openflow.protocol.OFPhysicalPort;
import org.openflow.protocol.OFType;
import org.openflow.protocol.factory.BasicFactory;
import org.openflow.util.HexString;
@@ -675,5 +676,17 @@
Map<String, String> removedControllerNodeIPs) {
// ignore
}
+
+ @Override
+ public void switchPortAdded(Long switchId, OFPhysicalPort port) {
+ // TODO Auto-generated method stub
+
+ }
+
+ @Override
+ public void switchPortRemoved(Long switchId, OFPhysicalPort port) {
+ // TODO Auto-generated method stub
+
+ }
}
diff --git a/src/main/java/net/onrc/onos/registry/controller/IControllerRegistryService.java b/src/main/java/net/onrc/onos/registry/controller/IControllerRegistryService.java
index dadee65..21da47e 100644
--- a/src/main/java/net/onrc/onos/registry/controller/IControllerRegistryService.java
+++ b/src/main/java/net/onrc/onos/registry/controller/IControllerRegistryService.java
@@ -115,4 +115,7 @@
* @return Collection of dpids
*/
public Collection<Long> getSwitchesControlledByController(String controllerId);
+
+ public IdBlock allocateUniqueIdBlock();
+
}
diff --git a/src/main/java/net/onrc/onos/registry/controller/IdBlock.java b/src/main/java/net/onrc/onos/registry/controller/IdBlock.java
new file mode 100644
index 0000000..64b2af8
--- /dev/null
+++ b/src/main/java/net/onrc/onos/registry/controller/IdBlock.java
@@ -0,0 +1,31 @@
+package net.onrc.onos.registry.controller;
+
+public class IdBlock {
+ private long start;
+ private long end;
+ private long size;
+
+ public IdBlock(long start, long end, long size) {
+ this.start = start;
+ this.end = end;
+ this.size = size;
+ }
+
+ public long getStart() {
+ return start;
+ }
+
+ public long getEnd() {
+ return end;
+ }
+
+ public long getSize() {
+ return size;
+ }
+
+ @Override
+ public String toString() {
+ return "IdBlock [start=" + start + ", end=" + end + ", size=" + size
+ + "]";
+ }
+}
diff --git a/src/main/java/net/onrc/onos/registry/controller/StandaloneRegistry.java b/src/main/java/net/onrc/onos/registry/controller/StandaloneRegistry.java
index 2c220fd..e48c519 100644
--- a/src/main/java/net/onrc/onos/registry/controller/StandaloneRegistry.java
+++ b/src/main/java/net/onrc/onos/registry/controller/StandaloneRegistry.java
@@ -115,6 +115,12 @@
String controllerId) {
throw new RuntimeException("Not yet implemented");
}
+
+ @Override
+ public IdBlock allocateUniqueIdBlock(){
+ //XXX Not exactly unique...
+ return new IdBlock(0L, 0x10000000L, 0x10000000L);
+ }
@Override
public Collection<Class<? extends IFloodlightService>> getModuleServices() {
diff --git a/src/main/java/net/onrc/onos/registry/controller/ZookeeperRegistry.java b/src/main/java/net/onrc/onos/registry/controller/ZookeeperRegistry.java
index 76b7ebd..82259a9 100644
--- a/src/main/java/net/onrc/onos/registry/controller/ZookeeperRegistry.java
+++ b/src/main/java/net/onrc/onos/registry/controller/ZookeeperRegistry.java
@@ -24,6 +24,8 @@
import com.netflix.curator.RetryPolicy;
import com.netflix.curator.framework.CuratorFramework;
import com.netflix.curator.framework.CuratorFrameworkFactory;
+import com.netflix.curator.framework.recipes.atomic.AtomicValue;
+import com.netflix.curator.framework.recipes.atomic.DistributedAtomicLong;
import com.netflix.curator.framework.recipes.cache.ChildData;
import com.netflix.curator.framework.recipes.cache.PathChildrenCache;
import com.netflix.curator.framework.recipes.cache.PathChildrenCache.StartMode;
@@ -33,6 +35,7 @@
import com.netflix.curator.framework.recipes.leader.LeaderLatchEvent;
import com.netflix.curator.framework.recipes.leader.LeaderLatchListener;
import com.netflix.curator.retry.ExponentialBackoffRetry;
+import com.netflix.curator.retry.RetryOneTime;
import com.netflix.curator.x.discovery.ServiceCache;
import com.netflix.curator.x.discovery.ServiceDiscovery;
import com.netflix.curator.x.discovery.ServiceDiscoveryBuilder;
@@ -67,6 +70,10 @@
protected ConcurrentHashMap<String, SwitchLeadershipData> switches;
protected Map<String, PathChildrenCache> switchPathCaches;
+ private final String ID_COUNTER_PATH = "/flowidcounter";
+ private final Long ID_BLOCK_SIZE = 0x100000000L;
+ protected DistributedAtomicLong distributedIdCounter;
+
//Zookeeper performance-related configuration
protected static final int sessionTimeout = 5000;
protected static final int connectionTimeout = 7000;
@@ -372,6 +379,21 @@
return data;
}
+ public IdBlock allocateUniqueIdBlock(){
+ try {
+ AtomicValue<Long> result = null;
+ do {
+ result = distributedIdCounter.add(ID_BLOCK_SIZE);
+ } while (result == null || !result.succeeded());
+
+ return new IdBlock(result.preValue(), result.postValue() - 1, ID_BLOCK_SIZE);
+ } catch (Exception e) {
+ log.error("Error allocating ID block");
+ }
+
+ return null;
+ }
+
/*
* IFloodlightModule
*/
@@ -427,6 +449,10 @@
client.start();
client = client.usingNamespace(namespace);
+ distributedIdCounter = new DistributedAtomicLong(
+ client,
+ ID_COUNTER_PATH,
+ new RetryOneTime(100));
switchCache = new PathChildrenCache(client, switchLatchesPath, true);
switchCache.getListenable().addListener(switchPathCacheListener);
diff --git a/src/main/resources/META-INF/services/net.floodlightcontroller.core.module.IFloodlightModule b/src/main/resources/META-INF/services/net.floodlightcontroller.core.module.IFloodlightModule
index 99ca4c8..b4b4f27 100644
--- a/src/main/resources/META-INF/services/net.floodlightcontroller.core.module.IFloodlightModule
+++ b/src/main/resources/META-INF/services/net.floodlightcontroller.core.module.IFloodlightModule
@@ -26,6 +26,6 @@
net.floodlightcontroller.onoslistener.OnosPublisher
net.floodlightcontroller.flowcache.FlowManager
net.floodlightcontroller.routing.TopoRouteService
+net.floodlightcontroller.bgproute.BgpRoute
net.onrc.onos.registry.controller.ZookeeperRegistry
net.onrc.onos.registry.controller.StandaloneRegistry
-
diff --git a/start-onos.sh b/start-onos.sh
index 77accd0..0a59701 100755
--- a/start-onos.sh
+++ b/start-onos.sh
@@ -82,8 +82,23 @@
echo "Starting ONOS controller ..."
echo
#java ${JVM_OPTS} -Dlogback.configurationFile=${FL_LOGBACK} -jar ${FL_JAR} -cf ${FL_HOME}/onos.properties > /dev/null 2>&1 &
- java ${JVM_OPTS} -Dlogback.configurationFile=${FL_LOGBACK} -cp ${CLASSPATH} ${MAIN_CLASS} -cf ${FL_HOME}/onos.properties > /dev/null 2>&1 &
+ #java ${JVM_OPTS} -Dlogback.configurationFile=${FL_LOGBACK} -cp ${CLASSPATH} ${MAIN_CLASS} -cf ${FL_HOME}/onos.properties > /dev/n
+ mvn exec:exec -Dexec.executable="java" -Dexec.args="${JVM_OPTS} -Dlogback.configurationFile=${FL_LOGBACK} -cp %classpath ${MAIN_CLASS} -cf ${FL_HOME}/onos.properties" > ${LOGDIR}/onos.stdout 2>${LOGDIR}/onos.stderr &
+
+ echo "Waiting for ONOS to start..."
+ COUNT=0
+ ESTATE=0
+ while [ "$COUNT" != "10" ]; do
+ COUNT=$((COUNT + 1))
+ n=`jps -l |grep "${MAIN_CLASS}" | wc -l`
+ if [ "$n" -ge "1" ]; then
+ exit 0
+ fi
+ sleep $COUNT
+ done
+ echo "Timed out"
+ exit 1
# echo "java ${JVM_OPTS} -Dlogback.configurationFile=${FL_LOGBACK} -jar ${FL_JAR} -cf ./onos.properties > /dev/null 2>&1 &"
# sudo -b /usr/sbin/tcpdump -n -i eth0 -s0 -w ${PCAP_LOG} 'tcp port 6633' > /dev/null 2>&1
@@ -128,7 +143,7 @@
start
;;
startifdown)
- n=`jps -l |grep "net.floodlightcontroller.core.Main" | wc -l`
+ n=`jps -l |grep "${MAIN_CLASS}" | wc -l`
if [ $n == 0 ]; then
start
else
@@ -142,7 +157,7 @@
deldb
;;
status)
- n=`jps -l |grep "net.floodlightcontroller.core.Main" | wc -l`
+ n=`jps -l |grep "${MAIN_CLASS}" | wc -l`
echo "$n instance of onos running"
;;
*)
diff --git a/titan/gremlin.sh b/titan/gremlin.sh
index c7155ac..aa9c513 100755
--- a/titan/gremlin.sh
+++ b/titan/gremlin.sh
@@ -1,6 +1,8 @@
#!/bin/bash
-CP=$( echo `dirname $0`/../lib/*.jar `dirname $0`/../lib/titan/*.jar . | sed 's/ /:/g')
+ONOS_DIR="`dirname $0`/.."
+#CP=$( echo `dirname $0`/../lib/*.jar `dirname $0`/../lib/titan/*.jar . | sed 's/ /:/g')
+CP=`mvn -f ${ONOS_DIR}/pom.xml dependency:build-classpath -Dmdep.outputFile=/dev/stdout -l /dev/stderr`
# Find Java
if [ "$JAVA_HOME" = "" ] ; then
diff --git a/titan/listNotUpdated b/titan/listNotUpdated
new file mode 100644
index 0000000..a44c458
--- /dev/null
+++ b/titan/listNotUpdated
@@ -0,0 +1,4 @@
+g.stopTransaction(SUCCESS)
+g.V('type', 'flow_entry').each{
+if (it.switch_state.equals("FE_SWITCH_NOT_UPDATED")) println it.map.next()
+}
\ No newline at end of file
diff --git a/web/clear_flow.py b/web/clear_flow.py
index 50678e2..db70d40 100755
--- a/web/clear_flow.py
+++ b/web/clear_flow.py
@@ -51,6 +51,7 @@
usage_msg = usage_msg + " Arguments:\n"
usage_msg = usage_msg + " <begin-flow-id> <end-flow-id> Clear all flows in the flow ID range\n"
usage_msg = usage_msg + " <flow-id> Clear a single flow with the flow ID\n"
+ usage_msg = usage_msg + " all Clear all flows\n"
# app.debug = False;
@@ -63,14 +64,18 @@
if len(sys.argv) < 2:
log_error(usage_msg)
exit(1)
- begin_flow_id = int(sys.argv[1], 0)
- if len(sys.argv) >= 3:
- end_flow_id = int(sys.argv[2], 0)
- else:
- end_flow_id = begin_flow_id
- # Do the work
- flow_id = begin_flow_id
- while flow_id <= end_flow_id:
- clear_flow_path(flow_id)
- flow_id = flow_id + 1
+ if (sys.argv[1] == "all"):
+ clear_flow_path(sys.argv[1])
+ else:
+ begin_flow_id = int(sys.argv[1], 0)
+ if len(sys.argv) >= 3:
+ end_flow_id = int(sys.argv[2], 0)
+ else:
+ end_flow_id = begin_flow_id
+
+ # Do the work
+ flow_id = begin_flow_id
+ while flow_id <= end_flow_id:
+ clear_flow_path(flow_id)
+ flow_id = flow_id + 1
diff --git a/web/delete_flow.py b/web/delete_flow.py
index ff4caff..fff9319 100755
--- a/web/delete_flow.py
+++ b/web/delete_flow.py
@@ -51,6 +51,7 @@
usage_msg = usage_msg + " Arguments:\n"
usage_msg = usage_msg + " <begin-flow-id> <end-flow-id> Delete all flows in the flow ID range\n"
usage_msg = usage_msg + " <flow-id> Delete a single flow with the flow ID\n"
+ usage_msg = usage_msg + " all Delete all flows\n"
# app.debug = False;
@@ -63,14 +64,18 @@
if len(sys.argv) < 2:
log_error(usage_msg)
exit(1)
- begin_flow_id = int(sys.argv[1], 0)
- if len(sys.argv) >= 3:
- end_flow_id = int(sys.argv[2], 0)
- else:
- end_flow_id = begin_flow_id
- # Do the work
- flow_id = begin_flow_id
- while flow_id <= end_flow_id:
- delete_flow_path(flow_id)
- flow_id = flow_id + 1
+ if (sys.argv[1] == "all"):
+ delete_flow_path(sys.argv[1])
+ else:
+ begin_flow_id = int(sys.argv[1], 0)
+ if len(sys.argv) >= 3:
+ end_flow_id = int(sys.argv[2], 0)
+ else:
+ end_flow_id = begin_flow_id
+
+ # Do the work
+ flow_id = begin_flow_id
+ while flow_id <= end_flow_id:
+ delete_flow_path(flow_id)
+ flow_id = flow_id + 1
diff --git a/web/measurement_clear_all_paths.py b/web/measurement_clear_all_paths.py
new file mode 100755
index 0000000..5bb73c5
--- /dev/null
+++ b/web/measurement_clear_all_paths.py
@@ -0,0 +1,61 @@
+#! /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/measurement-clear-all-paths/json")
+def measurement_clear_all_paths():
+ command = "curl -s \"http://%s:%s/wm/flow/measurement-clear-all-paths/json\"" % (ControllerIP, ControllerPort)
+ debug("measurement_clear_all_paths %s" % command)
+ result = os.popen(command).read()
+ debug("result %s" % result)
+ # parsedResult = json.loads(result)
+ # debug("parsed %s" % parsedResult)
+
+if __name__ == "__main__":
+ usage_msg = "Clear the paths that have been stored for measurement purpose\n"
+ usage_msg = usage_msg + "Usage: %s\n" % (sys.argv[0])
+ usage_msg = usage_msg + "\n"
+
+ # 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
+
+ # Do the work
+ measurement_clear_all_paths()
diff --git a/web/measurement_get_install_paths_time_nsec.py b/web/measurement_get_install_paths_time_nsec.py
new file mode 100755
index 0000000..d64dc49
--- /dev/null
+++ b/web/measurement_get_install_paths_time_nsec.py
@@ -0,0 +1,61 @@
+#! /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/measurement-get-install-paths-time-nsec/json")
+def measurement_get_install_paths_time_nsec():
+ command = "curl -s \"http://%s:%s/wm/flow/measurement-get-install-paths-time-nsec/json\"" % (ControllerIP, ControllerPort)
+ debug("measurement_get_install_paths_time_nsec %s" % command)
+ result = os.popen(command).read()
+ print '%s nsec' % (result)
+ # parsedResult = json.loads(result)
+ # debug("parsed %s" % parsedResult)
+
+if __name__ == "__main__":
+ usage_msg = "Get the measured time to install the stored flow paths\n"
+ usage_msg = usage_msg + "Usage: %s\n" % (sys.argv[0])
+ usage_msg = usage_msg + "\n"
+
+ # 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
+
+ # Do the work
+ measurement_get_install_paths_time_nsec()
diff --git a/web/measurement_get_per_flow_install_time.py b/web/measurement_get_per_flow_install_time.py
new file mode 100755
index 0000000..bf2bcc7
--- /dev/null
+++ b/web/measurement_get_per_flow_install_time.py
@@ -0,0 +1,61 @@
+#! /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/measurement-get-per-flow-install-time/json")
+def measurement_get_per_flow_install_time():
+ command = "curl -s \"http://%s:%s/wm/flow/measurement-get-per-flow-install-time/json\"" % (ControllerIP, ControllerPort)
+ debug("measurement_get_per_flow_install_time %s" % command)
+ result = os.popen(command).read()
+ print '%s' % (result)
+ # parsedResult = json.loads(result)
+ # debug("parsed %s" % parsedResult)
+
+if __name__ == "__main__":
+ usage_msg = "Get the measured time per flow to install each stored flow path\n"
+ usage_msg = usage_msg + "Usage: %s\n" % (sys.argv[0])
+ usage_msg = usage_msg + "\n"
+
+ # 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
+
+ # Do the work
+ measurement_get_per_flow_install_time()
diff --git a/web/measurement_install_paths.py b/web/measurement_install_paths.py
new file mode 100755
index 0000000..d99070e
--- /dev/null
+++ b/web/measurement_install_paths.py
@@ -0,0 +1,67 @@
+#! /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/measurement-install-paths/<num-threads>/json")
+def measurement_install_paths(num_threads):
+ command = "curl -s \"http://%s:%s/wm/flow/measurement-install-paths/%s/json\"" % (ControllerIP, ControllerPort, num_threads)
+ debug("measurement_install_paths %s" % command)
+ result = os.popen(command).read()
+ debug("result %s" % result)
+ # parsedResult = json.loads(result)
+ # debug("parsed %s" % parsedResult)
+
+if __name__ == "__main__":
+ usage_msg = "Install flow paths and start measurements\n"
+ usage_msg = usage_msg + "Usage: %s <num-threads>\n" % (sys.argv[0])
+ usage_msg = usage_msg + "\n"
+ usage_msg = usage_msg + " Arguments:\n"
+ usage_msg = usage_msg + " <num-threads> Number of threads to use to install the flows\n"
+
+ # 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)
+ num_threads = int(sys.argv[1], 0)
+
+ # Do the work
+ measurement_install_paths(num_threads)
diff --git a/web/measurement_process.py b/web/measurement_process.py
new file mode 100755
index 0000000..3187299
--- /dev/null
+++ b/web/measurement_process.py
@@ -0,0 +1,59 @@
+#! /usr/bin/env python
+# -*- Mode: python; py-indent-offset: 4; tab-width: 8; indent-tabs-mode: t; -*-
+
+import functools
+import math
+import sys
+
+## {{{ http://code.activestate.com/recipes/511478/ (r1)
+
+def percentile(N, percent, key=lambda x:x):
+ """
+ Find the percentile of a list of values.
+
+ @parameter N - is a list of values. Note N MUST BE already sorted.
+ @parameter percent - a float value from 0.0 to 1.0.
+ @parameter key - optional key function to compute value from each element of N.
+
+ @return - the percentile of the values
+ """
+ if not N:
+ return None
+ k = (len(N)-1) * percent
+ f = math.floor(k)
+ c = math.ceil(k)
+ if f == c:
+ return key(N[int(k)])
+ d0 = key(N[int(f)]) * (c-k)
+ d1 = key(N[int(c)]) * (k-f)
+ return d0+d1
+
+# median is 50th percentile.
+# median = functools.partial(percentile, percent=0.5)
+## end of http://code.activestate.com/recipes/511478/ }}}
+
+if __name__ == "__main__":
+
+ dict = {}
+
+ #
+ # Read the data from the stdin, and store it in a dictionary.
+ # The dictionary uses lists as values.
+ #
+ data = sys.stdin.readlines()
+ for line in data:
+ words = line.split()
+ thread_n = int(words[0])
+ msec = float(words[1])
+ dict.setdefault(thread_n, []).append(msec)
+
+ #
+ # Compute and print the values: median (50-th), 10-th, and 90-th
+ # percentile:
+ # <key> <median> <10-percentile> <90-percentile>
+ #
+ for key, val_list in sorted(dict.items()):
+ val_10 = percentile(sorted(val_list), 0.1)
+ val_50 = percentile(sorted(val_list), 0.5)
+ val_90 = percentile(sorted(val_list), 0.9)
+ print "%s %s %s %s" % (str(key), str(val_50), str(val_10), str(val_90))
diff --git a/web/measurement_run.py b/web/measurement_run.py
new file mode 100755
index 0000000..80d0517
--- /dev/null
+++ b/web/measurement_run.py
@@ -0,0 +1,104 @@
+#! /usr/bin/env python
+# -*- Mode: python; py-indent-offset: 4; tab-width: 8; indent-tabs-mode: t; -*-
+
+import os
+import string
+import subprocess
+import time
+
+# flow_n = 252
+# threads_n = [1, 2, 3, 4, 5, 10, 20, 30, 40, 50, 100]
+# iterations_n = 10
+
+flow_n = 1
+threads_n = [1]
+iterations_n = 10
+# iterations_n = 100
+
+# flow_n = 42
+# flow_n = 420
+# flow_n = 1008
+
+def run_command(cmd):
+ """
+ - Run an external command, and return a tuple: stdout as the
+ first argument, and stderr as the second argument.
+ - Returns None if error.
+ """
+ try:
+ pr = subprocess.Popen(cmd, stdout = subprocess.PIPE, stderr = subprocess.PIPE)
+ ret_tuple = pr.communicate();
+ if pr.returncode:
+ print "%s failed with error code: %s" % (cmd, str(pr.returncode))
+ return ret_tuple
+ except OSError:
+ print "OS Error running %s" % cmd
+
+def run_install_paths(flowdef_filename):
+ # Prepare the flows to measure
+ cmd = "web/measurement_store_flow.py -f " + flowdef_filename
+ os.system(cmd)
+
+def run_measurement(thread_n):
+ # Install the Flow Paths
+ cmd = ["web/measurement_install_paths.py", str(thread_n)]
+ run_command(cmd)
+
+ # Get the measurement data and print it
+ cmd = "web/measurement_get_install_paths_time_nsec.py"
+ r = run_command(cmd) # Tuple: [<stdout>, <stderr>]
+ res = r[0].split() # Tuple: [<num>, nsec]
+ nsec_str = res[0]
+ msec = float(nsec_str) / (1000 * 1000)
+
+ # Get the measurement data and print it
+ cmd = "web/measurement_get_per_flow_install_time.py"
+ r = run_command(cmd) # Tuple: [<stdout>, <stderr>]
+ res = r[0]
+ print res
+
+ # Keep checking until all Flow Paths are installed
+ while True:
+ # time.sleep(3)
+ cmd = ["web/get_flow.py", "all"]
+ r = run_command(cmd)
+ if string.count(r[0], "FlowPath") != flow_n:
+ continue
+ if string.find(r[0], "NOT") == -1:
+ break
+
+ # Remove the installed Flow Paths
+ cmd = ["web/delete_flow.py", "all"]
+ run_command(cmd)
+
+ # Keep checking until all Flows are removed
+ while True:
+ # time.sleep(3)
+ cmd = ["web/get_flow.py", "all"]
+ r = run_command(cmd)
+ if r[0] == "":
+ break
+
+ return msec
+
+
+if __name__ == "__main__":
+
+ # Initial cleanup
+ cmd = "web/measurement_clear_all_paths.py"
+ run_command(cmd)
+
+ # Install the Flow Paths to measure
+ flowdef_filename = "web/flowdef_8node_" + str(flow_n) + ".txt"
+ run_install_paths(flowdef_filename)
+
+ # Do the work
+ for thread_n in threads_n:
+ for n in range(iterations_n):
+ msec = run_measurement(thread_n)
+ # Format: <number of threads> <time in ms>
+ print "%d %f" % (thread_n, msec / flow_n)
+
+ # Cleanup on exit
+ cmd = "web/measurement_clear_all_paths.py"
+ run_command(cmd)
diff --git a/web/measurement_store_flow.py b/web/measurement_store_flow.py
new file mode 100755
index 0000000..637ab3e
--- /dev/null
+++ b/web/measurement_store_flow.py
@@ -0,0 +1,447 @@
+#! /usr/bin/env python
+# -*- Mode: python; py-indent-offset: 4; tab-width: 8; indent-tabs-mode: t; -*-
+
+import copy
+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
+ReadFromFile = ""
+
+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)
+
+def measurement_store_path_flow(flow_path):
+ flow_path_json = json.dumps(flow_path)
+
+ try:
+ command = "curl -s -H 'Content-Type: application/json' -d '%s' http://%s:%s/wm/flow/measurement-store-path/json" % (flow_path_json, ControllerIP, ControllerPort)
+ debug("measurement_store_path_flow %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)
+
+def extract_flow_args(my_args):
+ # Check the arguments
+ if len(my_args) < 6:
+ log_error(usage_msg)
+ exit(1)
+
+ # Extract the mandatory arguments
+ my_flow_id = my_args[0]
+ my_installer_id = my_args[1]
+ my_src_dpid = my_args[2]
+ my_src_port = my_args[3]
+ my_dst_dpid = my_args[4]
+ my_dst_port = my_args[5]
+
+ #
+ # Extract the "match" and "action" arguments
+ #
+ match = {}
+ matchInPortEnabled = True # NOTE: Enabled by default
+ actions = []
+ actionOutputEnabled = True # NOTE: Enabled by default
+ idx = 6
+ while idx < len(my_args):
+ action = {}
+ arg1 = my_args[idx]
+ idx = idx + 1
+ # Extract the second argument
+ if idx >= len(my_args):
+ error_arg = "ERROR: Missing or invalid '" + arg1 + "' argument"
+ log_error(error_arg)
+ log_error(usage_msg)
+ exit(1)
+ arg2 = my_args[idx]
+ idx = idx + 1
+
+ if arg1 == "matchInPort":
+ # Just mark whether inPort matching is enabled
+ matchInPortEnabled = arg2 in ['True', 'true']
+ # inPort = {}
+ # inPort['value'] = int(arg2, 0)
+ # match['inPort'] = inPort
+ ## match['matchInPort'] = True
+ elif arg1 == "matchSrcMac":
+ srcMac = {}
+ srcMac['value'] = arg2
+ match['srcMac'] = srcMac
+ # match['matchSrcMac'] = True
+ elif arg1 == "matchDstMac":
+ dstMac = {}
+ dstMac['value'] = arg2
+ match['dstMac'] = dstMac
+ # match['matchDstMac'] = True
+ elif arg1 == "matchVlanId":
+ match['vlanId'] = int(arg2, 0)
+ # match['matchVlanId'] = True
+ elif arg1 == "matchVlanPriority":
+ match['vlanPriority'] = int(arg2, 0)
+ # match['matchVlanPriority'] = True
+ elif arg1 == "matchEthernetFrameType":
+ match['ethernetFrameType'] = int(arg2, 0)
+ # match['matchEthernetFrameType'] = True
+ elif arg1 == "matchIpToS":
+ match['ipToS'] = int(arg2, 0)
+ # match['matchIpToS'] = True
+ elif arg1 == "matchIpProto":
+ match['ipProto'] = int(arg2, 0)
+ # match['matchIpProto'] = True
+ elif arg1 == "matchSrcIPv4Net":
+ srcIPv4Net = {}
+ srcIPv4Net['value'] = arg2
+ match['srcIPv4Net'] = srcIPv4Net
+ # match['matchSrcIPv4Net'] = True
+ elif arg1 == "matchDstIPv4Net":
+ dstIPv4Net = {}
+ dstIPv4Net['value'] = arg2
+ match['dstIPv4Net'] = dstIPv4Net
+ # match['matchDstIPv4Net'] = True
+ elif arg1 == "matchSrcTcpUdpPort":
+ match['srcTcpUdpPort'] = int(arg2, 0)
+ # match['matchSrcTcpUdpPort'] = True
+ elif arg1 == "matchDstTcpUdpPort":
+ match['dstTcpUdpPort'] = int(arg2, 0)
+ # match['matchDstTcpUdpPort'] = True
+ elif arg1 == "actionOutput":
+ # Just mark whether ACTION_OUTPUT action is enabled
+ actionOutputEnabled = arg2 in ['True', 'true']
+ #
+ # TODO: Complete the implementation for ACTION_OUTPUT
+ # actionOutput = {}
+ # outPort = {}
+ # outPort['value'] = int(arg2, 0)
+ # actionOutput['port'] = outPort
+ # actionOutput['maxLen'] = int(arg3, 0)
+ # action['actionOutput'] = actionOutput
+ # # action['actionType'] = 'ACTION_OUTPUT'
+ # actions.append(action)
+ #
+ elif arg1 == "actionSetVlanId":
+ vlanId = {}
+ vlanId['vlanId'] = int(arg2, 0)
+ action['actionSetVlanId'] = vlanId
+ # action['actionType'] = 'ACTION_SET_VLAN_VID'
+ actions.append(copy.deepcopy(action))
+ elif arg1 == "actionSetVlanPriority":
+ vlanPriority = {}
+ vlanPriority['vlanPriority'] = int(arg2, 0)
+ action['actionSetVlanPriority'] = vlanPriority
+ # action['actionType'] = 'ACTION_SET_VLAN_PCP'
+ actions.append(copy.deepcopy(action))
+ elif arg1 == "actionSetIpToS":
+ ipToS = {}
+ ipToS['ipToS'] = int(arg2, 0)
+ action['actionSetIpToS'] = ipToS
+ # action['actionType'] = 'ACTION_SET_NW_TOS'
+ actions.append(copy.deepcopy(action))
+ elif arg1 == "actionSetTcpUdpSrcPort":
+ tcpUdpSrcPort = {}
+ tcpUdpSrcPort['port'] = int(arg2, 0)
+ action['actionSetTcpUdpSrcPort'] = tcpUdpSrcPort
+ # action['actionType'] = 'ACTION_SET_TP_SRC'
+ actions.append(copy.deepcopy(action))
+ elif arg1 == "actionSetTcpUdpDstPort":
+ tcpUdpDstPort = {}
+ tcpUdpDstPort['port'] = int(arg2, 0)
+ action['actionSetTcpUdpDstPort'] = tcpUdpDstPort
+ # action['actionType'] = 'ACTION_SET_TP_DST'
+ actions.append(copy.deepcopy(action))
+ elif arg1 == "actionStripVlan":
+ stripVlan = {}
+ stripVlan['stripVlan'] = arg2 in ['True', 'true']
+ action['actionStripVlan'] = stripVlan
+ # action['actionType'] = 'ACTION_STRIP_VLAN'
+ actions.append(copy.deepcopy(action))
+ elif arg1 == "actionSetEthernetSrcAddr":
+ ethernetSrcAddr = {}
+ ethernetSrcAddr['value'] = arg2
+ setEthernetSrcAddr = {}
+ setEthernetSrcAddr['addr'] = ethernetSrcAddr
+ action['actionSetEthernetSrcAddr'] = setEthernetSrcAddr
+ # action['actionType'] = 'ACTION_SET_DL_SRC'
+ actions.append(copy.deepcopy(action))
+ elif arg1 == "actionSetEthernetDstAddr":
+ ethernetDstAddr = {}
+ ethernetDstAddr['value'] = arg2
+ setEthernetDstAddr = {}
+ setEthernetDstAddr['addr'] = ethernetDstAddr
+ action['actionSetEthernetDstAddr'] = setEthernetDstAddr
+ # action['actionType'] = 'ACTION_SET_DL_DST'
+ actions.append(copy.deepcopy(action))
+ elif arg1 == "actionSetIPv4SrcAddr":
+ IPv4SrcAddr = {}
+ IPv4SrcAddr['value'] = arg2
+ setIPv4SrcAddr = {}
+ setIPv4SrcAddr['addr'] = IPv4SrcAddr
+ action['actionSetIPv4SrcAddr'] = setIPv4SrcAddr
+ # action['actionType'] = 'ACTION_SET_NW_SRC'
+ actions.append(copy.deepcopy(action))
+ elif arg1 == "actionSetIPv4DstAddr":
+ IPv4DstAddr = {}
+ IPv4DstAddr['value'] = arg2
+ setIPv4DstAddr = {}
+ setIPv4DstAddr['addr'] = IPv4DstAddr
+ action['actionSetIPv4DstAddr'] = setIPv4DstAddr
+ # action['actionType'] = 'ACTION_SET_NW_DST'
+ actions.append(copy.deepcopy(action))
+ elif arg1 == "actionEnqueue":
+ # TODO: Implement ACTION_ENQUEUE
+ actionEnqueue = {}
+ # actionEnqueue['queueId'] = int(arg2, 0)
+ # enqueuePort = {}
+ # enqueuePort['value'] = int(arg3, 0)
+ # actionEnqueue['port'] = enqueuePort
+ # action['actionEnqueue'] = actionEnqueue
+ # # action['actionType'] = 'ACTION_ENQUEUE'
+ # actions.append(copy.deepcopy(action))
+ #
+ else:
+ log_error("ERROR: Unknown argument '%s'" % (arg1))
+ log_error(usage_msg)
+ exit(1)
+
+ return {
+ 'my_flow_id' : my_flow_id,
+ 'my_installer_id' : my_installer_id,
+ 'my_src_dpid' : my_src_dpid,
+ 'my_src_port' : my_src_port,
+ 'my_dst_dpid' : my_dst_dpid,
+ 'my_dst_port' : my_dst_port,
+ 'match' : match,
+ 'matchInPortEnabled' : matchInPortEnabled,
+ 'actions' : actions,
+ 'actionOutputEnabled' : actionOutputEnabled
+ }
+
+def compute_flow_path(parsed_args, data_path):
+
+ my_flow_id = parsed_args['my_flow_id']
+ my_installer_id = parsed_args['my_installer_id']
+ match = parsed_args['match']
+ matchInPortEnabled = parsed_args['matchInPortEnabled']
+ actions = parsed_args['actions']
+ actionOutputEnabled = parsed_args['actionOutputEnabled']
+ my_data_path = copy.deepcopy(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
+
+ if (len(match) > 0):
+ flow_path['flowEntryMatch'] = copy.deepcopy(match)
+
+ #
+ # Add the match conditions to each flow entry
+ #
+ if (len(match) > 0) or matchInPortEnabled:
+ idx = 0
+ while idx < len(my_data_path['flowEntries']):
+ if matchInPortEnabled:
+ inPort = my_data_path['flowEntries'][idx]['inPort']
+ match['inPort'] = copy.deepcopy(inPort)
+ # match['matchInPort'] = True
+ my_data_path['flowEntries'][idx]['flowEntryMatch'] = copy.deepcopy(match)
+ idx = idx + 1
+
+ #
+ # Set the actions for each flow entry
+ # NOTE: The actions from the command line are aplied
+ # ONLY to the first flow entry.
+ #
+ # If ACTION_OUTPUT action is enabled, then apply it
+ # to each flow entry.
+ #
+ if (len(actions) > 0) or actionOutputEnabled:
+ idx = 0
+ while idx < len(my_data_path['flowEntries']):
+ if idx > 0:
+ actions = [] # Reset the actions for all but first entry
+ action = {}
+ outPort = my_data_path['flowEntries'][idx]['outPort']
+ actionOutput = {}
+ actionOutput['port'] = copy.deepcopy(outPort)
+ # actionOutput['maxLen'] = 0 # TODO: not used for now
+ action['actionOutput'] = copy.deepcopy(actionOutput)
+ # action['actionType'] = 'ACTION_OUTPUT'
+ actions.append(copy.deepcopy(action))
+
+ my_data_path['flowEntries'][idx]['flowEntryActions'] = copy.deepcopy(actions)
+ idx = idx + 1
+
+
+ flow_path['dataPath'] = my_data_path
+ debug("Flow Path: %s" % flow_path)
+ return flow_path
+
+def measurement_store_paths(parsed_args):
+ idx = 0
+ while idx < len(parsed_args):
+ data_path = {}
+ src_dpid = {}
+ src_port = {}
+ dst_dpid = {}
+ dst_port = {}
+ src_switch_port = {}
+ dst_switch_port = {}
+ flow_entries = []
+
+ src_dpid['value'] = parsed_args[idx]['my_src_dpid']
+ src_port['value'] = parsed_args[idx]['my_src_port']
+ dst_dpid['value'] = parsed_args[idx]['my_dst_dpid']
+ dst_port['value'] = parsed_args[idx]['my_dst_port']
+ src_switch_port['dpid'] = src_dpid
+ src_switch_port['port'] = src_port
+ dst_switch_port['dpid'] = dst_dpid
+ dst_switch_port['port'] = dst_port
+
+ data_path['srcPort'] = copy.deepcopy(src_switch_port)
+ data_path['dstPort'] = copy.deepcopy(dst_switch_port)
+ data_path['flowEntries'] = copy.deepcopy(flow_entries)
+
+ #
+ # XXX: Explicitly disable the InPort matching, and
+ # the Output action, because they get in the way
+ # during the compute_flow_path() processing.
+ #
+ parsed_args[idx]['matchInPortEnabled'] = False
+ parsed_args[idx]['actionOutputEnabled'] = False
+
+ flow_path = compute_flow_path(parsed_args[idx], data_path)
+ measurement_store_path_flow(flow_path)
+
+ idx = idx + 1
+
+
+if __name__ == "__main__":
+ usage_msg = "Store Flow Paths into ONOS for measurement purpose.\n"
+ usage_msg = usage_msg + "\n"
+ usage_msg = usage_msg + "Usage: %s [Flags] <flow-id> <installer-id> <src-dpid> <src-port> <dest-dpid> <dest-port> [Match Conditions] [Actions]\n" % (sys.argv[0])
+ usage_msg = usage_msg + "\n"
+ usage_msg = usage_msg + " Flags:\n"
+ usage_msg = usage_msg + " -f <filename> Read the flow(s) to install from a file\n"
+ usage_msg = usage_msg + " File format: one line per flow starting with <flow-id>\n"
+ usage_msg = usage_msg + "\n"
+ usage_msg = usage_msg + " Match Conditions:\n"
+ usage_msg = usage_msg + " matchInPort <True|False> (default to True)\n"
+ usage_msg = usage_msg + " matchSrcMac <source MAC address>\n"
+ usage_msg = usage_msg + " matchDstMac <destination MAC address>\n"
+ usage_msg = usage_msg + " matchSrcIPv4Net <source IPv4 network address>\n"
+ usage_msg = usage_msg + " matchDstIPv4Net <destination IPv4 network address>\n"
+ usage_msg = usage_msg + " matchEthernetFrameType <Ethernet frame type>\n"
+ usage_msg = usage_msg + "\n"
+ usage_msg = usage_msg + " Match Conditions (not implemented yet):\n"
+ usage_msg = usage_msg + " matchVlanId <VLAN ID>\n"
+ usage_msg = usage_msg + " matchVlanPriority <VLAN priority>\n"
+ usage_msg = usage_msg + " matchIpToS <IP ToS (DSCP field, 6 bits)>\n"
+ usage_msg = usage_msg + " matchIpProto <IP protocol>\n"
+ usage_msg = usage_msg + " matchSrcTcpUdpPort <source TCP/UDP port>\n"
+ usage_msg = usage_msg + " matchDstTcpUdpPort <destination TCP/UDP port>\n"
+ usage_msg = usage_msg + "\n"
+ usage_msg = usage_msg + " Actions:\n"
+ usage_msg = usage_msg + " actionOutput <True|False> (default to True)\n"
+ usage_msg = usage_msg + " actionSetEthernetSrcAddr <source MAC address>\n"
+ usage_msg = usage_msg + " actionSetEthernetDstAddr <destination MAC address>\n"
+ usage_msg = usage_msg + " actionSetIPv4SrcAddr <source IPv4 address>\n"
+ usage_msg = usage_msg + " actionSetIPv4DstAddr <destination IPv4 address>\n"
+ usage_msg = usage_msg + "\n"
+ usage_msg = usage_msg + " Actions (not implemented yet):\n"
+ usage_msg = usage_msg + " actionSetVlanId <VLAN ID>\n"
+ usage_msg = usage_msg + " actionSetVlanPriority <VLAN priority>\n"
+ usage_msg = usage_msg + " actionSetIpToS <IP ToS (DSCP field, 6 bits)>\n"
+ usage_msg = usage_msg + " actionSetTcpUdpSrcPort <source TCP/UDP port>\n"
+ usage_msg = usage_msg + " actionSetTcpUdpDstPort <destination TCP/UDP port>\n"
+ usage_msg = usage_msg + " actionStripVlan <True|False>\n"
+ usage_msg = usage_msg + " actionEnqueue <dummy argument>\n"
+
+ # 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 the flags
+ #
+ start_argv_index = 1
+ idx = 1
+ while idx < len(sys.argv):
+ arg1 = sys.argv[idx]
+ idx = idx + 1
+ if arg1 == "-f":
+ if idx >= len(sys.argv):
+ error_arg = "ERROR: Missing or invalid '" + arg1 + "' argument"
+ log_error(error_arg)
+ log_error(usage_msg)
+ exit(1)
+ ReadFromFile = sys.argv[idx]
+ idx = idx + 1
+ start_argv_index = idx
+ else:
+ break;
+
+ #
+ # Read the arguments from a file or from the remaining command line options
+ #
+ my_lines = []
+ if len(ReadFromFile) > 0:
+ f = open(ReadFromFile, "rt")
+ my_line = f.readline()
+ while my_line:
+ if len(my_line.rstrip()) > 0 and my_line[0] != "#":
+ my_token_line = my_line.rstrip().split()
+ my_lines.append(my_token_line)
+ my_line = f.readline()
+ else:
+ my_lines.append(copy.deepcopy(sys.argv[start_argv_index:]))
+
+ #
+ # Initialization
+ #
+ last_data_paths = []
+ parsed_args = []
+ idx = 0
+ while idx < len(my_lines):
+ last_data_path = []
+ last_data_paths.append(copy.deepcopy(last_data_path))
+ #
+ # Parse the flow arguments
+ #
+ my_args = my_lines[idx]
+ parsed_args.append(copy.deepcopy(extract_flow_args(my_args)))
+
+ idx = idx + 1
+
+ #
+ measurement_store_paths(parsed_args)
diff --git a/web/shortest_path.py b/web/shortest_path.py
index b379a82..0f23bf4 100755
--- a/web/shortest_path.py
+++ b/web/shortest_path.py
@@ -20,7 +20,7 @@
ControllerIP="127.0.0.1"
ControllerPort=8080
-DEBUG=1
+DEBUG=0
pp = pprint.PrettyPrinter(indent=4)
app = Flask(__name__)