Merge remote-tracking branch 'upstream/master'
diff --git a/build.xml b/build.xml
index 1c473ce..657a80d 100644
--- a/build.xml
+++ b/build.xml
@@ -26,7 +26,7 @@
generated code.
-->
-<project default="dist" name="Floodlight">
+<project default="jar" name="Floodlight">
<property name="target" location="target"/>
<property name="build" location="${target}/bin"/>
<property name="build-test" location="${target}/bin-test"/>
@@ -66,9 +66,9 @@
<include name="concurrentlinkedhashmap-lru-1.3.jar"/>
<include name="jython-2.5.2.jar"/>
<include name="libthrift-0.7.0.jar"/>
- <include name="curator-client-1.3.4-SNAPSHOT.jar"/>
- <include name="curator-framework-1.3.4-SNAPSHOT.jar"/>
- <include name="curator-recipes-1.3.4-SNAPSHOT.jar"/>
+ <include name="curator-client-1.3.5-SNAPSHOT.jar"/>
+ <include name="curator-framework-1.3.5-SNAPSHOT.jar"/>
+ <include name="curator-recipes-1.3.5-SNAPSHOT.jar"/>
<include name="zookeeper-3.4.5.jar"/>
</patternset>
diff --git a/cleanup-cassandra.sh b/cleanup-cassandra.sh
deleted file mode 100755
index 2844c1a..0000000
--- a/cleanup-cassandra.sh
+++ /dev/null
@@ -1,3 +0,0 @@
-#! /bin/bash
-DIR=~/ONOS
-~/titan-0.2.0/bin/gremlin.sh -e $DIR/cleanup-onos-db
diff --git a/cluster-mgmt/README.txt b/cluster-mgmt/README.txt
new file mode 100644
index 0000000..974e3d2
--- /dev/null
+++ b/cluster-mgmt/README.txt
@@ -0,0 +1,22 @@
+########
+Cluster Management Tools
+#######
+
+ssh : contains necessary files in .ssh (don't change file permission)
+bash_profile: Can be used as $HOME/.bash_profile file. The following four lines needs to be changed as necessary
+
+export RCP_USER=ubuntu
+export RCMD_CMD=ssh
+export RCMD_CMD_ARGS="-i $HOME/.ssh/onlabkey.pem"
+export RCMD_USER=ubuntu
+export RCP_CMD="scp -i $HOME/.ssh/onlabkey.pem -o StrictHostKeyChecking=no"
+export FANOUT=64
+export CLUSTER="$HOME/cluster-mgmt/cluster.txt"
+
+### Set the proper value ##
+export ONOS_CLUSTER_BASENAME="onosdevx"
+export ONOS_CLUSTER_NR_NODES=8
+
+bin/start.sh : shutdown all service and restart
+bin/stop.sh : shutdown all service
+bin/status.sh : show status of the services
diff --git a/cluster-mgmt/bash_profile b/cluster-mgmt/bash_profile
new file mode 100644
index 0000000..e9f2a39
--- /dev/null
+++ b/cluster-mgmt/bash_profile
@@ -0,0 +1,25 @@
+# .bash_profile
+
+# Get the aliases and functions
+if [ -f ~/.bashrc ]; then
+ . ~/.bashrc
+fi
+
+# User specific environment and startup programs
+
+PATH=$PATH:$HOME/bin
+
+export PATH
+
+### Cluster-IT setting##
+export RCP_USER=ubuntu
+export RCMD_CMD=ssh
+export RCMD_CMD_ARGS="-i $HOME/.ssh/onlabkey.pem"
+export RCMD_USER=ubuntu
+export RCP_CMD="scp -i $HOME/.ssh/onlabkey.pem -o StrictHostKeyChecking=no"
+export FANOUT=64
+export CLUSTER="$HOME/cluster-mgmt/cluster.txt"
+
+#### Set the proper value ####
+#export ONOS_CLUSTER_BASENAME="onosdevx"
+#export ONOS_CLUSTER_NR_NODES=8
diff --git a/cluster-mgmt/bin/start.sh b/cluster-mgmt/bin/start.sh
new file mode 100755
index 0000000..bd65df7
--- /dev/null
+++ b/cluster-mgmt/bin/start.sh
@@ -0,0 +1,13 @@
+#! /bin/bash
+. $HOME/cluster-mgmt/func.sh
+
+onos stop
+cassandra cleandb
+cassandra stop
+zk stop
+
+zk start
+cassandra start
+cassandra cleandb
+onos start
+dsh -g $basename 'cd ONOS; ./ctrl-local.sh'
diff --git a/cluster-mgmt/bin/status.sh b/cluster-mgmt/bin/status.sh
new file mode 100755
index 0000000..880bf10
--- /dev/null
+++ b/cluster-mgmt/bin/status.sh
@@ -0,0 +1,9 @@
+#! /bin/bash
+. $HOME/cluster-mgmt/func.sh
+
+basename="onosdevb"
+nr_nodes=4
+
+onos status
+cassandra status
+zk status
diff --git a/cluster-mgmt/bin/stop.sh b/cluster-mgmt/bin/stop.sh
new file mode 100755
index 0000000..a8f64ca
--- /dev/null
+++ b/cluster-mgmt/bin/stop.sh
@@ -0,0 +1,10 @@
+#! /bin/bash
+. $HOME/cluster-mgmt/func.sh
+
+basename="onosdevc"
+nr_nodes=4
+
+onos stop
+cassandra cleandb
+cassandra stop
+zk stop
diff --git a/cluster-mgmt/common/hosts b/cluster-mgmt/common/hosts
new file mode 100644
index 0000000..220b30f
--- /dev/null
+++ b/cluster-mgmt/common/hosts
@@ -0,0 +1,11 @@
+127.0.0.1 localhost
+
+# The following lines are desirable for IPv6 capable hosts
+::1 ip6-localhost ip6-loopback
+fe00::0 ip6-localnet
+ff00::0 ip6-mcastprefix
+ff02::1 ip6-allnodes
+ff02::2 ip6-allrouters
+ff02::3 ip6-allhosts
+
+## For ONOS Development
diff --git a/cluster-mgmt/common/known_hosts b/cluster-mgmt/common/known_hosts
new file mode 100644
index 0000000..bc8d892
--- /dev/null
+++ b/cluster-mgmt/common/known_hosts
@@ -0,0 +1,2 @@
+|1|vpuCVwBaUAW338i8XkTyuZpPn3o=|OEtDpg0rUr4I6MJrPU3UgO6xIjY= ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAq2A7hRGmdnm9tUDbO9IDSwBK6TbQa+PXYPCPy6rbTrTtw7PHkccKrpp0yVhp5HdEIcKr6pLlVDBfOLX9QUsyCOV0wzfjIJNlGEYsdlLJizHhbn2mUjvSAHQqZETYP81eFzLQNnPHt4EVVUh7VfDESU84KezmD5QlWpXLmvU31/yMf+Se8xhHTvKSCZIFImWwoG6mbUoWf9nzpIoaSjB+weqqUUmpaaasXVal72J+UX2B+2RPW3RcT0eOzQgqlJL3RKrTJvdsjE3JEAvGq3lGHSZXy28G3skua2SmVi/w4yCE6gbODqnTWlg7+wC604ydGXA8VJiS5ap43JXiUFFAaQ==
+|1|oQEfymNRsrXOo9uHu/jCST0f0I0=|UqxLCIvwPdgIlZWmusieRLCzRxE= ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAvCFIZTznMUPbS/r6b0Gw9jcnOBbH21wcBKETjXg9U5bMwHz2ocnEK8PPL1EK8uUTjZ3Kbilx4Jeio8HXEWtUkyOF/KyW1nXd0mxrWqqGQjFlpPj017Wfo0KIISgCWB9L8RJJ3aJ0selZwvmdHmg7uS306UGsJf1co2qubLGMAsdjPhYpvKXSJHoThupHBCuoqqOw80Tt5b3qJ6RwFjt/QiCgom9KoQn2DMQhS0iB9h5NHpejDX9/qLgFFiF3PdXaBCTE+vFLvoXwecp/x3pP2c8zA6FhCzYbZxLYMdMHqSmJRSKALWU3Qg9ekdXUBfzrLs4lPQ6UGFcku9WBAtN7oQ==
diff --git a/cluster-mgmt/common/onos.properties b/cluster-mgmt/common/onos.properties
new file mode 100644
index 0000000..1828db7
--- /dev/null
+++ b/cluster-mgmt/common/onos.properties
@@ -0,0 +1,18 @@
+floodlight.modules = net.floodlightcontroller.storage.memory.MemoryStorageSource,\
+net.floodlightcontroller.core.FloodlightProvider,\
+net.floodlightcontroller.threadpool.ThreadPool,\
+net.floodlightcontroller.devicemanager.internal.DeviceManagerImpl,\
+net.floodlightcontroller.staticflowentry.StaticFlowEntryPusher,\
+net.floodlightcontroller.firewall.Firewall,\
+net.floodlightcontroller.jython.JythonDebugInterface,\
+net.floodlightcontroller.counter.CounterStore,\
+net.floodlightcontroller.perfmon.PktInProcessingTime,\
+net.floodlightcontroller.ui.web.StaticWebRoutable,\
+net.floodlightcontroller.onoslistener.OnosPublisher, \
+net.onrc.onos.registry.controller.ZookeeperRegistry
+net.floodlightcontroller.restserver.RestApiServer.port = 8080
+net.floodlightcontroller.core.FloodlightProvider.openflowport = 6633
+net.floodlightcontroller.jython.JythonDebugInterface.port = 6655
+net.floodlightcontroller.forwarding.Forwarding.idletimeout = 5
+net.floodlightcontroller.forwarding.Forwarding.hardtimeout = 0
+net.floodlightcontroller.onoslistener.OnosPublisher.dbconf = /tmp/cassandra.titan
diff --git a/cluster-mgmt/common/zoo.cfg b/cluster-mgmt/common/zoo.cfg
new file mode 100644
index 0000000..c4e1eb3
--- /dev/null
+++ b/cluster-mgmt/common/zoo.cfg
@@ -0,0 +1,45 @@
+# The number of milliseconds of each tick
+tickTime=2000
+# The number of ticks that the initial
+# synchronization phase can take
+initLimit=10
+# The number of ticks that can pass between
+# sending a request and getting an acknowledgement
+syncLimit=5
+# the directory where the snapshot is stored.
+# do not use /tmp for storage, /tmp here is just
+# example sakes.
+dataDir=/var/lib/zookeeper
+# the port at which the clients will connect
+clientPort=2181
+#
+# specify all servers in the Zookeeper ensemble
+
+#server.1=onosgui1:2888:3888
+#server.2=onosgui2:2888:3888
+#server.3=onosgui3:2888:3888
+#server.4=onosgui4:2888:3888
+#server.5=onosgui5:2888:3888
+#server.6=onosgui6:2888:3888
+#server.7=onosgui7:2888:3888
+#server.8=onosgui8:2888:3888
+#
+#
+# Be sure to read the maintenance section of the
+# administrator guide before turning on autopurge.
+#
+#
+# Be sure to read the maintenance section of the
+# administrator guide before turning on autopurge.
+#
+# http://zookeeper.apache.org/doc/current/zookeeperAdmin.html#sc_maintenance
+#
+# The number of snapshots to retain in dataDir
+#autopurge.snapRetainCount=3
+# Purge task interval in hours
+# Set to "0" to disable auto purge feature
+#autopurge.purgeInterval=1
+server.1=test1:2888:3888
+server.2=test2:2888:3888
+server.3=test3:2888:3888
+server.4=test4:2888:3888
diff --git a/cluster-mgmt/cp-config.sh b/cluster-mgmt/cp-config.sh
new file mode 100755
index 0000000..5c74462
--- /dev/null
+++ b/cluster-mgmt/cp-config.sh
@@ -0,0 +1,69 @@
+#! /bin/bash
+USERNAME=ubuntu
+CASSANDRA_DIR='/home/ubuntu/apache-cassandra-1.1.4'
+ZK_DIR='/home/ubuntu/zookeeper-3.4.5'
+ZK_LIB='/var/lib/zookeeper'
+CASSANDRA_LIB='/var/lib/cassandra'
+
+SSH_COPY="authorized_keys id_rsa id_rsa.pub known_hosts onlab-gui.pem onlabkey.pem"
+
+if [ $# == 2 ]; then
+ NR_NODES=$1
+ basename=$2
+else
+ echo "$0 nr_nodes basename"
+ exit
+fi
+
+if [ ! -f ./cluster.txt ]; then
+ echo "Cannot find cluster.txt"
+ exit
+fi
+
+export CLUSTER="./cluster.txt"
+dsh -g $basename 'uname -a'
+
+echo "Stopping Services"
+#dsh -g $basename 'cd ONOS; ./start-onos.sh stop'
+#dsh -g $basename 'cd ONOS; ./stop-cassandra stop'
+#dsh -g $basename '$ZK_DIR/bin/zkServer.sh stop'
+
+# authorized_keys cassandra.yaml hosts id_rsa id_rsa.pub known_hosts onlab-gui.pem onlabkey.pem onos.properties zoo.cfg
+## SSH Setting
+dsh -g $basename 'mkdir -m 700 .ssh'
+for n in $SSH_COPY; do
+ pcp -g $basename common/$n '.ssh'
+ if [ $n != "id_rsa.pub" ] ; then
+ dsh -g $basename "chmod 600 .ssh/$n"
+ fi
+done
+
+dsh -g $basename "sudo rm -rf $CASSANDRA_LIB/commitlog/*"
+dsh -g $basename "sudo rm -rf $CASSANDRA_LIB/saved_caches/*"
+dsh -g $basename "sudo rm -rf $CASSANDRA_LIB/data/*"
+dsh -g $basename "sudo chown -R $username:$username $CASSANDRA_LIB"
+
+dsh -g $basename "sudo rm -rf $ZK_LIB/version-2*"
+dsh -g $basename "sudo rm -rf $ZK_LIB/myid"
+
+pcp -g $basename common/cassandra.yaml $CASSANDRA_DIR/conf
+pcp -g $basename common/zoo.cfg $ZK_DIR/conf
+pcp -g $basename common/hosts '~'
+
+for n in `seq 1 $NR_NODES`; do
+ pcp -w ${basename}${n} ${basename}${n}/hostname '~'
+ pcp -w ${basename}${n} ${basename}${n}/myid $ZK_DIR/conf
+done
+
+dsh -g $basename 'sudo cp ~/hostname /etc'
+dsh -g $basename 'sudo cp ~/hosts /etc'
+dsh -g $basename "cd $ZK_LIB; sudo ln -s $ZK_DIR/conf/myid"
+
+dsh -g $basename 'sudo hostname `cat /etc/hostname`'
+
+for n in `seq 2 $NR_NODES`; do
+ pcp -w ${basename}${n} ${basename}${n}/onsdemo_edge.py 'ONOS/test-network/mininet'
+ pcp -w ${basename}${n} ${basename}${n}/tunnel_onos_edge.sh 'ONOS/test-network/mininet'
+done
+pcp -w ${basename}1 ${basename}1/tunnel_onos_core.sh 'ONOS/test-network/mininet'
+pcp -w ${basename}1 ${basename}1/onsdemo_core.py 'ONOS/test-network/mininet'
diff --git a/cluster-mgmt/cp-mininet.sh b/cluster-mgmt/cp-mininet.sh
new file mode 100755
index 0000000..c3d69fe
--- /dev/null
+++ b/cluster-mgmt/cp-mininet.sh
@@ -0,0 +1,29 @@
+#! /bin/bash
+USERNAME=ubuntu
+CASSANDRA_DIR='/home/ubuntu/apache-cassandra-1.1.4'
+ZK_DIR='/home/ubuntu/zookeeper-3.4.5'
+ZK_LIB='/var/lib/zookeeper'
+CASSANDRA_LIB='/var/lib/cassandra'
+
+if [ $# == 2 ]; then
+ NR_NODES=$1
+ basename=$2
+else
+ echo "$0 nr_nodes basename"
+ exit
+fi
+
+if [ ! -f ./cluster.txt ]; then
+ echo "Cannot find cluster.txt"
+ exit
+fi
+
+export CLUSTER="./cluster.txt"
+dsh -g $basename 'uname -a'
+
+for n in `seq 1 $NR_NODES`; do
+ pcp -w ${basename}${n} ${basename}${n}/onsdemo.py 'ONOS/test-network/mininet'
+ pcp -w ${basename}${n} ${basename}${n}/tunnel_onsdemo.sh 'ONOS/test-network/mininet'
+done
+dsh -g $basename 'chmod 755 ONOS/test-network/mininet/tunnel_onsdemo.sh'
+dsh -g $basename 'chmod 755 ONOS/test-network/mininet/onsdemo.py'
diff --git a/cluster-mgmt/func.sh b/cluster-mgmt/func.sh
new file mode 100755
index 0000000..e7d976d
--- /dev/null
+++ b/cluster-mgmt/func.sh
@@ -0,0 +1,109 @@
+USERNAME=ubuntu
+CASSANDRA_DIR='/home/ubuntu/apache-cassandra-1.1.4'
+ZK_DIR='/home/ubuntu/zookeeper-3.4.5'
+ONOS_DIR='/home/ubuntu/ONOS'
+ZK_LIB='/var/lib/zookeeper'
+CASSANDRA_LIB='/var/lib/cassandra'
+
+if [ x$CLUSTER == "x" ]; then
+ echo "CLUSTER is not set. Exitting."
+ exit
+fi
+if [ x$ONOS_CLUSTER_BASENAME == "x" ]; then
+ echo "ONOS_CLUSTER_BASENAME is not set. Exitting"
+ exit
+fi
+if [ x$ONOS_CLUSTER_NR_NODES == "x" ]; then
+ echo "ONOS_CLUSTER_NR_NODES is not set. Exitting"
+ exit
+fi
+
+export basename=$ONOS_CLUSTER_BASENAME
+export nr_nodes=$ONOS_CLUSTER_NR_NODES
+
+checkcluster () {
+ dsh -g $basename 'uname -a'
+}
+
+zk () {
+ case "$1" in
+ start)
+ echo "Starting ZK.."
+ dsh -g $basename "$ZK_DIR/bin/zkServer.sh start"
+ while [ 1 ]; do
+ nup=`dsh -g $basename "$ZK_DIR/bin/zkServer.sh status" | grep "Mode" | egrep "leader|follower" | wc -l`
+ if [ $nup == $nr_nodes ]; then
+ echo "everybody's up: $nup up of of $nr_nodes"
+ echo "ZK started"
+ break;
+ fi
+ echo "waiting for everybody's up: $nup up of of $nr_nodes"
+ sleep 1
+ done
+ ;;
+ stop)
+ echo "Stopping ZK.."
+ dsh -g $basename "$ZK_DIR/bin/zkServer.sh stop"
+ ;;
+ status)
+ echo "Checking ZK Status"
+ dsh -g $basename "$ZK_DIR/bin/zkServer.sh status"
+ ;;
+ esac
+}
+
+cassandra () {
+ case "$1" in
+ start)
+ echo "Starting Cassandra.."
+ echo " start cassandra at the seed node"
+ dsh -w ${basename}1 "cd $ONOS_DIR; ./start-cassandra.sh start"
+ sleep 1
+ echo " start cassandra in rest nodes"
+ dsh -g ${basename} -x ${basename}1 "cd $ONOS_DIR; ./start-cassandra.sh start"
+ while [ 1 ]; do
+ echo $$
+ dsh -w ${basename}1 "cd $ONOS_DIR; ./start-cassandra.sh status" > .cassandra_check.$$
+ cat .cassandra_check.$$
+ nup=`cat .cassandra_check.$$ | grep Normal |grep Up| wc -l`
+ if [ $nup == $nr_nodes ]; then
+ echo "everybody's up: $nup up of of $nr_nodes"
+ echo "Cassandra started"
+ break;
+ fi
+ echo "waiting for everybody's up: $nup up of of $nr_nodes"
+ sleep 1
+ done
+ ;;
+ stop)
+ echo "Stopping Cassandra.."
+ dsh -g ${basename} "cd $ONOS_DIR; ./start-cassandra.sh stop"
+ ;;
+ cleandb)
+ echo "Removing all data in db"
+ dsh -w ${basename}1 "cd $ONOS_DIR; ./scripts/cleanup-cassandra.sh"
+ ;;
+ status)
+ echo "Checking Cassandra Status"
+ dsh -w ${basename}1 "cd $ONOS_DIR; ./start-cassandra.sh status"
+ ;;
+ esac
+}
+
+onos () {
+ case "$1" in
+ start)
+ echo "Starting ONOS"
+ dsh -g ${basename} "cd $ONOS_DIR; ./start-onos.sh start"
+ dsh -w ${basename}1 "cd $ONOS_DIR; ./start-rest.sh start"
+ ;;
+ stop)
+ echo "Stop ONOS"
+ dsh -g ${basename} "cd $ONOS_DIR; ./start-onos.sh stop"
+ ;;
+ status)
+ echo "Checking ONOS Status"
+ dsh -g ${basename} "cd $ONOS_DIR; ./start-onos.sh status"
+ ;;
+ esac
+}
diff --git a/cluster-mgmt/make-config.sh b/cluster-mgmt/make-config.sh
new file mode 100755
index 0000000..b97a818
--- /dev/null
+++ b/cluster-mgmt/make-config.sh
@@ -0,0 +1,56 @@
+#! /bin/bash
+USERNAME=ubuntu
+if [ $# == 3 ]; then
+ NR_NODES=$1
+ basename=$2
+ hosts_file=$3
+else
+ echo "$0 nr_hodes basename hostfile"
+ exit
+fi
+
+for n in `seq 1 $NR_NODES`; do
+ rm -rf ${basename}${n}
+ mkdir ${basename}${n}
+ echo "${basename}${n}" > ${basename}${n}/hostname
+ echo $n > ${basename}${n}/myid
+done
+
+## ZK config ##
+cp template/zoo.cfg common/
+for n in `seq 1 $NR_NODES`; do
+ echo "server.${n}=${basename}${n}:2888:3888"
+done >> common/zoo.cfg
+
+## Cassandra config ##
+cat template/cassandra.yaml |\
+ sed "s/__SEED__/${basename}1/g" > common/cassandra.yaml
+
+## /etc/hosts ##
+cat template/hosts $hosts_file > common/hosts
+
+## .ssh/known_hosts ##
+ssh-keyscan -H -t rsa github.com > common/known_hosts
+ssh-keyscan -H -t rsa onosnat >> common/known_hosts
+for n in `seq 1 $NR_NODES`; do
+ ssh-keyscan -H -t rsa ${basename}${n}
+done >> common/known_hosts
+
+echo "GROUP: $basename" > cluster.txt
+cat $hosts_file | awk '{print $2}' >> cluster.txt
+
+
+## Creating shell script to login each node ##
+for n in `seq 1 $NR_NODES`; do
+ cat << EOF > bin/${basename}${n}
+#!/bin/sh
+ssh $USERNAME@${basename}${n}
+EOF
+ chmod 755 bin/${basename}${n}
+done
+
+echo "======================================"
+echo "Do not forget to do the following"
+echo "paste $hosts_file to /etc/hosts"
+echo "paste cluster.txt to your CLUSTER file"
+echo "======================================"
diff --git a/cluster-mgmt/make-mininet.sh b/cluster-mgmt/make-mininet.sh
new file mode 100755
index 0000000..e314114
--- /dev/null
+++ b/cluster-mgmt/make-mininet.sh
@@ -0,0 +1,31 @@
+#! /bin/bash
+if [ $# == 3 ]; then
+ NR_NODES=$1
+ basename=$2
+ hosts_file=$3
+else
+ echo "$0 nr_hodes basename hostfile"
+ exit
+fi
+
+for n in `seq 2 $NR_NODES`; do
+ if [ $n == 2 ]; then
+ nrsw=50
+ else
+ nrsw=25
+ fi
+ cat template/onsdemo_edge_template.py | sed "s/__NWID__/$n/g" | sed "s/__NRSW__/${nrsw}/g" > ${basename}${n}/onsdemo.py
+done
+cp template/onsdemo_core.py ${basename}1/onsdemo.py
+
+cat hosts | awk '{printf("%s=%s\n",$2,$1)}' > .tmp
+for n in `seq 2 $NR_NODES`; do
+ cat template/tunnel_onsdemo_edge_template.sh | awk '{if(NR==2){system("cat .tmp")}else{print $0}}' |\
+ sed "s/__NWID__/$n/g" |\
+ sed "s/__TUNNEL__/TUNNEL\=\(\"1 $n ${basename}1\"\)/g" > ${basename}${n}/tunnel_onsdemo.sh
+ chmod 755 ${basename}${n}/tunnel_onsdemo.sh
+done
+
+cat template/tunnel_onsdemo_core_template.sh | awk '{if(NR==2){system("cat .tmp")}else{print $0}}' |\
+ sed "s/__basename__/$basename/g" > ${basename}1/tunnel_onsdemo.sh
+ chmod 755 ${basename}1/tunnel_onsdemo.sh
diff --git a/cluster-mgmt/ssh/authorized_keys b/cluster-mgmt/ssh/authorized_keys
new file mode 100644
index 0000000..f723bc2
--- /dev/null
+++ b/cluster-mgmt/ssh/authorized_keys
@@ -0,0 +1,2 @@
+ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCTlBTIOKm30b7TsCgIT+xjq42q0zwG+EohOGkCtNr1eGkS9OZDYwkNAkPtpzYtZJ914oRL29JiXFm+OsAfwVKsY2yZlV+tcnTx4Djfhgs6/wURMhw3sOovWu2iAoPAhQYvvvq8maD8ZvybYTzq4yHNP27G7rv4s+GCtv3bXOgzsKd8Zkg0+tGZYuCks5mNimlfWGBlA5jI9MEkd0nWTqSTRj8IkfhJo26HralR+X/KwHGryfxjG9rsyqoZGnVC/xV4KOOtZlVRzTVxCDFPj86lO4dzf7Tt+dst/t/9u/V2d7YxnuhaM+Sarve+6f/tZoekWzpNRGGT9h7FzT7Osg+l onlab-gui
+ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDEHmYMw6DugE6FCoLR5fdTO7iQfouHmLm60yxSjXu/wnBGmM7SGc1AAgmtr6JaEPYj8H6g7AL8+wFrbj7TXOoMD4HWoEzC/PuTZ5JgyCeTK/rmYdBlbAqBbLeD1d9q35O+GnWOsLIsSQHcKvKZveLLPTBtzJ6em9NfgiPKibbsAFD716w++cxXKHabzHw7KB9XaewoYdznrosWwU3TXR4C2rzMAimh6XuBLZ0xFTNF4nFhy+H0AWUEN8dY8NHwAMGlAWK4g7phZ2cQhgU4GeutfGlEKlKT3iT7j8rkW1JKsx/AOVfcnozuHCm76jYD5qXcizHeS4BYinXRepGY7mfn onlabkey
diff --git a/cluster-mgmt/ssh/id_rsa.pub b/cluster-mgmt/ssh/id_rsa.pub
new file mode 100644
index 0000000..bcb2d75
--- /dev/null
+++ b/cluster-mgmt/ssh/id_rsa.pub
@@ -0,0 +1 @@
+ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC3QgAX4yEcOHaKFgeq/tD2lbGg5VbNvRka1atUSd5q8hhtw5rB8um5Q5Z6+AfL83+Xlez2KonH6JLjhhs8wBHaJCVbzvDnycMEEHg12o+MvlKgKTkkSqP9W+Jejk4YGIr6QOQ/yzZRhRGoNGMaqI6KU7NjtgZyZs8h66GTyoBeXi9TZwGYdxeF5rVqZD80nlb+xlc+PUC4TQ/o2RnGej7S0J/+ES+/X6LiNgHyZPdFK2Pr4BilLwS8c5EyAHHQuW8hIcPhNwXgrx97f5L8yuNKAmW9WSYLk0r4DhnFUZrvIGqh3isxtnJDDf3UZ2U+PtGZ75ZNfk546obsuyc/IwHH ubuntu@onos9vpc
diff --git a/cluster-mgmt/start-mininet.sh b/cluster-mgmt/start-mininet.sh
new file mode 100755
index 0000000..46a0239
--- /dev/null
+++ b/cluster-mgmt/start-mininet.sh
@@ -0,0 +1,21 @@
+#! /bin/bash
+USERNAME=ubuntu
+CASSANDRA_DIR='/home/ubuntu/apache-cassandra-1.1.4'
+ZK_DIR='/home/ubuntu/zookeeper-3.4.5'
+ZK_LIB='/var/lib/zookeeper'
+CASSANDRA_LIB='/var/lib/cassandra'
+
+if [ $# == 1 ]; then
+ basename=$1
+else
+ echo "$0 basename"
+ exit
+fi
+
+export CLUSTER="./cluster.txt"
+dsh -g $basename 'uname -a'
+
+#dsh -g ${basename} 'cd ONOS/test-network/mininet; ./tunnel_onsdemo.sh start'
+#dsh -g ${basename} 'cd ONOS/test-network/mininet; ./tunnel_onsdemo.sh start'
+dsh -g ${basename} 'cd ONOS/test-network/mininet; sudo mn -c'
+dsh -g ${basename} 'cd ONOS/test-network/mininet; sudo ./onsdemo.py -n'
diff --git a/cluster-mgmt/template/cassandra.yaml b/cluster-mgmt/template/cassandra.yaml
new file mode 100644
index 0000000..ab79cdc
--- /dev/null
+++ b/cluster-mgmt/template/cassandra.yaml
@@ -0,0 +1,568 @@
+# Cassandra storage config YAML
+
+# NOTE:
+# See http://wiki.apache.org/cassandra/StorageConfiguration for
+# full explanations of configuration directives
+# /NOTE
+
+# The name of the cluster. This is mainly used to prevent machines in
+# one logical cluster from joining another.
+cluster_name: 'ONOS Test Cluster'
+
+# You should always specify InitialToken when setting up a production
+# cluster for the first time, and often when adding capacity later.
+# The principle is that each node should be given an equal slice of
+# the token ring; see http://wiki.apache.org/cassandra/Operations
+# for more details.
+#
+# If blank, Cassandra will request a token bisecting the range of
+# the heaviest-loaded existing node. If there is no load information
+# available, such as is the case with a new cluster, it will pick
+# a random token, which will lead to hot spots.
+initial_token:
+
+# See http://wiki.apache.org/cassandra/HintedHandoff
+hinted_handoff_enabled: true
+# this defines the maximum amount of time a dead host will have hints
+# generated. After it has been dead this long, hints will be dropped.
+max_hint_window_in_ms: 3600000 # one hour
+# Sleep this long after delivering each hint
+hinted_handoff_throttle_delay_in_ms: 1
+
+# The following setting populates the page cache on memtable flush and compaction
+# WARNING: Enable this setting only when the whole node's data fits in memory.
+# Defaults to: false
+# populate_io_cache_on_flush: false
+
+# authentication backend, implementing IAuthenticator; used to identify users
+authenticator: org.apache.cassandra.auth.AllowAllAuthenticator
+
+# authorization backend, implementing IAuthority; used to limit access/provide permissions
+authority: org.apache.cassandra.auth.AllowAllAuthority
+
+# The partitioner is responsible for distributing rows (by key) across
+# nodes in the cluster. Any IPartitioner may be used, including your
+# own as long as it is on the classpath. Out of the box, Cassandra
+# provides org.apache.cassandra.dht.RandomPartitioner
+# org.apache.cassandra.dht.ByteOrderedPartitioner,
+# org.apache.cassandra.dht.OrderPreservingPartitioner (deprecated),
+# and org.apache.cassandra.dht.CollatingOrderPreservingPartitioner
+# (deprecated).
+#
+# - RandomPartitioner distributes rows across the cluster evenly by md5.
+# When in doubt, this is the best option.
+# - ByteOrderedPartitioner orders rows lexically by key bytes. BOP allows
+# scanning rows in key order, but the ordering can generate hot spots
+# for sequential insertion workloads.
+# - OrderPreservingPartitioner is an obsolete form of BOP, that stores
+# - keys in a less-efficient format and only works with keys that are
+# UTF8-encoded Strings.
+# - CollatingOPP colates according to EN,US rules rather than lexical byte
+# ordering. Use this as an example if you need custom collation.
+#
+# See http://wiki.apache.org/cassandra/Operations for more on
+# partitioners and token selection.
+partitioner: org.apache.cassandra.dht.RandomPartitioner
+
+# directories where Cassandra should store data on disk.
+data_file_directories:
+ - /var/lib/cassandra/data
+
+# commit log
+commitlog_directory: /var/lib/cassandra/commitlog
+
+# Maximum size of the key cache in memory.
+#
+# Each key cache hit saves 1 seek and each row cache hit saves 2 seeks at the
+# minimum, sometimes more. The key cache is fairly tiny for the amount of
+# time it saves, so it's worthwhile to use it at large numbers.
+# The row cache saves even more time, but must store the whole values of
+# its rows, so it is extremely space-intensive. It's best to only use the
+# row cache if you have hot rows or static rows.
+#
+# NOTE: if you reduce the size, you may not get you hottest keys loaded on startup.
+#
+# Default value is empty to make it "auto" (min(5% of Heap (in MB), 100MB)). Set to 0 to disable key cache.
+key_cache_size_in_mb:
+
+# Duration in seconds after which Cassandra should
+# safe the keys cache. Caches are saved to saved_caches_directory as
+# specified in this configuration file.
+#
+# Saved caches greatly improve cold-start speeds, and is relatively cheap in
+# terms of I/O for the key cache. Row cache saving is much more expensive and
+# has limited use.
+#
+# Default is 14400 or 4 hours.
+key_cache_save_period: 14400
+
+# Number of keys from the key cache to save
+# Disabled by default, meaning all keys are going to be saved
+# key_cache_keys_to_save: 100
+
+# Maximum size of the row cache in memory.
+# NOTE: if you reduce the size, you may not get you hottest keys loaded on startup.
+#
+# Default value is 0, to disable row caching.
+row_cache_size_in_mb: 0
+
+# Duration in seconds after which Cassandra should
+# safe the row cache. Caches are saved to saved_caches_directory as specified
+# in this configuration file.
+#
+# Saved caches greatly improve cold-start speeds, and is relatively cheap in
+# terms of I/O for the key cache. Row cache saving is much more expensive and
+# has limited use.
+#
+# Default is 0 to disable saving the row cache.
+row_cache_save_period: 0
+
+# Number of keys from the row cache to save
+# Disabled by default, meaning all keys are going to be saved
+# row_cache_keys_to_save: 100
+
+# The provider for the row cache to use.
+#
+# Supported values are: ConcurrentLinkedHashCacheProvider, SerializingCacheProvider
+#
+# SerializingCacheProvider serialises the contents of the row and stores
+# it in native memory, i.e., off the JVM Heap. Serialized rows take
+# significantly less memory than "live" rows in the JVM, so you can cache
+# more rows in a given memory footprint. And storing the cache off-heap
+# means you can use smaller heap sizes, reducing the impact of GC pauses.
+#
+# It is also valid to specify the fully-qualified class name to a class
+# that implements org.apache.cassandra.cache.IRowCacheProvider.
+#
+# Defaults to SerializingCacheProvider
+row_cache_provider: SerializingCacheProvider
+
+# saved caches
+saved_caches_directory: /var/lib/cassandra/saved_caches
+
+# commitlog_sync may be either "periodic" or "batch."
+# When in batch mode, Cassandra won't ack writes until the commit log
+# has been fsynced to disk. It will wait up to
+# commitlog_sync_batch_window_in_ms milliseconds for other writes, before
+# performing the sync.
+#
+# commitlog_sync: batch
+# commitlog_sync_batch_window_in_ms: 50
+#
+# the other option is "periodic" where writes may be acked immediately
+# and the CommitLog is simply synced every commitlog_sync_period_in_ms
+# milliseconds.
+commitlog_sync: periodic
+commitlog_sync_period_in_ms: 10000
+
+# The size of the individual commitlog file segments. A commitlog
+# segment may be archived, deleted, or recycled once all the data
+# in it (potentally from each columnfamily in the system) has been
+# flushed to sstables.
+#
+# The default size is 32, which is almost always fine, but if you are
+# archiving commitlog segments (see commitlog_archiving.properties),
+# then you probably want a finer granularity of archiving; 8 or 16 MB
+# is reasonable.
+commitlog_segment_size_in_mb: 32
+
+# any class that implements the SeedProvider interface and has a
+# constructor that takes a Map<String, String> of parameters will do.
+seed_provider:
+ # Addresses of hosts that are deemed contact points.
+ # Cassandra nodes use this list of hosts to find each other and learn
+ # the topology of the ring. You must change this if you are running
+ # multiple nodes!
+ - class_name: org.apache.cassandra.locator.SimpleSeedProvider
+ parameters:
+ # seeds is actually a comma-delimited list of addresses.
+ # Ex: "<ip1>,<ip2>,<ip3>"
+# - seeds: "10.0.1.243"
+ - seeds: "__SEED__"
+
+# emergency pressure valve: each time heap usage after a full (CMS)
+# garbage collection is above this fraction of the max, Cassandra will
+# flush the largest memtables.
+#
+# Set to 1.0 to disable. Setting this lower than
+# CMSInitiatingOccupancyFraction is not likely to be useful.
+#
+# RELYING ON THIS AS YOUR PRIMARY TUNING MECHANISM WILL WORK POORLY:
+# it is most effective under light to moderate load, or read-heavy
+# workloads; under truly massive write load, it will often be too
+# little, too late.
+flush_largest_memtables_at: 0.75
+
+# emergency pressure valve #2: the first time heap usage after a full
+# (CMS) garbage collection is above this fraction of the max,
+# Cassandra will reduce cache maximum _capacity_ to the given fraction
+# of the current _size_. Should usually be set substantially above
+# flush_largest_memtables_at, since that will have less long-term
+# impact on the system.
+#
+# Set to 1.0 to disable. Setting this lower than
+# CMSInitiatingOccupancyFraction is not likely to be useful.
+reduce_cache_sizes_at: 0.85
+reduce_cache_capacity_to: 0.6
+
+# For workloads with more data than can fit in memory, Cassandra's
+# bottleneck will be reads that need to fetch data from
+# disk. "concurrent_reads" should be set to (16 * number_of_drives) in
+# order to allow the operations to enqueue low enough in the stack
+# that the OS and drives can reorder them.
+#
+# On the other hand, since writes are almost never IO bound, the ideal
+# number of "concurrent_writes" is dependent on the number of cores in
+# your system; (8 * number_of_cores) is a good rule of thumb.
+concurrent_reads: 16
+concurrent_writes: 16
+
+# Total memory to use for memtables. Cassandra will flush the largest
+# memtable when this much memory is used.
+# If omitted, Cassandra will set it to 1/3 of the heap.
+# memtable_total_space_in_mb: 2048
+
+# Total space to use for commitlogs. Since commitlog segments are
+# mmapped, and hence use up address space, the default size is 32
+# on 32-bit JVMs, and 1024 on 64-bit JVMs.
+#
+# If space gets above this value (it will round up to the next nearest
+# segment multiple), Cassandra will flush every dirty CF in the oldest
+# segment and remove it. So a small total commitlog space will tend
+# to cause more flush activity on less-active columnfamilies.
+# commitlog_total_space_in_mb: 4096
+
+# This sets the amount of memtable flush writer threads. These will
+# be blocked by disk io, and each one will hold a memtable in memory
+# while blocked. If you have a large heap and many data directories,
+# you can increase this value for better flush performance.
+# By default this will be set to the amount of data directories defined.
+#memtable_flush_writers: 1
+
+# the number of full memtables to allow pending flush, that is,
+# waiting for a writer thread. At a minimum, this should be set to
+# the maximum number of secondary indexes created on a single CF.
+memtable_flush_queue_size: 4
+
+# Whether to, when doing sequential writing, fsync() at intervals in
+# order to force the operating system to flush the dirty
+# buffers. Enable this to avoid sudden dirty buffer flushing from
+# impacting read latencies. Almost always a good idea on SSD:s; not
+# necessarily on platters.
+trickle_fsync: false
+trickle_fsync_interval_in_kb: 10240
+
+# TCP port, for commands and data
+storage_port: 7000
+
+# SSL port, for encrypted communication. Unused unless enabled in
+# encryption_options
+ssl_storage_port: 7001
+
+# Address to bind to and tell other Cassandra nodes to connect to. You
+# _must_ change this if you want multiple nodes to be able to
+# communicate!
+#
+# Leaving it blank leaves it up to InetAddress.getLocalHost(). This
+# will always do the Right Thing *if* the node is properly configured
+# (hostname, name resolution, etc), and the Right Thing is to use the
+# address associated with the hostname (it might not be).
+#
+# Setting this to 0.0.0.0 is always wrong.
+listen_address:
+
+# Address to broadcast to other Cassandra nodes
+# Leaving this blank will set it to the same value as listen_address
+# broadcast_address: 1.2.3.4
+
+# The address to bind the Thrift RPC service to -- clients connect
+# here. Unlike ListenAddress above, you *can* specify 0.0.0.0 here if
+# you want Thrift to listen on all interfaces.
+#
+# Leaving this blank has the same effect it does for ListenAddress,
+# (i.e. it will be based on the configured hostname of the node).
+rpc_address: 0.0.0.0
+# port for Thrift to listen for clients on
+rpc_port: 9160
+
+# enable or disable keepalive on rpc connections
+rpc_keepalive: true
+
+# Cassandra provides three options for the RPC Server:
+#
+# sync -> One connection per thread in the rpc pool (see below).
+# For a very large number of clients, memory will be your limiting
+# factor; on a 64 bit JVM, 128KB is the minimum stack size per thread.
+# Connection pooling is very, very strongly recommended.
+#
+# async -> Nonblocking server implementation with one thread to serve
+# rpc connections. This is not recommended for high throughput use
+# cases. Async has been tested to be about 50% slower than sync
+# or hsha and is deprecated: it will be removed in the next major release.
+#
+# hsha -> Stands for "half synchronous, half asynchronous." The rpc thread pool
+# (see below) is used to manage requests, but the threads are multiplexed
+# across the different clients.
+#
+# The default is sync because on Windows hsha is about 30% slower. On Linux,
+# sync/hsha performance is about the same, with hsha of course using less memory.
+rpc_server_type: sync
+
+# Uncomment rpc_min|max|thread to set request pool size.
+# You would primarily set max for the sync server to safeguard against
+# misbehaved clients; if you do hit the max, Cassandra will block until one
+# disconnects before accepting more. The defaults for sync are min of 16 and max
+# unlimited.
+#
+# For the Hsha server, the min and max both default to quadruple the number of
+# CPU cores.
+#
+# This configuration is ignored by the async server.
+#
+# rpc_min_threads: 16
+# rpc_max_threads: 2048
+
+# uncomment to set socket buffer sizes on rpc connections
+# rpc_send_buff_size_in_bytes:
+# rpc_recv_buff_size_in_bytes:
+
+# Frame size for thrift (maximum field length).
+# 0 disables TFramedTransport in favor of TSocket. This option
+# is deprecated; we strongly recommend using Framed mode.
+thrift_framed_transport_size_in_mb: 15
+
+# The max length of a thrift message, including all fields and
+# internal thrift overhead.
+thrift_max_message_length_in_mb: 16
+
+# Set to true to have Cassandra create a hard link to each sstable
+# flushed or streamed locally in a backups/ subdirectory of the
+# Keyspace data. Removing these links is the operator's
+# responsibility.
+incremental_backups: false
+
+# Whether or not to take a snapshot before each compaction. Be
+# careful using this option, since Cassandra won't clean up the
+# snapshots for you. Mostly useful if you're paranoid when there
+# is a data format change.
+snapshot_before_compaction: false
+
+# Whether or not a snapshot is taken of the data before keyspace truncation
+# or dropping of column families. The STRONGLY advised default of true
+# should be used to provide data safety. If you set this flag to false, you will
+# lose data on truncation or drop.
+auto_snapshot: true
+
+# Add column indexes to a row after its contents reach this size.
+# Increase if your column values are large, or if you have a very large
+# number of columns. The competing causes are, Cassandra has to
+# deserialize this much of the row to read a single column, so you want
+# it to be small - at least if you do many partial-row reads - but all
+# the index data is read for each access, so you don't want to generate
+# that wastefully either.
+column_index_size_in_kb: 64
+
+# Size limit for rows being compacted in memory. Larger rows will spill
+# over to disk and use a slower two-pass compaction process. A message
+# will be logged specifying the row key.
+in_memory_compaction_limit_in_mb: 64
+
+# Number of simultaneous compactions to allow, NOT including
+# validation "compactions" for anti-entropy repair. Simultaneous
+# compactions can help preserve read performance in a mixed read/write
+# workload, by mitigating the tendency of small sstables to accumulate
+# during a single long running compactions. The default is usually
+# fine and if you experience problems with compaction running too
+# slowly or too fast, you should look at
+# compaction_throughput_mb_per_sec first.
+#
+# This setting has no effect on LeveledCompactionStrategy.
+#
+# concurrent_compactors defaults to the number of cores.
+# Uncomment to make compaction mono-threaded, the pre-0.8 default.
+#concurrent_compactors: 1
+
+# Multi-threaded compaction. When enabled, each compaction will use
+# up to one thread per core, plus one thread per sstable being merged.
+# This is usually only useful for SSD-based hardware: otherwise,
+# your concern is usually to get compaction to do LESS i/o (see:
+# compaction_throughput_mb_per_sec), not more.
+multithreaded_compaction: false
+
+# Throttles compaction to the given total throughput across the entire
+# system. The faster you insert data, the faster you need to compact in
+# order to keep the sstable count down, but in general, setting this to
+# 16 to 32 times the rate you are inserting data is more than sufficient.
+# Setting this to 0 disables throttling. Note that this account for all types
+# of compaction, including validation compaction.
+compaction_throughput_mb_per_sec: 16
+
+# Track cached row keys during compaction, and re-cache their new
+# positions in the compacted sstable. Disable if you use really large
+# key caches.
+compaction_preheat_key_cache: true
+
+# Throttles all outbound streaming file transfers on this node to the
+# given total throughput in Mbps. This is necessary because Cassandra does
+# mostly sequential IO when streaming data during bootstrap or repair, which
+# can lead to saturating the network connection and degrading rpc performance.
+# When unset, the default is 400 Mbps or 50 MB/s.
+# stream_throughput_outbound_megabits_per_sec: 400
+
+# Time to wait for a reply from other nodes before failing the command
+rpc_timeout_in_ms: 10000
+
+# Enable socket timeout for streaming operation.
+# When a timeout occurs during streaming, streaming is retried from the start
+# of the current file. This *can* involve re-streaming an important amount of
+# data, so you should avoid setting the value too low.
+# Default value is 0, which never timeout streams.
+# streaming_socket_timeout_in_ms: 0
+
+# phi value that must be reached for a host to be marked down.
+# most users should never need to adjust this.
+# phi_convict_threshold: 8
+
+# endpoint_snitch -- Set this to a class that implements
+# IEndpointSnitch. The snitch has two functions:
+# - it teaches Cassandra enough about your network topology to route
+# requests efficiently
+# - it allows Cassandra to spread replicas around your cluster to avoid
+# correlated failures. It does this by grouping machines into
+# "datacenters" and "racks." Cassandra will do its best not to have
+# more than one replica on the same "rack" (which may not actually
+# be a physical location)
+#
+# IF YOU CHANGE THE SNITCH AFTER DATA IS INSERTED INTO THE CLUSTER,
+# YOU MUST RUN A FULL REPAIR, SINCE THE SNITCH AFFECTS WHERE REPLICAS
+# ARE PLACED.
+#
+# Out of the box, Cassandra provides
+# - SimpleSnitch:
+# Treats Strategy order as proximity. This improves cache locality
+# when disabling read repair, which can further improve throughput.
+# Only appropriate for single-datacenter deployments.
+# - PropertyFileSnitch:
+# Proximity is determined by rack and data center, which are
+# explicitly configured in cassandra-topology.properties.
+# - GossipingPropertyFileSnitch
+# The rack and datacenter for the local node are defined in
+# cassandra-rackdc.properties and propagated to other nodes via gossip. If
+# cassandra-topology.properties exists, it is used as a fallback, allowing
+# migration from the PropertyFileSnitch.
+# - RackInferringSnitch:
+# Proximity is determined by rack and data center, which are
+# assumed to correspond to the 3rd and 2nd octet of each node's
+# IP address, respectively. Unless this happens to match your
+# deployment conventions (as it did Facebook's), this is best used
+# as an example of writing a custom Snitch class.
+# - Ec2Snitch:
+# Appropriate for EC2 deployments in a single Region. Loads Region
+# and Availability Zone information from the EC2 API. The Region is
+# treated as the Datacenter, and the Availability Zone as the rack.
+# Only private IPs are used, so this will not work across multiple
+# Regions.
+# - Ec2MultiRegionSnitch:
+# Uses public IPs as broadcast_address to allow cross-region
+# connectivity. (Thus, you should set seed addresses to the public
+# IP as well.) You will need to open the storage_port or
+# ssl_storage_port on the public IP firewall. (For intra-Region
+# traffic, Cassandra will switch to the private IP after
+# establishing a connection.)
+#
+# You can use a custom Snitch by setting this to the full class name
+# of the snitch, which will be assumed to be on your classpath.
+endpoint_snitch: SimpleSnitch
+
+# controls how often to perform the more expensive part of host score
+# calculation
+dynamic_snitch_update_interval_in_ms: 100
+# controls how often to reset all host scores, allowing a bad host to
+# possibly recover
+dynamic_snitch_reset_interval_in_ms: 600000
+# if set greater than zero and read_repair_chance is < 1.0, this will allow
+# 'pinning' of replicas to hosts in order to increase cache capacity.
+# The badness threshold will control how much worse the pinned host has to be
+# before the dynamic snitch will prefer other replicas over it. This is
+# expressed as a double which represents a percentage. Thus, a value of
+# 0.2 means Cassandra would continue to prefer the static snitch values
+# until the pinned host was 20% worse than the fastest.
+dynamic_snitch_badness_threshold: 0.1
+
+# request_scheduler -- Set this to a class that implements
+# RequestScheduler, which will schedule incoming client requests
+# according to the specific policy. This is useful for multi-tenancy
+# with a single Cassandra cluster.
+# NOTE: This is specifically for requests from the client and does
+# not affect inter node communication.
+# org.apache.cassandra.scheduler.NoScheduler - No scheduling takes place
+# org.apache.cassandra.scheduler.RoundRobinScheduler - Round robin of
+# client requests to a node with a separate queue for each
+# request_scheduler_id. The scheduler is further customized by
+# request_scheduler_options as described below.
+request_scheduler: org.apache.cassandra.scheduler.NoScheduler
+
+# Scheduler Options vary based on the type of scheduler
+# NoScheduler - Has no options
+# RoundRobin
+# - throttle_limit -- The throttle_limit is the number of in-flight
+# requests per client. Requests beyond
+# that limit are queued up until
+# running requests can complete.
+# The value of 80 here is twice the number of
+# concurrent_reads + concurrent_writes.
+# - default_weight -- default_weight is optional and allows for
+# overriding the default which is 1.
+# - weights -- Weights are optional and will default to 1 or the
+# overridden default_weight. The weight translates into how
+# many requests are handled during each turn of the
+# RoundRobin, based on the scheduler id.
+#
+# request_scheduler_options:
+# throttle_limit: 80
+# default_weight: 5
+# weights:
+# Keyspace1: 1
+# Keyspace2: 5
+
+# request_scheduler_id -- An identifer based on which to perform
+# the request scheduling. Currently the only valid option is keyspace.
+# request_scheduler_id: keyspace
+
+# index_interval controls the sampling of entries from the primrary
+# row index in terms of space versus time. The larger the interval,
+# the smaller and less effective the sampling will be. In technicial
+# terms, the interval coresponds to the number of index entries that
+# are skipped between taking each sample. All the sampled entries
+# must fit in memory. Generally, a value between 128 and 512 here
+# coupled with a large key cache size on CFs results in the best trade
+# offs. This value is not often changed, however if you have many
+# very small rows (many to an OS page), then increasing this will
+# often lower memory usage without a impact on performance.
+index_interval: 128
+
+# Enable or disable inter-node encryption
+# Default settings are TLS v1, RSA 1024-bit keys (it is imperative that
+# users generate their own keys) TLS_RSA_WITH_AES_128_CBC_SHA as the cipher
+# suite for authentication, key exchange and encryption of the actual data transfers.
+# NOTE: No custom encryption options are enabled at the moment
+# The available internode options are : all, none, dc, rack
+#
+# If set to dc cassandra will encrypt the traffic between the DCs
+# If set to rack cassandra will encrypt the traffic between the racks
+#
+# The passwords used in these options must match the passwords used when generating
+# the keystore and truststore. For instructions on generating these files, see:
+# http://download.oracle.com/javase/6/docs/technotes/guides/security/jsse/JSSERefGuide.html#CreateKeystore
+#
+encryption_options:
+ internode_encryption: none
+ keystore: conf/.keystore
+ keystore_password: cassandra
+ truststore: conf/.truststore
+ truststore_password: cassandra
+ # More advanced defaults below:
+ # protocol: TLS
+ # algorithm: SunX509
+ # store_type: JKS
+ # cipher_suites: [TLS_RSA_WITH_AES_128_CBC_SHA,TLS_RSA_WITH_AES_256_CBC_SHA]
diff --git a/cluster-mgmt/template/hosts b/cluster-mgmt/template/hosts
new file mode 100644
index 0000000..220b30f
--- /dev/null
+++ b/cluster-mgmt/template/hosts
@@ -0,0 +1,11 @@
+127.0.0.1 localhost
+
+# The following lines are desirable for IPv6 capable hosts
+::1 ip6-localhost ip6-loopback
+fe00::0 ip6-localnet
+ff00::0 ip6-mcastprefix
+ff02::1 ip6-allnodes
+ff02::2 ip6-allrouters
+ff02::3 ip6-allhosts
+
+## For ONOS Development
diff --git a/cluster-mgmt/template/onsdemo_core.py b/cluster-mgmt/template/onsdemo_core.py
new file mode 100755
index 0000000..e62ae77
--- /dev/null
+++ b/cluster-mgmt/template/onsdemo_core.py
@@ -0,0 +1,191 @@
+#!/usr/bin/python
+
+"""
+Start up a Simple topology
+"""
+from mininet.net import Mininet
+from mininet.node import Controller, RemoteController
+from mininet.log import setLogLevel, info, error, warn, debug
+from mininet.cli import CLI
+from mininet.topo import Topo
+from mininet.util import quietRun
+from mininet.moduledeps import pathCheck
+from mininet.link import Link, TCLink
+
+from sys import exit
+import os.path
+from subprocess import Popen, STDOUT, PIPE
+
+import sys
+
+
+#import argparse
+
+class MyController( Controller ):
+ def __init__( self, name, ip='127.0.0.1', port=6633, **kwargs):
+ """Init.
+ name: name to give controller
+ ip: the IP address where the remote controller is
+ listening
+ port: the port where the remote controller is listening"""
+ Controller.__init__( self, name, ip=ip, port=port, **kwargs )
+
+ def start( self ):
+ "Overridden to do nothing."
+ return
+
+ def stop( self ):
+ "Overridden to do nothing."
+ return
+
+ def checkListening( self ):
+ "Warn if remote controller is not accessible"
+ listening = self.cmd( "echo A | telnet -e A %s %d" %
+ ( self.ip, self.port ) )
+ if 'Unable' in listening:
+ warn( "Unable to contact the remote controller"
+ " at %s:%d\n" % ( self.ip, self.port ) )
+
+class SDNTopo( Topo ):
+ "SDN Topology"
+
+ def __init__( self, *args, **kwargs ):
+ Topo.__init__( self, *args, **kwargs )
+ sw1 = self.addSwitch('sw1', dpid='00:00:00:16:97:08:9a:46')
+ sw2 = self.addSwitch('sw2', dpid='00:00:00:00:ba:5e:ba:11')
+ sw3 = self.addSwitch('sw3', dpid='00:00:00:08:a2:08:f9:01')
+ sw4 = self.addSwitch('sw4', dpid='00:00:00:00:00:00:ba:12')
+ sw5 = self.addSwitch('sw5', dpid='00:00:00:00:ba:5e:ba:13')
+ sw6 = self.addSwitch('sw6', dpid='00:00:20:4e:7f:51:8a:35')
+
+ host1 = self.addHost( 'host1' )
+ host2 = self.addHost( 'host2' )
+ host3 = self.addHost( 'host3' )
+ host4 = self.addHost( 'host4' )
+ host5 = self.addHost( 'host5' )
+ host6 = self.addHost( 'host6' )
+
+ root1 = self.addHost( 'root1', inNamespace=False )
+ root2 = self.addHost( 'root2', inNamespace=False )
+ root3 = self.addHost( 'root3', inNamespace=False )
+ root4 = self.addHost( 'root4', inNamespace=False )
+ root5 = self.addHost( 'root5', inNamespace=False )
+ root6 = self.addHost( 'root6', inNamespace=False )
+
+
+ self.addLink( host1, sw1 )
+ self.addLink( host2, sw2 )
+ self.addLink( host3, sw3 )
+ self.addLink( host4, sw4 )
+ self.addLink( host5, sw5 )
+ self.addLink( host6, sw6 )
+
+
+ self.addLink( sw1, sw2 )
+ self.addLink( sw1, sw6 )
+ self.addLink( sw2, sw3 )
+ self.addLink( sw3, sw4 )
+ self.addLink( sw3, sw6 )
+ self.addLink( sw4, sw5 )
+ self.addLink( sw5, sw6 )
+
+ self.addLink( root1, host1 )
+ self.addLink( root2, host2 )
+ self.addLink( root3, host3 )
+ self.addLink( root4, host4 )
+ self.addLink( root5, host5 )
+ self.addLink( root6, host6 )
+
+def startsshd( host ):
+ "Start sshd on host"
+ info( '*** Starting sshd\n' )
+ name, intf, ip = host.name, host.defaultIntf(), host.IP()
+ banner = '/tmp/%s.banner' % name
+ host.cmd( 'echo "Welcome to %s at %s" > %s' % ( name, ip, banner ) )
+ host.cmd( '/usr/sbin/sshd -o "Banner %s"' % banner, '-o "UseDNS no"' )
+ info( '***', host.name, 'is running sshd on', intf, 'at', ip, '\n' )
+
+def startsshds ( hosts ):
+ for h in hosts:
+ startsshd( h )
+
+def stopsshd( ):
+ "Stop *all* sshd processes with a custom banner"
+ info( '*** Shutting down stale sshd/Banner processes ',
+ quietRun( "pkill -9 -f Banner" ), '\n' )
+
+def sdnnet(opt):
+# os.system('/home/ubuntu/openflow/controller/controller ptcp: &')
+# os.system('/home/ubuntu/openflow/controller/controller ptcp:7000 &')
+
+ topo = SDNTopo()
+ info( '*** Creating network\n' )
+# net = Mininet( topo=topo, controller=RemoteController )
+ net = Mininet( topo=topo, controller=MyController, link=TCLink)
+# dc = DebugController('c3', ip='127.0.0.1', port=7000)
+# net.addController(dc)
+# net.addController(controller=RemoteController)
+
+ host1, host2, host3, host4, host5, host6 = net.get( 'host1', 'host2', 'host3', 'host4', 'host5', 'host6')
+
+ ## Adding 2nd, 3rd and 4th interface to host1 connected to sw1 (for another BGP peering)
+ sw1 = net.get('sw1')
+ sw2 = net.get('sw2')
+ sw3 = net.get('sw3')
+ sw4 = net.get('sw4')
+ sw5 = net.get('sw5')
+ sw6 = net.get('sw6')
+
+ net.start()
+
+ sw2.attach('tap01_2')
+ sw3.attach('tap01_3')
+ sw4.attach('tap01_4')
+ sw4.attach('tap01_5')
+ sw5.attach('tap01_6')
+ sw6.attach('tap01_7')
+ sw1.attach('tap01_8')
+
+ host1.defaultIntf().setIP('192.168.100.141/16')
+ host2.defaultIntf().setIP('192.168.100.142/16')
+ host3.defaultIntf().setIP('192.168.100.143/16')
+ host4.defaultIntf().setIP('192.168.100.144/16')
+ host5.defaultIntf().setIP('192.168.100.145/16')
+ host6.defaultIntf().setIP('192.168.100.146/16')
+
+ root1, root2, root3, root4, root5, root6 = net.get( 'root1', 'root2', 'root3', 'root4', 'root5', 'root6' )
+ host1.intf('host1-eth1').setIP('1.1.1.1/24')
+ root1.intf('root1-eth0').setIP('1.1.1.2/24')
+
+ host2.intf('host2-eth1') .setIP('1.1.2.1/24')
+ root2.intf('root2-eth0').setIP('1.1.2.2/24')
+
+ host3.intf('host3-eth1') .setIP('1.1.3.1/24')
+ root3.intf('root3-eth0').setIP('1.1.3.2/24')
+
+ host4.intf('host4-eth1') .setIP('1.1.4.1/24')
+ root4.intf('root4-eth0').setIP('1.1.4.2/24')
+
+ host5.intf('host5-eth1') .setIP('1.1.5.1/24')
+ root5.intf('root5-eth0').setIP('1.1.5.2/24')
+
+ host6.intf('host6-eth1') .setIP('1.1.6.1/24')
+ root6.intf('root6-eth0').setIP('1.1.6.2/24')
+
+ hosts = [ host1, host2, host3, host4, host5, host6 ]
+ stopsshd ()
+ startsshds ( hosts )
+
+ if opt=="cli":
+ CLI(net)
+ stopsshd()
+ net.stop()
+
+if __name__ == '__main__':
+ setLogLevel( 'info' )
+ if len(sys.argv) == 1:
+ sdnnet("cli")
+ elif len(sys.argv) == 2 and sys.argv[1] == "-n":
+ sdnnet("nocli")
+ else:
+ print "%s [-n]" % sys.argv[0]
diff --git a/cluster-mgmt/template/onsdemo_edge_template.py b/cluster-mgmt/template/onsdemo_edge_template.py
new file mode 100755
index 0000000..a1ac11c
--- /dev/null
+++ b/cluster-mgmt/template/onsdemo_edge_template.py
@@ -0,0 +1,155 @@
+#!/usr/bin/python
+NWID=__NWID__
+NR_NODES=__NRSW__
+Controllers=[{"ip":"127.0.0.1", "port":6633}]
+
+"""
+Start up a Simple topology
+"""
+from mininet.net import Mininet
+from mininet.node import Controller, RemoteController
+from mininet.log import setLogLevel, info, error, warn, debug
+from mininet.cli import CLI
+from mininet.topo import Topo
+from mininet.util import quietRun
+from mininet.moduledeps import pathCheck
+from mininet.link import Link, TCLink
+
+from sys import exit
+import os.path
+from subprocess import Popen, STDOUT, PIPE
+
+import sys
+
+#import argparse
+
+class MyController( Controller ):
+ def __init__( self, name, ip='127.0.0.1', port=6633, **kwargs):
+ """Init.
+ name: name to give controller
+ ip: the IP address where the remote controller is
+ listening
+ port: the port where the remote controller is listening"""
+ Controller.__init__( self, name, ip=ip, port=port, **kwargs )
+
+ def start( self ):
+ "Overridden to do nothing."
+ return
+
+ def stop( self ):
+ "Overridden to do nothing."
+ return
+
+ def checkListening( self ):
+ "Warn if remote controller is not accessible"
+ listening = self.cmd( "echo A | telnet -e A %s %d" %
+ ( self.ip, self.port ) )
+ if 'Unable' in listening:
+ warn( "Unable to contact the remote controller"
+ " at %s:%d\n" % ( self.ip, self.port ) )
+
+class SDNTopo( Topo ):
+ "SDN Topology"
+
+ def __init__( self, *args, **kwargs ):
+ Topo.__init__( self, *args, **kwargs )
+
+ switch = []
+ host = []
+ root = []
+
+ for i in range (NR_NODES):
+ name_suffix = '%02d' % NWID + "." + '%02d' % (int(i)+1)
+ dpid_suffix = '%02x' % NWID + '%02x' % (int(i)+1)
+ dpid = '0000' + '0000' + '0000' + dpid_suffix
+ sw = self.addSwitch('sw'+name_suffix, dpid=dpid)
+ switch.append(sw)
+
+ for i in range (NR_NODES):
+ host.append(self.addHost( 'host%d' % (int(i)+1) ))
+ root.append(self.addHost( 'root%d' % (int(i)+1), inNamespace=False ))
+
+ for i in range (NR_NODES):
+ self.addLink(host[i], switch[i])
+
+ for i in range (1, NR_NODES):
+ self.addLink(switch[0], switch[i])
+
+ for i in range (NR_NODES):
+ self.addLink(root[i], host[i])
+
+def startsshd( host ):
+ "Start sshd on host"
+ info( '*** Starting sshd\n' )
+ name, intf, ip = host.name, host.defaultIntf(), host.IP()
+ banner = '/tmp/%s.banner' % name
+ host.cmd( 'echo "Welcome to %s at %s" > %s' % ( name, ip, banner ) )
+ host.cmd( '/usr/sbin/sshd -o "Banner %s"' % banner, '-o "UseDNS no"' )
+ info( '***', host.name, 'is running sshd on', intf, 'at', ip, '\n' )
+
+def startsshds ( hosts ):
+ for h in hosts:
+ startsshd( h )
+
+def stopsshd( ):
+ "Stop *all* sshd processes with a custom banner"
+ info( '*** Shutting down stale sshd/Banner processes ',
+ quietRun( "pkill -9 -f Banner" ), '\n' )
+
+def sdnnet(opt):
+ topo = SDNTopo()
+ info( '*** Creating network\n' )
+ net = Mininet( topo=topo, controller=MyController, link=TCLink)
+ #net = Mininet( topo=topo, link=TCLink, build=False)
+ #controllers=[]
+ #for c in Controllers:
+ # rc = RemoteController('c%d' % Controllers.index(c), ip=c['ip'],port=c['port'])
+ # print "controller ip %s port %s" % (c['ip'], c['port'])
+ # controllers.append(rc)
+
+ #net.controllers=controllers
+ net.build()
+
+ host = []
+ for i in range (NR_NODES):
+ host.append(net.get( 'host%d' % (int(i)+1) ))
+
+ net.start()
+
+ sw=net.get('sw%02x.%02x' % (NWID,1))
+ print "center sw", sw
+ sw.attach('tap%02x_1' % NWID)
+
+ for i in range (NR_NODES):
+ host[i].defaultIntf().setIP('192.168.%d.%d/16' % (NWID,(int(i)+1)))
+ host[i].defaultIntf().setMAC('00:00:%02x:%02x:%02x:%02x' % (192,168,NWID,(int(i)+1)))
+
+ for i in range (NR_NODES):
+ for n in range (1,8):
+ for h in range (25):
+ host[i].setARP('192.168.%d.%d' % (n, (int(h)+1)), '00:00:%02x:%02x:%02x:%02x' % (192,168,n,(int(h)+1)))
+
+ root = []
+ for i in range (NR_NODES):
+ root.append(net.get( 'root%d' % (int(i)+1) ))
+
+ for i in range (NR_NODES):
+ host[i].intf('host%d-eth1' % (int(i)+1)).setIP('1.1.%d.1/24' % (int(i)+1))
+ root[i].intf('root%d-eth0' % (int(i)+1)).setIP('1.1.%d.2/24' % (int(i)+1))
+
+ stopsshd ()
+ startsshds ( host )
+
+ if opt=="cli":
+ CLI(net)
+ stopsshd()
+ net.stop()
+
+if __name__ == '__main__':
+ setLogLevel( 'info' )
+ if len(sys.argv) == 1:
+ sdnnet("cli")
+ elif len(sys.argv) == 2 and sys.argv[1] == "-n":
+ sdnnet("nocli")
+ else:
+ print "%s [-n]" % sys.argv[0]
diff --git a/cluster-mgmt/template/tunnel_onsdemo_core_template.sh b/cluster-mgmt/template/tunnel_onsdemo_core_template.sh
new file mode 100755
index 0000000..d697c6c
--- /dev/null
+++ b/cluster-mgmt/template/tunnel_onsdemo_core_template.sh
@@ -0,0 +1,51 @@
+#!/bin/bash
+
+TUNNEL=( "2 2 __basename__2" "3 3 __basename__3" "4 4 __basename__4" "5 5 __basename__5" "6 6 __basename__6" "7 7 __basename__7" "8 8 __basename__8")
+NW_ID=01
+
+start () {
+ ## Modify ##
+ ulimit -c
+ for (( i = 0; i< ${#TUNNEL[@]}; i ++)); do
+ t=`echo ${TUNNEL[$i]}`
+ ifnr=`echo $t | awk '{print $1}'`
+ tun_tag=`echo $t | awk '{print $2}'`
+ tun_end_=`echo $t | awk '{print $3}'`
+ tun_end=`eval echo '$'$tun_end_`
+ ifconfig tap${NW_ID}_${ifnr}
+ echo "ifconfig tap${NW_ID}_${ifnr}"
+ if [ $? -ne 0 ]; then
+ echo "creating tap${NW_ID}_${ifnr}"
+ sudo tunctl -t tap${NW_ID}_${ifnr}
+ fi
+ echo "./capsulator -v -d -t eth0 -f ${tun_end} -vb tap${NW_ID}_${ifnr}#${tun_tag}"
+ sudo ifconfig tap${NW_ID}_${ifnr} 0.0.0.0 up > /dev/null 2>&1
+ sudo ./capsulator -v -d -t eth0 -f ${tun_end} -vb tap${NW_ID}_${ifnr}#${tun_tag} > /dev/null 2>&1 &
+ done
+}
+
+stop () {
+ sudo pkill capsulator
+}
+
+case "$1" in
+ start | restart)
+ stop
+ start
+ ;;
+ stop)
+ stop
+ ;;
+ status)
+ nr=`pgrep capsulator | wc -l`
+ if [ $nr -gt 0 ]; then
+ echo "$nr tunnel(s) is running"
+ else
+ echo "tunnel is not running"
+ fi
+ ;;
+ *)
+ echo "Usage: $0 {start|stop|restart|status}" >&2
+ exit 1
+ ;;
+esac
diff --git a/cluster-mgmt/template/tunnel_onsdemo_edge_template.sh b/cluster-mgmt/template/tunnel_onsdemo_edge_template.sh
new file mode 100755
index 0000000..bbd7274
--- /dev/null
+++ b/cluster-mgmt/template/tunnel_onsdemo_edge_template.sh
@@ -0,0 +1,52 @@
+#!/bin/bash
+
+#TUNNEL=("0 1 VIP_ONOS10" "1 2 VIP_ONOS10")
+__TUNNEL__
+NW_ID=0__NWID__
+
+start () {
+ ## Modify ##
+ ulimit -c
+ for (( i = 0; i< ${#TUNNEL[@]}; i ++)); do
+ t=`echo ${TUNNEL[$i]}`
+ ifnr=`echo $t | awk '{print $1}'`
+ tun_tag=`echo $t | awk '{print $2}'`
+ tun_end_=`echo $t | awk '{print $3}'`
+ tun_end=`eval echo '$'$tun_end_`
+ ifconfig tap${NW_ID}_${ifnr}
+ echo "ifconfig tap${NW_ID}_${ifnr}"
+ if [ $? -ne 0 ]; then
+ echo "creating tap${NW_ID}_${ifnr}"
+ sudo tunctl -t tap${NW_ID}_${ifnr}
+ fi
+ echo "./capsulator -v -d -t eth0 -f ${tun_end} -vb tap${NW_ID}_${ifnr}#${tun_tag}"
+ sudo ifconfig tap${NW_ID}_${ifnr} 0.0.0.0 up > /dev/null 2>&1
+ sudo ./capsulator -v -d -t eth0 -f ${tun_end} -vb tap${NW_ID}_${ifnr}#${tun_tag} > /dev/null 2>&1 &
+ done
+}
+
+stop () {
+ sudo pkill capsulator
+}
+
+case "$1" in
+ start | restart)
+ stop
+ start
+ ;;
+ stop)
+ stop
+ ;;
+ status)
+ nr=`pgrep capsulator | wc -l`
+ if [ $nr -gt 0 ]; then
+ echo "$nr tunnel(s) is running"
+ else
+ echo "tunnel is not running"
+ fi
+ ;;
+ *)
+ echo "Usage: $0 {start|stop|restart|status}" >&2
+ exit 1
+ ;;
+esac
diff --git a/cluster-mgmt/template/zoo.cfg b/cluster-mgmt/template/zoo.cfg
new file mode 100644
index 0000000..e1ab8c3
--- /dev/null
+++ b/cluster-mgmt/template/zoo.cfg
@@ -0,0 +1,41 @@
+# The number of milliseconds of each tick
+tickTime=2000
+# The number of ticks that the initial
+# synchronization phase can take
+initLimit=10
+# The number of ticks that can pass between
+# sending a request and getting an acknowledgement
+syncLimit=5
+# the directory where the snapshot is stored.
+# do not use /tmp for storage, /tmp here is just
+# example sakes.
+dataDir=/var/lib/zookeeper
+# the port at which the clients will connect
+clientPort=2181
+#
+# specify all servers in the Zookeeper ensemble
+
+#server.1=onosgui1:2888:3888
+#server.2=onosgui2:2888:3888
+#server.3=onosgui3:2888:3888
+#server.4=onosgui4:2888:3888
+#server.5=onosgui5:2888:3888
+#server.6=onosgui6:2888:3888
+#server.7=onosgui7:2888:3888
+#server.8=onosgui8:2888:3888
+#
+#
+# Be sure to read the maintenance section of the
+# administrator guide before turning on autopurge.
+#
+#
+# Be sure to read the maintenance section of the
+# administrator guide before turning on autopurge.
+#
+# http://zookeeper.apache.org/doc/current/zookeeperAdmin.html#sc_maintenance
+#
+# The number of snapshots to retain in dataDir
+#autopurge.snapRetainCount=3
+# Purge task interval in hours
+# Set to "0" to disable auto purge feature
+#autopurge.purgeInterval=1
diff --git a/scripts/check-cluster-hw.sh b/scripts/check-cluster-hw.sh
new file mode 100755
index 0000000..1243e3a
--- /dev/null
+++ b/scripts/check-cluster-hw.sh
@@ -0,0 +1,30 @@
+#! /bin/bash
+
+# This checks the overall status of Cassandra, onos, zookeeper, and the web server
+# Checks status on all 8 onos nodes.
+# Built to run only on the hw testbed from ONOS1.
+#
+
+echo ""
+echo "****************************"
+echo "***** CASSANDRA STATUS *****"
+echo "****************************"
+dsh -w onos1 'cd ONOS; ./start-cassandra.sh status'
+
+echo ""
+echo "***********************"
+echo "***** ONOS STATUS *****"
+echo "***********************"
+dsh -g onos 'cd ONOS; ./start-onos.sh status; echo "Open ports on 9160: "; netstat -nat | grep 9160 | wc -l'
+
+echo ""
+echo "****************************"
+echo "***** ZOOKEEPER STATUS *****"
+echo "****************************"
+dsh -g onos '~/zookeeper-3.4.5/bin/zkServer.sh status'
+
+echo ""
+echo "**************************"
+echo "***** WEB GUI STATUS *****"
+echo "**************************"
+~/ONOS/start-rest.sh status
diff --git a/scripts/cleanup-cassandra.sh b/scripts/cleanup-cassandra.sh
new file mode 100755
index 0000000..a88ae6a
--- /dev/null
+++ b/scripts/cleanup-cassandra.sh
@@ -0,0 +1,3 @@
+#! /bin/bash
+DIR=~/ONOS
+~/titan-0.2.0/bin/gremlin.sh -e $DIR/scripts/cleanup-onos-db
diff --git a/cleanup-onos-db b/scripts/cleanup-onos-db
similarity index 100%
rename from cleanup-onos-db
rename to scripts/cleanup-onos-db
diff --git a/ctrl-local.sh b/scripts/ctrl-local.sh
similarity index 100%
rename from ctrl-local.sh
rename to scripts/ctrl-local.sh
diff --git a/ctrl-none.sh b/scripts/ctrl-none.sh
similarity index 100%
rename from ctrl-none.sh
rename to scripts/ctrl-none.sh
diff --git a/delflow.sh b/scripts/delflow.sh
similarity index 100%
rename from delflow.sh
rename to scripts/delflow.sh
diff --git a/clean-cassandra.sh b/scripts/drop-keyspace.sh
similarity index 100%
rename from clean-cassandra.sh
rename to scripts/drop-keyspace.sh
diff --git a/showdpid.sh b/scripts/showdpid.sh
similarity index 100%
rename from showdpid.sh
rename to scripts/showdpid.sh
diff --git a/showflow.sh b/scripts/showflow.sh
similarity index 100%
rename from showflow.sh
rename to scripts/showflow.sh
diff --git a/ctrl-add-ext-template.sh b/scripts/template/ctrl-add-ext-template.sh
similarity index 100%
rename from ctrl-add-ext-template.sh
rename to scripts/template/ctrl-add-ext-template.sh
diff --git a/src/main/java/net/floodlightcontroller/core/INetMapTopologyObjects.java b/src/main/java/net/floodlightcontroller/core/INetMapTopologyObjects.java
index 2c19f68..e58b19f 100644
--- a/src/main/java/net/floodlightcontroller/core/INetMapTopologyObjects.java
+++ b/src/main/java/net/floodlightcontroller/core/INetMapTopologyObjects.java
@@ -62,10 +62,23 @@
@Property("number")
public Short getNumber();
+ @Property("number")
+ public void setNumber(Short n);
+
@JsonProperty("desc")
@Property("desc")
public String getDesc();
+ @Property("desc")
+ public void setDesc(String s);
+
+ @JsonIgnore
+ @Property("port_sate")
+ public Integer getPortState();
+
+ @Property("port_state")
+ public void setPortState(Integer s);
+
@JsonIgnore
@Incidence(label="on",direction = Direction.IN)
public ISwitchObject getSwitch();
diff --git a/src/main/java/net/floodlightcontroller/core/INetMapTopologyService.java b/src/main/java/net/floodlightcontroller/core/INetMapTopologyService.java
index b16e4a9..ecf217e 100644
--- a/src/main/java/net/floodlightcontroller/core/INetMapTopologyService.java
+++ b/src/main/java/net/floodlightcontroller/core/INetMapTopologyService.java
@@ -19,12 +19,14 @@
Iterable<ISwitchObject> getInactiveSwitches();
Iterable<IPortObject> getPortsOnSwitch(String dpid);
IPortObject getPortOnSwitch(String dpid, short port_num);
+ void close();
}
public interface ITopoLinkService {
List<Link> getActiveLinks();
List<Link> getLinksOnSwitch(String dpid);
+ void close();
}
public interface ITopoDeviceService {
Iterable<IDeviceObject> getActiveDevices();
@@ -33,7 +35,74 @@
}
public interface ITopoRouteService extends IFloodlightService {
+ /**
+ * Get the shortest path from a source to a destination.
+ *
+ * @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 getShortestPath(SwitchPort src, SwitchPort dest);
+
+ /**
+ * Fetch the Switch and Ports info from the Titan Graph
+ * and store it locally for fast access during the shortest path
+ * computation.
+ *
+ * After fetching the state, method @ref getTopoShortestPath()
+ * can be used for fast shortest path computation.
+ *
+ * Note: There is certain cost to fetch the state, hence it should
+ * be used only when there is a large number of shortest path
+ * computations that need to be done on the same topology.
+ * Typically, a single call to @ref prepareShortestPathTopo()
+ * should be followed by a large number of calls to
+ * method @ref getTopoShortestPath().
+ * After the last @ref getTopoShortestPath() call,
+ * method @ref dropShortestPathTopo() should be used to release
+ * the internal state that is not needed anymore:
+ *
+ * prepareShortestPathTopo();
+ * for (int i = 0; i < 10000; i++) {
+ * dataPath = getTopoShortestPath(...);
+ * ...
+ * }
+ * dropShortestPathTopo();
+ */
+ void prepareShortestPathTopo();
+
+ /**
+ * Release the state that was populated by
+ * method @ref prepareShortestPathTopo().
+ *
+ * See the documentation for method @ref prepareShortestPathTopo()
+ * for additional information and usage.
+ */
+ void dropShortestPathTopo();
+
+ /**
+ * Get the shortest path from a source to a destination by
+ * using the pre-populated local topology state prepared
+ * by method @ref prepareShortestPathTopo().
+ *
+ * See the documentation for method @ref prepareShortestPathTopo()
+ * for additional information and usage.
+ *
+ * @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);
+
+ /**
+ * Test whether a route exists from a source to a destination.
+ *
+ * @param src the source node for the test.
+ * @param dest the destination node for the test.
+ * @return true if a route exists, otherwise false.
+ */
Boolean routeExists(SwitchPort src, SwitchPort dest);
}
diff --git a/src/main/java/net/floodlightcontroller/core/ISwitchStorage.java b/src/main/java/net/floodlightcontroller/core/ISwitchStorage.java
index 3646e15..1219aac 100644
--- a/src/main/java/net/floodlightcontroller/core/ISwitchStorage.java
+++ b/src/main/java/net/floodlightcontroller/core/ISwitchStorage.java
@@ -50,9 +50,6 @@
*/
public void deletePort(String dpid, String portName);
- public Iterable<ISwitchObject> getActiveSwitches();
- public Iterable<ISwitchObject> getAllSwitches();
- public Iterable<ISwitchObject> getInactiveSwitches();
/*
* Initialize
diff --git a/src/main/java/net/floodlightcontroller/core/internal/Controller.java b/src/main/java/net/floodlightcontroller/core/internal/Controller.java
index 4057bc9..1a9a9c5 100644
--- a/src/main/java/net/floodlightcontroller/core/internal/Controller.java
+++ b/src/main/java/net/floodlightcontroller/core/internal/Controller.java
@@ -146,18 +146,8 @@
*/
public class Controller implements IFloodlightProviderService,
IStorageSourceListener {
-
- ThreadLocal<SwitchStorageImpl> store = new ThreadLocal<SwitchStorageImpl>() {
- @Override
- protected SwitchStorageImpl initialValue() {
- SwitchStorageImpl swStore = new SwitchStorageImpl();
- //TODO: Get the file path from global properties
- swStore.init("/tmp/cassandra.titan");
- return swStore;
- }
- };
-
- protected SwitchStorageImpl swStore = store.get();
+
+ protected SwitchStorageImpl swStore;;
protected static Logger log = LoggerFactory.getLogger(Controller.class);
@@ -560,7 +550,9 @@
removeSwitch(sw);
}
synchronized(roleChanger) {
- registryService.releaseControl(sw.getId());
+ if (controlRequested) {
+ registryService.releaseControl(sw.getId());
+ }
connectedSwitches.remove(sw);
}
sw.setConnected(false);
@@ -767,6 +759,7 @@
*/
void sendHelloConfiguration() throws IOException {
// Send initial Features Request
+ log.debug("Sending FEATURES_REQUEST to {}", sw);
sw.write(factory.getMessage(OFType.FEATURES_REQUEST), null);
}
@@ -776,6 +769,7 @@
* @throws IOException
*/
void sendFeatureReplyConfiguration() throws IOException {
+ log.debug("Sending CONFIG_REQUEST to {}", sw);
// Ensure we receive the full packet via PacketIn
OFSetConfig config = (OFSetConfig) factory
.getMessage(OFType.SET_CONFIG);
@@ -795,12 +789,15 @@
dfuture);
}
-
+
+ volatile Boolean controlRequested = Boolean.FALSE;
protected void checkSwitchReady() {
+
if (state.hsState == HandshakeState.FEATURES_REPLY &&
state.hasDescription && state.hasGetConfigReply) {
state.hsState = HandshakeState.READY;
+ log.debug("Handshake with {} complete", sw);
synchronized(roleChanger) {
// We need to keep track of all of the switches that are connected
@@ -821,10 +818,12 @@
//Request control of the switch from the global registry
try {
+ controlRequested = Boolean.TRUE;
registryService.requestControl(sw.getId(),
new RoleChangeCallback());
} catch (RegistryException e) {
log.debug("Registry error: {}", e.getMessage());
+ controlRequested = Boolean.FALSE;
}
@@ -858,6 +857,18 @@
state.firstRoleReplyReceived = true;
}
}
+ if (!controlRequested) {
+ // yield to allow other thread(s) to release control
+ try {
+ Thread.sleep(10);
+ } catch (InterruptedException e) {
+ // Ignore interruptions
+ }
+ // safer to bounce the switch to reconnect here than proceeding further
+ log.debug("Closing {} because we weren't able to request control " +
+ "successfully" + sw);
+ sw.channel.close();
+ }
}
}
@@ -1084,6 +1095,7 @@
shouldHandleMessage = handleVendorMessage((OFVendor)m);
break;
case ERROR:
+ log.debug("Recieved ERROR message from switch {}: {}", sw, m);
// TODO: we need better error handling. Especially for
// request/reply style message (stats, roles) we should have
// a unified way to lookup the xid in the error message.
@@ -1107,6 +1119,7 @@
// is not a spurious error
shouldLogError = !isBadVendorError;
if (isBadVendorError) {
+ log.debug("Handling bad vendor error for {}", sw);
if (state.firstRoleReplyReceived && (role != null)) {
log.warn("Received ERROR from sw {} that "
+"indicates roles are not supported "
@@ -1114,16 +1127,23 @@
+"role reply earlier", sw);
}
state.firstRoleReplyReceived = true;
- sw.deliverRoleRequestNotSupported(error.getXid());
+ Role requestedRole =
+ sw.deliverRoleRequestNotSupported(error.getXid());
synchronized(roleChanger) {
if (sw.role == null && Controller.this.role==Role.SLAVE) {
+ //This will now never happen. The Controller's role
+ //is now never SLAVE, always MASTER.
// the switch doesn't understand role request
// messages and the current controller role is
// slave. We need to disconnect the switch.
// @see RoleChanger for rationale
+ log.warn("Closing {} channel because controller's role " +
+ "is SLAVE", sw);
sw.getChannel().close();
}
- else if (sw.role == null) {
+ else if (sw.role == null && requestedRole == Role.MASTER) {
+ log.debug("Adding switch {} because we got an error" +
+ " returned from a MASTER role request", sw);
// Controller's role is master: add to
// active
// TODO: check if clearing flow table is
@@ -1156,6 +1176,8 @@
// to make sure that the switch eventually accepts one
// of our requests or disconnect the switch. This feels
// cumbersome.
+ log.debug("Closing {} channel because we recieved an " +
+ "error other than BAD_VENDOR", sw);
sw.getChannel().close();
}
}
@@ -1527,6 +1549,8 @@
// a "Not removing Switch ... already removed debug message.
// TODO: Figure out a way to handle this that avoids the
// spurious debug message.
+ log.debug("Closing {} because a new IOFSwitch got added " +
+ "for this dpid", oldSw);
oldSw.getChannel().close();
}
finally {
@@ -2183,11 +2207,20 @@
this.updates = new LinkedBlockingQueue<IUpdate>();
this.factory = new BasicFactory();
this.providerMap = new HashMap<String, List<IInfoProvider>>();
+
setConfigParams(configParams);
//this.role = getInitialRole(configParams);
//Set the controller's role to MASTER so it always tries to do role requests.
this.role = Role.MASTER;
this.roleChanger = new RoleChanger();
+
+ String conf = configParams.get("dbconf");
+ if (conf == null) {
+ conf = "/tmp/cassandra.titan";
+ }
+ this.swStore = new SwitchStorageImpl();
+ this.swStore.init(conf);
+
initVendorMessages();
this.systemStartTime = System.currentTimeMillis();
}
diff --git a/src/main/java/net/floodlightcontroller/core/internal/OFSwitchImpl.java b/src/main/java/net/floodlightcontroller/core/internal/OFSwitchImpl.java
index 5ffd9bc..e0ff8c3 100644
--- a/src/main/java/net/floodlightcontroller/core/internal/OFSwitchImpl.java
+++ b/src/main/java/net/floodlightcontroller/core/internal/OFSwitchImpl.java
@@ -787,15 +787,19 @@
* Otherwise we ignore it.
* @param xid
*/
- protected void deliverRoleRequestNotSupported(int xid) {
+ protected Role deliverRoleRequestNotSupported(int xid) {
synchronized(pendingRoleRequests) {
PendingRoleRequestEntry head = pendingRoleRequests.poll();
this.role = null;
if (head!=null && head.xid == xid) {
setAttribute(SWITCH_SUPPORTS_NX_ROLE, false);
+ return head.role;
}
else {
+ log.debug("Closing {} because a role request error didn't match " +
+ "head of pendingRoleRequests queue", this);
this.channel.close();
+ return null;
}
}
}
diff --git a/src/main/java/net/floodlightcontroller/core/internal/RoleChanger.java b/src/main/java/net/floodlightcontroller/core/internal/RoleChanger.java
index 6378136..b2de649 100644
--- a/src/main/java/net/floodlightcontroller/core/internal/RoleChanger.java
+++ b/src/main/java/net/floodlightcontroller/core/internal/RoleChanger.java
@@ -275,10 +275,23 @@
sw.getAttribute(IOFSwitch.SWITCH_SUPPORTS_NX_ROLE);
if ((supportsNxRole == null) || supportsNxRole) {
// Handle cases #1 and #2
+ log.debug("Sending NxRoleRequest to {}", sw);
sw.sendNxRoleRequest(role, cookie);
- } else {
- // Handle case #3
- if (role == Role.SLAVE) {
+ } else {
+ if (role == Role.MASTER) {
+ log.debug("Switch {} doesn't support NxRoleRequests, but sending " +
+ "{} request anyway", sw, role);
+ //Send the role request anyway, even though we know the switch
+ //doesn't support it. The switch will give an error and in our
+ //error handling code we will add the switch.
+ //NOTE we *could* just add the switch right away rather than
+ //going through the overhead of sending a role request - however
+ //we then have to deal with concurrency issues resulting from
+ //calling addSwitch outside of a netty handler.
+ sw.sendNxRoleRequest(role, cookie);
+ }
+ // Handle case #3
+ else if (role == Role.SLAVE) {
log.debug("Disconnecting switch {} that doesn't support " +
"role request messages from a controller that went to SLAVE mode");
// Closing the channel should result in a call to
diff --git a/src/main/java/net/floodlightcontroller/core/internal/SwitchStorageImpl.java b/src/main/java/net/floodlightcontroller/core/internal/SwitchStorageImpl.java
index 7e049b6..a068586 100644
--- a/src/main/java/net/floodlightcontroller/core/internal/SwitchStorageImpl.java
+++ b/src/main/java/net/floodlightcontroller/core/internal/SwitchStorageImpl.java
@@ -1,31 +1,21 @@
package net.floodlightcontroller.core.internal;
-import java.util.ArrayList;
import java.util.Collection;
-import java.util.List;
-import java.util.Set;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
+import net.floodlightcontroller.core.INetMapTopologyObjects.IPortObject;
+import net.floodlightcontroller.core.INetMapTopologyObjects.ISwitchObject;
+import net.floodlightcontroller.core.ISwitchStorage;
+import net.onrc.onos.util.GraphDBConnection;
+import net.onrc.onos.util.GraphDBConnection.Transaction;
import org.openflow.protocol.OFPhysicalPort;
import org.openflow.protocol.OFPhysicalPort.OFPortConfig;
import org.openflow.protocol.OFPhysicalPort.OFPortState;
-
-import com.thinkaurelius.titan.core.TitanException;
-import com.thinkaurelius.titan.core.TitanFactory;
-import com.thinkaurelius.titan.core.TitanGraph;
-import com.tinkerpop.blueprints.Direction;
-import com.tinkerpop.blueprints.TransactionalGraph;
-import com.tinkerpop.blueprints.TransactionalGraph.Conclusion;
-import com.tinkerpop.blueprints.Edge;
-import com.tinkerpop.blueprints.Vertex;
-import com.tinkerpop.frames.FramedGraph;
-import net.floodlightcontroller.core.INetMapTopologyObjects.ISwitchObject;
-import net.floodlightcontroller.core.ISwitchStorage;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
public class SwitchStorageImpl implements ISwitchStorage {
- public TitanGraph graph;
+ public GraphDBConnection conn;
protected static Logger log = LoggerFactory.getLogger(SwitchStorageImpl.class);
@Override
@@ -50,26 +40,20 @@
}
private void setStatus(String dpid, SwitchState state) {
- Vertex sw;
- try {
- if ((sw = graph.getVertices("dpid",dpid).iterator().next()) != null) {
- sw.setProperty("state",state.toString());
- graph.stopTransaction(Conclusion.SUCCESS);
- log.info("SwitchStorage:setStatus dpid:{} state: {} done", dpid, state);
- }
- } catch (TitanException e) {
- // TODO: handle exceptions
- graph.stopTransaction(Conclusion.FAILURE);
+ ISwitchObject sw = conn.utils().searchSwitch(conn, dpid);
+ if (sw != null) {
+ sw.setState(state.toString());
+ conn.endTx(Transaction.COMMIT);
+ log.info("SwitchStorage:setStatus dpid:{} state: {} done", dpid, state);
+ } else {
+ conn.endTx(Transaction.ROLLBACK);
log.info("SwitchStorage:setStatus dpid:{} state: {} failed", dpid, state);
}
-
-
}
@Override
public void addPort(String dpid, OFPhysicalPort port) {
// TODO Auto-generated method stub
- Vertex sw;
boolean portDown = ((OFPortConfig.OFPPC_PORT_DOWN.getValue() & port.getConfig()) > 0) ||
((OFPortState.OFPPS_LINK_DOWN.getValue() & port.getState()) > 0);
@@ -77,30 +61,31 @@
deletePort(dpid, port.getPortNumber());
return;
}
+
try {
- if ((sw = graph.getVertices("dpid",dpid).iterator().next()) != null) {
+ ISwitchObject sw = conn.utils().searchSwitch(conn, dpid);
+
+ if (sw != null) {
+ IPortObject p = conn.utils().searchPort(conn, dpid, port.getPortNumber());
log.info("SwitchStorage:addPort dpid:{} port:{}", dpid, port.getPortNumber());
- // TODO: Check if port exists
- if (sw.query().direction(Direction.OUT).labels("on").has("number",port.getPortNumber()).vertices().iterator().hasNext()) {
- //TODO: Do nothing for now
+ if (p != null) {
log.error("SwitchStorage:addPort dpid:{} port:{} exists", dpid, port.getPortNumber());
} else {
- Vertex p = graph.addVertex(null);
- p.setProperty("type","port");
- p.setProperty("number",port.getPortNumber());
- p.setProperty("state", "ACTIVE");
- p.setProperty("port_state",port.getState());
- p.setProperty("desc",port.getName());
- Edge e = graph.addEdge(null, sw, p, "on");
- e.setProperty("state","ACTIVE");
- e.setProperty("number", port.getPortNumber());
-
- graph.stopTransaction(Conclusion.SUCCESS);
+ p = conn.utils().newPort(conn);
+
+ p.setType("port");
+ p.setNumber(port.getPortNumber());
+ p.setState("ACTIVE");
+ p.setPortState(port.getState());
+ p.setDesc(port.getName());
+ sw.addPort(p);
+ conn.endTx(Transaction.COMMIT);
+
}
}
- } catch (TitanException e) {
+ } catch (Exception e) {
// TODO: handle exceptions
- graph.stopTransaction(Conclusion.FAILURE);
+ conn.endTx(Transaction.ROLLBACK);
log.error("SwitchStorage:addPort dpid:{} port:{} failed", dpid, port.getPortNumber());
}
@@ -130,29 +115,29 @@
log.info("SwitchStorage:addSwitch(): dpid {} ", dpid);
try {
- if (graph.getVertices("dpid",dpid).iterator().hasNext()) {
+ ISwitchObject sw = conn.utils().searchSwitch(conn, dpid);
+ if (sw != null) {
/*
* Do nothing or throw exception?
*/
- Vertex sw = graph.getVertices("dpid",dpid).iterator().next();
log.info("SwitchStorage:addSwitch dpid:{} already exists", dpid);
- sw.setProperty("state",SwitchState.ACTIVE.toString());
- graph.stopTransaction(Conclusion.SUCCESS);
+ sw.setState(SwitchState.ACTIVE.toString());
+ conn.endTx(Transaction.COMMIT);
} else {
- Vertex sw = graph.addVertex(null);
+ sw = conn.utils().newSwitch(conn);
- sw.setProperty("type","switch");
- sw.setProperty("dpid", dpid);
- sw.setProperty("state",SwitchState.ACTIVE.toString());
- graph.stopTransaction(Conclusion.SUCCESS);
+ sw.setType("switch");
+ sw.setDPID(dpid);
+ sw.setState(SwitchState.ACTIVE.toString());
+ conn.endTx(Transaction.COMMIT);
log.info("SwitchStorage:addSwitch dpid:{} added", dpid);
}
- } catch (TitanException e) {
+ } catch (Exception e) {
/*
- * retry till we succeed?
+ * retry?
*/
- graph.stopTransaction(Conclusion.FAILURE);
+ conn.endTx(Transaction.ROLLBACK);
log.info("SwitchStorage:addSwitch dpid:{} failed", dpid);
}
@@ -162,17 +147,19 @@
@Override
public void deleteSwitch(String dpid) {
// TODO Setting inactive but we need to eventually remove data
- Vertex sw;
+
try {
-
- if ((sw = graph.getVertices("dpid",dpid).iterator().next()) != null) {
- graph.removeVertex(sw);
- graph.stopTransaction(Conclusion.SUCCESS);
+
+ ISwitchObject sw = conn.utils().searchSwitch(conn, dpid);
+ if (sw != null) {
+ conn.utils().removeSwitch(conn, sw);
+
+ conn.endTx(Transaction.COMMIT);
log.info("SwitchStorage:DeleteSwitch dpid:{} done", dpid);
}
- } catch (TitanException e) {
+ } catch (Exception e) {
// TODO: handle exceptions
- graph.stopTransaction(Conclusion.FAILURE);
+ conn.endTx(Transaction.ROLLBACK);
log.error("SwitchStorage:deleteSwitch {} failed", dpid);
}
@@ -181,21 +168,21 @@
@Override
public void deletePort(String dpid, short port) {
// TODO Auto-generated method stub
- Vertex sw;
try {
- if ((sw = graph.getVertices("dpid",dpid).iterator().next()) != null) {
- // TODO: Check if port exists
- log.info("SwitchStorage:deletePort dpid:{} port:{}", dpid, port);
- if (sw.query().direction(Direction.OUT).labels("on").has("number",port).vertices().iterator().hasNext()) {
- Vertex p = sw.query().direction(Direction.OUT).labels("on").has("number",port).vertices().iterator().next();
+ ISwitchObject sw = conn.utils().searchSwitch(conn, dpid);
+
+ if (sw != null) {
+ IPortObject p = conn.utils().searchPort(conn, dpid, port);
+ if (p != null) {
log.info("SwitchStorage:deletePort dpid:{} port:{} found and deleted", dpid, port);
- graph.removeVertex(p);
- graph.stopTransaction(Conclusion.SUCCESS);
+ sw.removePort(p);
+ conn.utils().removePort(conn, p);
+ conn.endTx(Transaction.COMMIT);
}
}
- } catch (TitanException e) {
+ } catch (Exception e) {
// TODO: handle exceptions
- graph.stopTransaction(Conclusion.FAILURE);
+ conn.endTx(Transaction.ROLLBACK);
log.info("SwitchStorage:deletePort dpid:{} port:{} failed", dpid, port);
}
}
@@ -206,74 +193,24 @@
}
- @Override
- public Iterable<ISwitchObject> getActiveSwitches() {
- // TODO Add unit test
- FramedGraph<TitanGraph> fg = new FramedGraph<TitanGraph>(graph);
- Iterable<ISwitchObject> switches = fg.getVertices("type","switch",ISwitchObject.class);
- List<ISwitchObject> activeSwitches = new ArrayList<ISwitchObject>();
- for (ISwitchObject sw: switches) {
- if(sw.getState().equals(SwitchState.ACTIVE.toString())) {
- activeSwitches.add(sw);
- }
- }
-
- return activeSwitches;
- }
@Override
public void init(String conf) {
- graph = TitanFactory.open(conf);
+ conn = GraphDBConnection.getInstance(conf);
- // FIXME: Creation on Indexes should be done only once
- Set<String> s = graph.getIndexedKeys(Vertex.class);
- if (!s.contains("dpid")) {
- graph.createKeyIndex("dpid", Vertex.class);
- graph.stopTransaction(Conclusion.SUCCESS);
- }
- if (!s.contains("type")) {
- graph.createKeyIndex("type", Vertex.class);
- graph.stopTransaction(Conclusion.SUCCESS);
- }
}
- @Override
- public Iterable<ISwitchObject> getAllSwitches() {
- // TODO Auto-generated method stub
- FramedGraph<TitanGraph> fg = new FramedGraph<TitanGraph>(graph);
- Iterable<ISwitchObject> switches = fg.getVertices("type","switch",ISwitchObject.class);
- return switches;
- }
-
- @Override
- public Iterable<ISwitchObject> getInactiveSwitches() {
- // TODO Auto-generated method stub
- FramedGraph<TitanGraph> fg = new FramedGraph<TitanGraph>(graph);
- Iterable<ISwitchObject> switches = fg.getVertices("type","switch",ISwitchObject.class);
-
- List<ISwitchObject> inactiveSwitches = new ArrayList<ISwitchObject>();
-
- for (ISwitchObject sw: switches) {
- if(sw.getState().equals(SwitchState.INACTIVE.toString())) {
- inactiveSwitches.add(sw);
- }
- }
- return inactiveSwitches;
- }
-
public void finalize() {
close();
}
@Override
public void close() {
- // TODO Auto-generated method stub
- graph.shutdown();
-
+ conn.close();
}
diff --git a/src/main/java/net/floodlightcontroller/core/internal/TopoSwitchServiceImpl.java b/src/main/java/net/floodlightcontroller/core/internal/TopoSwitchServiceImpl.java
index e8b1bf8..b3c31ec 100644
--- a/src/main/java/net/floodlightcontroller/core/internal/TopoSwitchServiceImpl.java
+++ b/src/main/java/net/floodlightcontroller/core/internal/TopoSwitchServiceImpl.java
@@ -1,39 +1,49 @@
package net.floodlightcontroller.core.internal;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
import net.floodlightcontroller.core.INetMapTopologyObjects.IPortObject;
import net.floodlightcontroller.core.INetMapTopologyObjects.ISwitchObject;
import net.floodlightcontroller.core.INetMapTopologyService.ITopoSwitchService;
+import net.onrc.onos.util.GraphDBConnection;
+import net.onrc.onos.util.GraphDBConnection.Transaction;
public class TopoSwitchServiceImpl implements ITopoSwitchService {
- ThreadLocal<SwitchStorageImpl> store = new ThreadLocal<SwitchStorageImpl>() {
- @Override
- protected SwitchStorageImpl initialValue() {
- SwitchStorageImpl swStore = new SwitchStorageImpl();
- //TODO: Get the file path from global properties
- swStore.init("/tmp/cassandra.titan");
- return swStore;
- }
- };
+ private GraphDBConnection conn;
+ protected static Logger log = LoggerFactory.getLogger(TopoSwitchServiceImpl.class);
+
+
+ public void finalize() {
+ close();
+ }
- SwitchStorageImpl swStore = store.get();
+ @Override
+ public void close() {
+ conn.endTx(Transaction.COMMIT);
+ conn.close();
+ }
@Override
public Iterable<ISwitchObject> getActiveSwitches() {
// TODO Auto-generated method stub
- return swStore.getActiveSwitches();
+ conn = GraphDBConnection.getInstance("");
+ return conn.utils().getActiveSwitches(conn);
}
@Override
public Iterable<ISwitchObject> getAllSwitches() {
- // TODO Auto-generated method stub
- return swStore.getAllSwitches();
+ // TODO Auto-generated method stub
+ conn = GraphDBConnection.getInstance("");
+ return conn.utils().getAllSwitches(conn);
}
@Override
public Iterable<ISwitchObject> getInactiveSwitches() {
// TODO Auto-generated method stub
- return swStore.getInactiveSwitches();
+ conn = GraphDBConnection.getInstance("");
+ return conn.utils().getInactiveSwitches(conn);
}
@Override
diff --git a/src/main/java/net/floodlightcontroller/devicemanager/internal/DeviceStorageImpl.java b/src/main/java/net/floodlightcontroller/devicemanager/internal/DeviceStorageImpl.java
index 64c12dc..e964b8e 100644
--- a/src/main/java/net/floodlightcontroller/devicemanager/internal/DeviceStorageImpl.java
+++ b/src/main/java/net/floodlightcontroller/devicemanager/internal/DeviceStorageImpl.java
@@ -26,7 +26,7 @@
conn = GraphDBConnection.getInstance(conf);
}
- public void finalize() {
+ public void finalize() {
close();
}
diff --git a/src/main/java/net/floodlightcontroller/flowcache/FlowManager.java b/src/main/java/net/floodlightcontroller/flowcache/FlowManager.java
index d55979e..24a0c58 100644
--- a/src/main/java/net/floodlightcontroller/flowcache/FlowManager.java
+++ b/src/main/java/net/floodlightcontroller/flowcache/FlowManager.java
@@ -8,15 +8,20 @@
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
+import java.util.Collections;
+import java.util.concurrent.BlockingQueue;
import java.util.concurrent.Executors;
+import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
+import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import net.floodlightcontroller.core.IFloodlightProviderService;
import net.floodlightcontroller.core.INetMapStorage;
import net.floodlightcontroller.core.INetMapTopologyObjects.IFlowEntry;
import net.floodlightcontroller.core.INetMapTopologyObjects.IFlowPath;
+import net.floodlightcontroller.core.INetMapTopologyService.ITopoRouteService;
import net.floodlightcontroller.core.IOFSwitch;
import net.floodlightcontroller.core.module.FloodlightModuleContext;
import net.floodlightcontroller.core.module.FloodlightModuleException;
@@ -41,6 +46,7 @@
import net.floodlightcontroller.util.MACAddress;
import net.floodlightcontroller.util.OFMessageDamper;
import net.floodlightcontroller.util.Port;
+import net.floodlightcontroller.util.SwitchPort;
import net.onrc.onos.util.GraphDBConnection;
import net.onrc.onos.util.GraphDBConnection.Transaction;
@@ -61,6 +67,7 @@
protected IRestApiService restApi;
protected IFloodlightProviderService floodlightProvider;
+ protected FloodlightModuleContext context;
protected OFMessageDamper messageDamper;
@@ -75,14 +82,178 @@
public static final short PRIORITY_DEFAULT = 100;
private static long nextFlowEntryId = 1;
+ private static long measurementFlowId = 100000;
+ private static String measurementFlowIdStr = "0x186a0"; // 100000
+ private long modifiedMeasurementFlowTime = 0;
/** The logger. */
private static Logger log = LoggerFactory.getLogger(FlowManager.class);
// The periodic task(s)
- private final ScheduledExecutorService scheduler =
+ private final ScheduledExecutorService measureShortestPathScheduler =
Executors.newScheduledThreadPool(1);
- final Runnable reader = new Runnable() {
+ private final ScheduledExecutorService measureMapReaderScheduler =
+ Executors.newScheduledThreadPool(1);
+ private final ScheduledExecutorService mapReaderScheduler =
+ Executors.newScheduledThreadPool(1);
+
+ private BlockingQueue<Runnable> shortestPathQueue = new LinkedBlockingQueue<Runnable>();
+ private ThreadPoolExecutor shortestPathExecutor =
+ new ThreadPoolExecutor(10, 10, 5, TimeUnit.SECONDS, shortestPathQueue);
+
+ class ShortestPathTask implements Runnable {
+ private int hint;
+ private ITopoRouteService topoRouteService;
+ private ArrayList<DataPath> dpList;
+
+ public ShortestPathTask(int hint,
+ ITopoRouteService topoRouteService,
+ ArrayList<DataPath> dpList) {
+ this.hint = hint;
+ this.topoRouteService = topoRouteService;
+ this.dpList = dpList;
+ }
+
+ @Override
+ public void run() {
+ /*
+ String logMsg = "MEASUREMENT: Running Thread hint " + this.hint;
+ log.debug(logMsg);
+ long startTime = System.nanoTime();
+ */
+ for (DataPath dp : this.dpList) {
+ topoRouteService.getTopoShortestPath(dp.srcPort(), dp.dstPort());
+ }
+ /*
+ long estimatedTime = System.nanoTime() - startTime;
+ double rate = (estimatedTime > 0)? ((double)dpList.size() * 1000000000) / estimatedTime: 0.0;
+ logMsg = "MEASUREMENT: Computed Thread hint " + hint + ": " + dpList.size() + " shortest paths in " + (double)estimatedTime / 1000000000 + " sec: " + rate + " flows/s";
+ log.debug(logMsg);
+ */
+ }
+ }
+
+ final Runnable measureShortestPath = new Runnable() {
+ public void run() {
+ log.debug("Recomputing Shortest Paths from the Network Map Flows...");
+ if (floodlightProvider == null) {
+ log.debug("FloodlightProvider service not found!");
+ return;
+ }
+
+ ITopoRouteService topoRouteService =
+ context.getServiceImpl(ITopoRouteService.class);
+ if (topoRouteService == null) {
+ log.debug("Topology Route Service not found");
+ return;
+ }
+
+ int leftoverQueueSize = shortestPathExecutor.getQueue().size();
+ if (leftoverQueueSize > 0) {
+ String logMsg = "MEASUREMENT: Leftover Shortest Path Queue Size: " + leftoverQueueSize;
+ log.debug(logMsg);
+ return;
+ }
+ log.debug("MEASUREMENT: Beginning Shortest Path Computation");
+
+ //
+ // Recompute the Shortest Paths for all Flows
+ //
+ int counter = 0;
+ int hint = 0;
+ ArrayList<DataPath> dpList = new ArrayList<DataPath>();
+ long startTime = System.nanoTime();
+
+ topoRouteService.prepareShortestPathTopo();
+
+ Iterable<IFlowPath> allFlowPaths = conn.utils().getAllFlowPaths(conn);
+ for (IFlowPath flowPathObj : allFlowPaths) {
+ FlowId flowId = new FlowId(flowPathObj.getFlowId());
+
+ // log.debug("Found Path {}", flowId.toString());
+ Dpid srcDpid = new Dpid(flowPathObj.getSrcSwitch());
+ Port srcPort = new Port(flowPathObj.getSrcPort());
+ Dpid dstDpid = new Dpid(flowPathObj.getDstSwitch());
+ Port dstPort = new Port(flowPathObj.getDstPort());
+ SwitchPort srcSwitchPort = new SwitchPort(srcDpid, srcPort);
+ SwitchPort dstSwitchPort = new SwitchPort(dstDpid, dstPort);
+
+ /*
+ DataPath dp = new DataPath();
+ dp.setSrcPort(srcSwitchPort);
+ dp.setDstPort(dstSwitchPort);
+ dpList.add(dp);
+ if ((dpList.size() % 10) == 0) {
+ shortestPathExecutor.execute(
+ new ShortestPathTask(hint, topoRouteService,
+ dpList));
+ dpList = new ArrayList<DataPath>();
+ hint++;
+ }
+ */
+
+ DataPath dataPath =
+ topoRouteService.getTopoShortestPath(srcSwitchPort,
+ dstSwitchPort);
+ counter++;
+ }
+ if (dpList.size() > 0) {
+ shortestPathExecutor.execute(
+ new ShortestPathTask(hint, topoRouteService,
+ dpList));
+ }
+
+ /*
+ // Wait for all tasks to finish
+ try {
+ while (shortestPathExecutor.getQueue().size() > 0) {
+ Thread.sleep(100);
+ }
+ } catch (InterruptedException ex) {
+ log.debug("MEASUREMENT: Shortest Path Computation interrupted");
+ }
+ */
+
+ conn.endTx(Transaction.COMMIT);
+ topoRouteService.dropShortestPathTopo();
+
+ long estimatedTime = System.nanoTime() - startTime;
+ double rate = (estimatedTime > 0)? ((double)counter * 1000000000) / estimatedTime: 0.0;
+ String logMsg = "MEASUREMENT: Computed " + counter + " shortest paths in " + (double)estimatedTime / 1000000000 + " sec: " + rate + " flows/s";
+ log.debug(logMsg);
+ }
+ };
+
+ final Runnable measureMapReader = new Runnable() {
+ public void run() {
+ if (floodlightProvider == null) {
+ log.debug("FloodlightProvider service not found!");
+ return;
+ }
+
+ //
+ // Fetch all Flow Entries
+ //
+ int counter = 0;
+ long startTime = System.nanoTime();
+ Iterable<IFlowEntry> allFlowEntries = conn.utils().getAllFlowEntries(conn);
+ for (IFlowEntry flowEntryObj : allFlowEntries) {
+ counter++;
+ FlowEntryId flowEntryId =
+ new FlowEntryId(flowEntryObj.getFlowEntryId());
+ String userState = flowEntryObj.getUserState();
+ String switchState = flowEntryObj.getSwitchState();
+ }
+ conn.endTx(Transaction.COMMIT);
+
+ long estimatedTime = System.nanoTime() - startTime;
+ double rate = (estimatedTime > 0)? ((double)counter * 1000000000) / estimatedTime: 0.0;
+ String logMsg = "MEASUREMENT: Fetched " + counter + " flow entries in " + (double)estimatedTime / 1000000000 + " sec: " + rate + " entries/s";
+ log.debug(logMsg);
+ }
+ };
+
+ final Runnable mapReader = new Runnable() {
public void run() {
// log.debug("Reading Flow Entries from the Network Map...");
if (floodlightProvider == null) {
@@ -101,13 +272,16 @@
for (IFlowEntry flowEntryObj : allFlowEntries) {
FlowEntryId flowEntryId =
new FlowEntryId(flowEntryObj.getFlowEntryId());
- String userState = "User State: " + flowEntryObj.getUserState();
- String switchState = "Switch State: " + flowEntryObj.getSwitchState();
+ String userState = flowEntryObj.getUserState();
+ String switchState = flowEntryObj.getSwitchState();
+ /**
log.debug("Found Flow Entry {}: {}",
flowEntryId.toString(),
- userState + " " + switchState);
-
+ "User State: " + userState +
+ " Switch State: " + switchState);
+ */
+
if (! switchState.equals("FE_SWITCH_NOT_UPDATED")) {
// Ignore the entry: nothing to do
continue;
@@ -125,8 +299,19 @@
//
// Process my Flow Entries
//
+ Boolean processed_measurement_flow = false;
for (Map.Entry<Long, IFlowEntry> entry : myFlowEntries.entrySet()) {
IFlowEntry flowEntryObj = entry.getValue();
+ // Code for measurement purpose
+ {
+ IFlowPath flowObj =
+ conn.utils().getFlowPathByFlowEntry(conn,
+ flowEntryObj);
+ if ((flowObj != null) &&
+ flowObj.getFlowId().equals(measurementFlowIdStr)) {
+ processed_measurement_flow = true;
+ }
+ }
//
// TODO: Eliminate the re-fetching of flowEntryId,
@@ -160,6 +345,8 @@
flowModCommand = OFFlowMod.OFPFC_DELETE_STRICT;
} else {
// Unknown user state. Ignore the entry
+ log.debug("Flow Entry ignored (FlowEntryId = {}): unknown user state {}",
+ flowEntryId.toString(), userState);
continue;
}
@@ -265,10 +452,28 @@
}
}
conn.endTx(Transaction.COMMIT);
+
+ if (processed_measurement_flow) {
+ long estimatedTime = System.nanoTime() - modifiedMeasurementFlowTime;
+ String logMsg = "MEASUREMENT: Pushed Flow delay: " +
+ (double)estimatedTime / 1000000000 + " sec";
+ log.debug(logMsg);
+ }
}
};
- final ScheduledFuture<?> readerHandle =
- scheduler.scheduleAtFixedRate(reader, 3, 3, TimeUnit.SECONDS);
+
+ /*
+ final ScheduledFuture<?> measureShortestPathHandle =
+ measureShortestPathScheduler.scheduleAtFixedRate(measureShortestPath, 10, 10, TimeUnit.SECONDS);
+ */
+
+ /*
+ final ScheduledFuture<?> measureMapReaderHandle =
+ measureMapReaderScheduler.scheduleAtFixedRate(measureMapReader, 10, 10, TimeUnit.SECONDS);
+ */
+
+ final ScheduledFuture<?> mapReaderHandle =
+ mapReaderScheduler.scheduleAtFixedRate(mapReader, 3, 3, TimeUnit.SECONDS);
@Override
public void init(String conf) {
@@ -316,6 +521,7 @@
@Override
public void init(FloodlightModuleContext context)
throws FloodlightModuleException {
+ this.context = context;
floodlightProvider = context.getServiceImpl(IFloodlightProviderService.class);
restApi = context.getServiceImpl(IRestApiService.class);
messageDamper = new OFMessageDamper(OFMESSAGE_DAMPER_CAPACITY,
@@ -329,6 +535,19 @@
@Override
public void startUp(FloodlightModuleContext context) {
restApi.addRestletRoutable(new FlowWebRoutable());
+
+ //
+ // Extract all flow entries and assign the next Flow Entry ID
+ // to be larger than the largest Flow Entry ID
+ //
+ Iterable<IFlowEntry> allFlowEntries = conn.utils().getAllFlowEntries(conn);
+ for (IFlowEntry flowEntryObj : allFlowEntries) {
+ FlowEntryId flowEntryId =
+ new FlowEntryId(flowEntryObj.getFlowEntryId());
+ if (flowEntryId.value() >= nextFlowEntryId)
+ nextFlowEntryId = flowEntryId.value() + 1;
+ }
+ conn.endTx(Transaction.COMMIT);
}
/**
@@ -343,6 +562,9 @@
*/
@Override
public boolean addFlow(FlowPath flowPath, FlowId flowId) {
+ if (flowPath.flowId().value() == measurementFlowId) {
+ modifiedMeasurementFlowTime = System.nanoTime();
+ }
//
// Assign the FlowEntry IDs
@@ -506,6 +728,10 @@
*/
@Override
public boolean deleteFlow(FlowId flowId) {
+ if (flowId.value() == measurementFlowId) {
+ modifiedMeasurementFlowTime = System.nanoTime();
+ }
+
IFlowPath flowObj = null;
//
// We just mark the entries for deletion,
@@ -733,6 +959,61 @@
}
/**
+ * Get summary of all installed flows by all installers in a given range
+ *
+ * @param flowId the data path endpoints of the flows to get.
+ * @param maxFlows: the maximum number of flows to be returned
+ * @return the Flow Paths if found, otherwise null.
+ */
+ @Override
+ public ArrayList<FlowPath> getAllFlowsSummary(FlowId flowId, int maxFlows) {
+ //
+ // TODO: The implementation below is not optimal:
+ // We fetch all flows, and then return only the subset that match
+ // the query conditions.
+ // We should use the appropriate Titan/Gremlin query to filter-out
+ // the flows as appropriate.
+ //
+ ArrayList<FlowPath> allFlows = getAllFlows();
+
+ if (allFlows == null) {
+ log.debug("Get FlowPathsSummary for {} {}: no FlowPaths found", flowId, maxFlows);
+ return null;
+ }
+
+ Collections.sort(allFlows);
+
+ ArrayList<FlowPath> flowPaths = new ArrayList<FlowPath>();
+ for (FlowPath flow : allFlows) {
+
+ // start from desired flowId
+ if (flow.flowId().value() < flowId.value()) {
+ continue;
+ }
+
+ // Summarize by making null flow entry fields that are not relevant to report
+ for (FlowEntry flowEntry : flow.dataPath().flowEntries()) {
+ flowEntry.setFlowEntryActions(null);
+ flowEntry.setFlowEntryMatch(null);
+ }
+
+ flowPaths.add(flow);
+ if (maxFlows != 0 && flowPaths.size() >= maxFlows) {
+ break;
+ }
+ }
+
+ if (flowPaths.isEmpty()) {
+ log.debug("Get FlowPathsSummary {} {}: no FlowPaths found", flowId, maxFlows);
+ flowPaths = null;
+ } else {
+ log.debug("Get FlowPathsSummary for {} {}: FlowPaths were found", flowId, maxFlows);
+ }
+
+ return flowPaths;
+ }
+
+ /**
* Get all installed flows by all installers.
*
* @return the Flow Paths if found, otherwise null.
diff --git a/src/main/java/net/floodlightcontroller/flowcache/IFlowService.java b/src/main/java/net/floodlightcontroller/flowcache/IFlowService.java
index 48477f1..41c0f57 100644
--- a/src/main/java/net/floodlightcontroller/flowcache/IFlowService.java
+++ b/src/main/java/net/floodlightcontroller/flowcache/IFlowService.java
@@ -68,6 +68,15 @@
ArrayList<FlowPath> getAllFlows(DataPathEndpoints dataPathEndpoints);
/**
+ * Get summary of all installed flows by all installers.
+ *
+ * @param flowId: starting flow Id of the range
+ * @param maxFlows: number of flows to return
+ * @return the Flow Paths if found, otherwise null.
+ */
+ ArrayList<FlowPath> getAllFlowsSummary(FlowId flowId, int maxFlows);
+
+ /**
* Get all installed flows by all installers.
*
* @return the Flow Paths if found, otherwise null.
diff --git a/src/main/java/net/floodlightcontroller/flowcache/web/FlowWebRoutable.java b/src/main/java/net/floodlightcontroller/flowcache/web/FlowWebRoutable.java
index a40a508..19f9e14 100644
--- a/src/main/java/net/floodlightcontroller/flowcache/web/FlowWebRoutable.java
+++ b/src/main/java/net/floodlightcontroller/flowcache/web/FlowWebRoutable.java
@@ -20,6 +20,7 @@
router.attach("/getall-by-installer-id/{installer-id}/{src-dpid}/{src-port}/{dst-dpid}/{dst-port}/json", GetAllFlowsByInstallerIdResource.class);
router.attach("/getall-by-endpoints/{src-dpid}/{src-port}/{dst-dpid}/{dst-port}/json", GetAllFlowsByEndpointsResource.class);
router.attach("/getall/json", GetAllFlowsResource.class);
+ router.attach("/getsummary/{flow-id}/{max-flows}/json", GetSummaryFlowsResource.class);
return router;
}
diff --git a/src/main/java/net/floodlightcontroller/flowcache/web/GetSummaryFlowsResource.java b/src/main/java/net/floodlightcontroller/flowcache/web/GetSummaryFlowsResource.java
new file mode 100644
index 0000000..7a928c9
--- /dev/null
+++ b/src/main/java/net/floodlightcontroller/flowcache/web/GetSummaryFlowsResource.java
@@ -0,0 +1,44 @@
+package net.floodlightcontroller.flowcache.web;
+
+import java.util.ArrayList;
+
+import net.floodlightcontroller.flowcache.IFlowService;
+import net.floodlightcontroller.util.FlowPath;
+import net.floodlightcontroller.util.FlowId;
+
+import org.restlet.resource.Get;
+import org.restlet.resource.ServerResource;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class GetSummaryFlowsResource extends ServerResource {
+ protected static Logger log = LoggerFactory.getLogger(GetSummaryFlowsResource.class);
+
+ @Get("json")
+ public ArrayList<FlowPath> retrieve() {
+ ArrayList<FlowPath> result = null;
+
+ FlowId flowId;
+ int maxFlows = 0;
+
+ 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 flowIdStr = (String) getRequestAttributes().get("flow-id");
+ String maxFlowStr = (String) getRequestAttributes().get("max-flows");
+ log.debug("Get Summary Flows starting flow-id: " + flowIdStr + " max-flows: " + maxFlowStr);
+
+ flowId = new FlowId(flowIdStr);
+ maxFlows = Integer.parseInt(maxFlowStr);
+ if (maxFlows < 0) maxFlows = 0;
+
+ result = flowService.getAllFlowsSummary(flowId, maxFlows);
+
+ 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 b08fd8c..fa75769 100644
--- a/src/main/java/net/floodlightcontroller/linkdiscovery/internal/LinkDiscoveryManager.java
+++ b/src/main/java/net/floodlightcontroller/linkdiscovery/internal/LinkDiscoveryManager.java
@@ -42,31 +42,30 @@
import net.floodlightcontroller.core.FloodlightContext;
import net.floodlightcontroller.core.IFloodlightProviderService;
import net.floodlightcontroller.core.IFloodlightProviderService.Role;
-import net.floodlightcontroller.core.INetMapStorage.DM_OPERATION;
import net.floodlightcontroller.core.IHAListener;
import net.floodlightcontroller.core.IInfoProvider;
+import net.floodlightcontroller.core.INetMapStorage.DM_OPERATION;
import net.floodlightcontroller.core.IOFMessageListener;
import net.floodlightcontroller.core.IOFSwitch;
-import net.floodlightcontroller.core.internal.OFSwitchImpl;
import net.floodlightcontroller.core.IOFSwitchListener;
import net.floodlightcontroller.core.annotations.LogMessageCategory;
import net.floodlightcontroller.core.annotations.LogMessageDoc;
import net.floodlightcontroller.core.annotations.LogMessageDocs;
-//import net.floodlightcontroller.core.internal.SwitchStorageImpl;
+import net.floodlightcontroller.core.internal.OFSwitchImpl;
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.util.SingletonTask;
import net.floodlightcontroller.linkdiscovery.ILinkDiscovery;
+import net.floodlightcontroller.linkdiscovery.ILinkDiscovery.LDUpdate;
import net.floodlightcontroller.linkdiscovery.ILinkDiscovery.LinkType;
import net.floodlightcontroller.linkdiscovery.ILinkDiscovery.SwitchType;
-import net.floodlightcontroller.linkdiscovery.ILinkDiscovery.LDUpdate;
import net.floodlightcontroller.linkdiscovery.ILinkDiscovery.UpdateOperation;
-import net.floodlightcontroller.linkdiscovery.web.LinkDiscoveryWebRoutable;
import net.floodlightcontroller.linkdiscovery.ILinkDiscoveryListener;
import net.floodlightcontroller.linkdiscovery.ILinkDiscoveryService;
import net.floodlightcontroller.linkdiscovery.LinkInfo;
+import net.floodlightcontroller.linkdiscovery.web.LinkDiscoveryWebRoutable;
import net.floodlightcontroller.packet.BSN;
import net.floodlightcontroller.packet.Ethernet;
import net.floodlightcontroller.packet.IPv4;
@@ -75,8 +74,8 @@
import net.floodlightcontroller.restserver.IRestApiService;
import net.floodlightcontroller.routing.Link;
import net.floodlightcontroller.storage.IResultSet;
-import net.floodlightcontroller.storage.IStorageSourceService;
import net.floodlightcontroller.storage.IStorageSourceListener;
+import net.floodlightcontroller.storage.IStorageSourceService;
import net.floodlightcontroller.storage.OperatorPredicate;
import net.floodlightcontroller.storage.StorageException;
import net.floodlightcontroller.threadpool.IThreadPoolService;
@@ -84,6 +83,10 @@
import net.floodlightcontroller.util.EventHistory;
import net.floodlightcontroller.util.EventHistory.EvAction;
+import net.onrc.onos.registry.controller.IControllerRegistryService;
+import net.onrc.onos.registry.controller.IControllerRegistryService.ControlChangeCallback;
+import net.onrc.onos.registry.controller.RegistryException;
+
import org.openflow.protocol.OFMessage;
import org.openflow.protocol.OFPacketIn;
import org.openflow.protocol.OFPacketOut;
@@ -146,7 +149,7 @@
protected IStorageSourceService storageSource;
protected IThreadPoolService threadPool;
protected IRestApiService restApi;
-
+ protected IControllerRegistryService registryService;
// LLDP and BDDP fields
private static final byte[] LLDP_STANDARD_DST_MAC_STRING =
@@ -1172,6 +1175,14 @@
* @param links The List of @LinkTuple to delete.
*/
protected void deleteLinks(List<Link> links, String reason) {
+ deleteLinks(links, reason, Boolean.TRUE);
+ }
+
+ /**
+ * Removes links from memory and storage.
+ * @param links The List of @LinkTuple to delete.
+ */
+ protected void deleteLinks(List<Link> links, String reason, Boolean hasControl) {
NodePortTuple srcNpt, dstNpt;
lock.writeLock().lock();
@@ -1219,7 +1230,9 @@
removeLinkFromStorage(lt);
// remote link from network map
- linkStore.update(lt, DM_OPERATION.DELETE);
+ if (hasControl) {
+ linkStore.update(lt, DM_OPERATION.DELETE);
+ }
// TODO Whenever link is removed, it has to checked if
// the switchports must be added to quarantine.
@@ -1244,7 +1257,10 @@
IOFSwitch iofSwitch = floodlightProvider.getSwitches().get(sw);
if (iofSwitch == null) return Command.CONTINUE;
-
+
+ // If we do not control this switch, then we should not process its port status messages
+ if (!registryService.hasControl(iofSwitch.getId())) return Command.CONTINUE;
+
if (log.isTraceEnabled()) {
log.trace("handlePortStatus: Switch {} port #{} reason {}; " +
"config is {} state is {}",
@@ -1418,7 +1434,11 @@
}
// add all tuples with an endpoint on this switch to erase list
eraseList.addAll(switchLinks.get(sw));
- deleteLinks(eraseList, "Switch Removed");
+
+ // We can get called to delete links when we lose mastership. To avoid clearing the network map in that case,
+ // figure out if we have control of the switch
+ boolean hasControl = registryService.hasControl(sw);
+ deleteLinks(eraseList, "Switch Removed", hasControl);
// Send a switch removed update
LDUpdate update = new LDUpdate(sw, null, UpdateOperation.SWITCH_REMOVED);
@@ -1848,6 +1868,7 @@
l.add(IStorageSourceService.class);
l.add(IThreadPoolService.class);
l.add(IRestApiService.class);
+ l.add(IControllerRegistryService.class);
return l;
}
@@ -1858,6 +1879,7 @@
storageSource = context.getServiceImpl(IStorageSourceService.class);
threadPool = context.getServiceImpl(IThreadPoolService.class);
restApi = context.getServiceImpl(IRestApiService.class);
+ registryService = context.getServiceImpl(IControllerRegistryService.class);
// Set the autoportfast feature to false.
this.autoPortFastFeature = false;
diff --git a/src/main/java/net/floodlightcontroller/linkdiscovery/internal/LinkStorageImpl.java b/src/main/java/net/floodlightcontroller/linkdiscovery/internal/LinkStorageImpl.java
index 503f2f3..83abc8e 100644
--- a/src/main/java/net/floodlightcontroller/linkdiscovery/internal/LinkStorageImpl.java
+++ b/src/main/java/net/floodlightcontroller/linkdiscovery/internal/LinkStorageImpl.java
@@ -47,7 +47,6 @@
public void update(Link link, LinkInfo linkinfo, DM_OPERATION op) {
switch (op) {
case UPDATE:
- break;
case CREATE:
case INSERT:
addOrUpdateLink(link, linkinfo, op);
diff --git a/src/main/java/net/floodlightcontroller/linkdiscovery/internal/TopoLinkServiceImpl.java b/src/main/java/net/floodlightcontroller/linkdiscovery/internal/TopoLinkServiceImpl.java
index c452fcd..c493887 100644
--- a/src/main/java/net/floodlightcontroller/linkdiscovery/internal/TopoLinkServiceImpl.java
+++ b/src/main/java/net/floodlightcontroller/linkdiscovery/internal/TopoLinkServiceImpl.java
@@ -1,33 +1,76 @@
package net.floodlightcontroller.linkdiscovery.internal;
+import java.util.ArrayList;
import java.util.List;
+import net.floodlightcontroller.core.INetMapTopologyObjects.ISwitchObject;
import net.floodlightcontroller.core.INetMapTopologyService.ITopoLinkService;
+import net.floodlightcontroller.linkdiscovery.internal.LinkStorageImpl.ExtractLink;
import net.floodlightcontroller.routing.Link;
+import net.onrc.onos.util.GraphDBConnection;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.tinkerpop.blueprints.Vertex;
+import com.tinkerpop.gremlin.java.GremlinPipeline;
public class TopoLinkServiceImpl implements ITopoLinkService {
- ThreadLocal<LinkStorageImpl> store = new ThreadLocal<LinkStorageImpl>() {
- @Override
- protected LinkStorageImpl initialValue() {
- LinkStorageImpl inStore = new LinkStorageImpl();
- //TODO: Get the file path from global properties
- inStore.init("/tmp/cassandra.titan");
- return inStore;
- }
- };
+ public GraphDBConnection conn;
+ protected static Logger log = LoggerFactory.getLogger(TopoLinkServiceImpl.class);
+
+
+ public void finalize() {
+ close();
+ }
+
+ @Override
+ public void close() {
+ conn.close();
+ }
- LinkStorageImpl linkStore = store.get();
@Override
public List<Link> getActiveLinks() {
// TODO Auto-generated method stub
- List<Link> retval = linkStore.getActiveLinks();
- return retval;
+ conn = GraphDBConnection.getInstance("");
+ Iterable<ISwitchObject> switches = conn.utils().getActiveSwitches(conn);
+ List<Link> links = new ArrayList<Link>();
+ for (ISwitchObject sw : switches) {
+ GremlinPipeline<Vertex, Link> pipe = new GremlinPipeline<Vertex, Link>();
+ ExtractLink extractor = new ExtractLink();
+
+ pipe.start(sw.asVertex());
+ pipe.enablePath(true);
+ pipe.out("on").out("link").in("on").path().step(extractor);
+
+ while (pipe.hasNext() ) {
+ Link l = pipe.next();
+ links.add(l);
+ }
+
+ }
+ return links;
}
@Override
public List<Link> getLinksOnSwitch(String dpid) {
// TODO Auto-generated method stub
- return linkStore.getLinks(dpid);
+ List<Link> links = new ArrayList<Link>();
+ ISwitchObject sw = conn.utils().searchSwitch(conn, dpid);
+ GremlinPipeline<Vertex, Link> pipe = new GremlinPipeline<Vertex, Link>();
+ ExtractLink extractor = new ExtractLink();
+
+ pipe.start(sw.asVertex());
+ pipe.enablePath(true);
+ pipe.out("on").out("link").in("on").path().step(extractor);
+
+ while (pipe.hasNext() ) {
+ Link l = pipe.next();
+ links.add(l);
+ }
+ return links;
+
}
+
}
diff --git a/src/main/java/net/floodlightcontroller/onoslistener/OnosPublisher.java b/src/main/java/net/floodlightcontroller/onoslistener/OnosPublisher.java
index c6fe108..36cde95 100644
--- a/src/main/java/net/floodlightcontroller/onoslistener/OnosPublisher.java
+++ b/src/main/java/net/floodlightcontroller/onoslistener/OnosPublisher.java
@@ -48,7 +48,7 @@
protected static final String CleanupEnabled = "EnableCleanup";
protected IThreadPoolService threadPool;
- protected final int CLEANUP_TASK_INTERVAL = 999; // 999 ms
+ protected final int CLEANUP_TASK_INTERVAL = 10; // 10 sec
protected SingletonTask cleanupTask;
/**
@@ -65,7 +65,7 @@
log.error("Error in cleanup thread", e);
} finally {
cleanupTask.reschedule(CLEANUP_TASK_INTERVAL,
- TimeUnit.MILLISECONDS);
+ TimeUnit.SECONDS);
}
}
@@ -74,30 +74,29 @@
// TODO Auto-generated method stub
if (hasControl) {
- log.debug("got control to set inactive sw {}", dpid);
+ log.debug("got control to set inactive sw {}", HexString.toHexString(dpid));
swStore.update(HexString.toHexString(dpid),SwitchState.INACTIVE, DM_OPERATION.UPDATE);
registryService.releaseControl(dpid);
}
}
}
-
-
protected void switchCleanup() {
-
TopoSwitchServiceImpl impl = new TopoSwitchServiceImpl();
Iterable<ISwitchObject> switches = impl.getActiveSwitches();
+
+ log.debug("Checking for inactive switches");
// For each switch check if a controller exists in controller registry
for (ISwitchObject sw: switches) {
- log.debug("checking if switch is inactive: {}", sw.getDPID());
+ //log.debug("checking if switch is inactive: {}", sw.getDPID());
try {
long dpid = HexString.toLong(sw.getDPID());
String controller = registryService.getControllerForSwitch(dpid);
if (controller == null) {
- log.debug("request Control to set inactive sw {}", dpid);
+ log.debug("request Control to set inactive sw {}", HexString.toHexString(dpid));
registryService.requestControl(dpid, new SwitchCleanup());
} else {
- log.debug("sw {} is controlled by controller: {}",dpid,controller);
+ log.debug("sw {} is controlled by controller: {}",HexString.toHexString(dpid),controller);
}
} catch (NumberFormatException e) {
// TODO Auto-generated catch block
@@ -223,10 +222,10 @@
deviceService.addListener(this);
// Setup the Cleanup task.
- if (cleanupNeeded != null &&cleanupNeeded.equals("True")) {
+ if (cleanupNeeded == null || !cleanupNeeded.equals("False")) {
ScheduledExecutorService ses = threadPool.getScheduledExecutor();
cleanupTask = new SingletonTask(ses, new SwitchCleanup());
- cleanupTask.reschedule(CLEANUP_TASK_INTERVAL, TimeUnit.MILLISECONDS);
+ cleanupTask.reschedule(CLEANUP_TASK_INTERVAL, TimeUnit.SECONDS);
}
}
diff --git a/src/main/java/net/floodlightcontroller/routing/TopoRouteService.java b/src/main/java/net/floodlightcontroller/routing/TopoRouteService.java
index 94e4769..fd7c364 100644
--- a/src/main/java/net/floodlightcontroller/routing/TopoRouteService.java
+++ b/src/main/java/net/floodlightcontroller/routing/TopoRouteService.java
@@ -2,42 +2,113 @@
import java.util.ArrayList;
import java.util.Collection;
+import java.util.Collections;
import java.util.HashMap;
-import java.util.Iterator;
+import java.util.HashSet;
+import java.util.LinkedList;
import java.util.List;
import java.util.Map;
+import java.util.Queue;
+import java.util.Set;
-import net.floodlightcontroller.core.internal.SwitchStorageImpl;
+import net.floodlightcontroller.core.INetMapTopologyObjects.ISwitchObject;
+import net.floodlightcontroller.core.INetMapTopologyService.ITopoRouteService;
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.INetMapTopologyService.ITopoRouteService;
import net.floodlightcontroller.util.DataPath;
import net.floodlightcontroller.util.Dpid;
import net.floodlightcontroller.util.FlowEntry;
import net.floodlightcontroller.util.Port;
import net.floodlightcontroller.util.SwitchPort;
+import net.onrc.onos.util.GraphDBConnection;
import org.openflow.util.HexString;
-
-import com.thinkaurelius.titan.core.TitanGraph;
-import com.tinkerpop.blueprints.TransactionalGraph.Conclusion;
-import com.tinkerpop.blueprints.Vertex;
-
-import javax.script.ScriptContext;
-import javax.script.ScriptEngine;
-import javax.script.ScriptException;
-import com.tinkerpop.gremlin.groovy.jsr223.GremlinGroovyScriptEngine;
-
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import com.thinkaurelius.titan.core.TitanFactory;
+import com.thinkaurelius.titan.core.TitanGraph;
+import com.thinkaurelius.titan.core.TitanTransaction;
+import com.tinkerpop.blueprints.Direction;
+import com.tinkerpop.blueprints.TransactionalGraph.Conclusion;
+import com.tinkerpop.blueprints.Vertex;
+import com.tinkerpop.pipes.PipeFunction;
+import com.tinkerpop.pipes.branch.LoopPipe.LoopBundle;
+
+
+/**
+ * A class for storing Node and Link information for fast computation
+ * of shortest paths.
+ */
+class Node {
+ class Link {
+ public Node me; // The node this link originates from
+ public Node neighbor; // The neighbor node on the other side
+ public short myPort; // Local port number for the link
+ public short neighborPort; // Neighbor port number for the link
+
+ /**
+ * Link constructor.
+ *
+ * @param me the node this link originates from.
+ * @param the neighbor node on the other side of the link.
+ * @param myPort local port number for the link.
+ * @param neighborPort neighrobr port number for the link.
+ */
+ public Link(Node me, Node neighbor, short myPort, short neighborPort) {
+ this.me = me;
+ this.neighbor = neighbor;
+ this.myPort = myPort;
+ this.neighborPort = neighborPort;
+ }
+ };
+
+ public long nodeId; // The node ID
+ public LinkedList<Link> links; // The links originating from this node
+
+ /**
+ * Node constructor.
+ *
+ * @param nodeId the node ID.
+ */
+ public Node(long nodeId) {
+ this.nodeId = nodeId;
+ links = new LinkedList<Link>();
+ }
+
+ /**
+ * Add a neighbor.
+ *
+ * A new link to the neighbor will be created.
+ *
+ * @param neighbor the neighbor to add.
+ * @param myPort the local port number for the link to the neighbor.
+ * @param neighborPort the neighbor port number for the link.
+ */
+ public void addNeighbor(Node neighbor, short myPort, short neighborPort) {
+ Link link = new Link(this, neighbor, myPort, neighborPort);
+ links.add(link);
+ }
+};
+
+
public class TopoRouteService implements IFloodlightModule, ITopoRouteService {
/** The logger. */
private static Logger log =
LoggerFactory.getLogger(TopoRouteService.class);
+
+ 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() {
@@ -72,6 +143,7 @@
public void init(FloodlightModuleContext context)
throws FloodlightModuleException {
// TODO: Add the appropriate initialization
+ conn = GraphDBConnection.getInstance("");
}
@Override
@@ -79,18 +151,256 @@
// TODO: Add the approprate setup
}
- ThreadLocal<SwitchStorageImpl> store = new ThreadLocal<SwitchStorageImpl>() {
- @Override
- protected SwitchStorageImpl initialValue() {
- SwitchStorageImpl swStore = new SwitchStorageImpl();
- // NOTE: This is the file path from global properties
- swStore.init("/tmp/cassandra.titan");
- return swStore;
+
+ static class ShortestPathLoopFunction implements PipeFunction<LoopBundle<Vertex>, Boolean> {
+ String dpid;
+ public ShortestPathLoopFunction(String dpid) {
+ super();
+ this.dpid = dpid;
}
- };
+ public Boolean compute(LoopBundle<Vertex> bundle) {
+ Boolean output = false;
+ if (! bundle.getObject().getProperty("dpid").equals(dpid)) {
+ output = true;
+ }
+ return output;
+ }
+ }
- SwitchStorageImpl swStore = store.get();
+ /**
+ * Fetch the Switch and Ports info from the Titan Graph
+ * and store it locally for fast access during the shortest path
+ * computation.
+ *
+ * After fetching the state, method @ref getTopoShortestPath()
+ * can be used for fast shortest path computation.
+ *
+ * Note: There is certain cost to fetch the state, hence it should
+ * be used only when there is a large number of shortest path
+ * computations that need to be done on the same topology.
+ * Typically, a single call to @ref prepareShortestPathTopo()
+ * should be followed by a large number of calls to
+ * method @ref getTopoShortestPath().
+ * After the last @ref getTopoShortestPath() call,
+ * method @ref dropShortestPathTopo() should be used to release
+ * the internal state that is not needed anymore:
+ *
+ * prepareShortestPathTopo();
+ * for (int i = 0; i < 10000; i++) {
+ * dataPath = getTopoShortestPath(...);
+ * ...
+ * }
+ * dropShortestPathTopo();
+ */
+
+ public void prepareShortestPathTopo() {
+ TitanGraph titanGraph = TitanFactory.open("/tmp/cassandra.titan");
+ TitanTransaction titanTransaction = titanGraph.startTransaction();
+ shortestPathTopo = new HashMap<Long, Node>();
+ //
+ // Fetch the relevant info from the Switch and Port vertices
+ // from the Titan Graph.
+ //
+ Iterable<Vertex> nodes = titanTransaction.getVertices("type", "switch");
+ for (Vertex nodeVertex : nodes) {
+
+ //
+ // The Switch info
+ //
+ String nodeDpid = nodeVertex.getProperty("dpid").toString();
+ long nodeId = HexString.toLong(nodeDpid);
+ Node me = shortestPathTopo.get(nodeId);
+ if (me == null) {
+ me = new Node(nodeId);
+ shortestPathTopo.put(nodeId, me);
+ }
+
+ //
+ // The local Port info
+ //
+ for (Vertex myPortVertex : nodeVertex.getVertices(Direction.OUT, "on")) {
+ short myPort = 0;
+ Object obj = myPortVertex.getProperty("number");
+ if (obj instanceof Short) {
+ myPort = (Short)obj;
+ } else if (obj instanceof Integer) {
+ Integer int_nodeId = (Integer)obj;
+ myPort = int_nodeId.shortValue();
+ }
+
+ //
+ // The neighbor Port info
+ //
+ for (Vertex neighborPortVertex : myPortVertex.getVertices(Direction.OUT, "link")) {
+ short neighborPort = 0;
+ obj = neighborPortVertex.getProperty("number");
+ if (obj instanceof Short) {
+ neighborPort = (Short)obj;
+ } else if (obj instanceof Integer) {
+ Integer int_nodeId = (Integer)obj;
+ neighborPort = int_nodeId.shortValue();
+ }
+ //
+ // The neighbor Switch info
+ //
+ for (Vertex neighborVertex : neighborPortVertex.getVertices(Direction.IN, "on")) {
+ String neighborDpid = neighborVertex.getProperty("dpid").toString();
+ long neighborId = HexString.toLong(neighborDpid);
+ Node neighbor = shortestPathTopo.get(neighborId);
+ if (neighbor == null) {
+ neighbor = new Node(neighborId);
+ shortestPathTopo.put(neighborId, neighbor);
+ }
+ me.addNeighbor(neighbor, myPort, neighborPort);
+ }
+ }
+ }
+ }
+
+ titanTransaction.stopTransaction(Conclusion.SUCCESS);
+ }
+
+ /**
+ * Release the state that was populated by
+ * method @ref prepareShortestPathTopo().
+ *
+ * See the documentation for method @ref prepareShortestPathTopo()
+ * for additional information and usage.
+ */
+
+ public void dropShortestPathTopo() {
+ shortestPathTopo = null;
+ }
+
+ /**
+ * Get the shortest path from a source to a destination by
+ * using the pre-populated local topology state prepared
+ * by method @ref prepareShortestPathTopo().
+ *
+ * See the documentation for method @ref prepareShortestPathTopo()
+ * for additional information and usage.
+ *
+ * @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) {
+ DataPath result_data_path = new DataPath();
+
+ // Initialize the source and destination in the data path to return
+ result_data_path.setSrcPort(src);
+ result_data_path.setDstPort(dest);
+
+ String dpid_src = src.dpid().toString();
+ String dpid_dest = dest.dpid().toString();
+
+ // Get the source vertex
+ Node v_src = shortestPathTopo.get(src.dpid().value());
+ if (v_src == null) {
+ return null; // Source vertex not found
+ }
+
+ // Get the destination vertex
+ Node v_dest = shortestPathTopo.get(dest.dpid().value());
+ if (v_dest == null) {
+ return null; // Destination vertex not found
+ }
+
+ //
+ // Test whether we are computing a path from/to the same DPID.
+ // If "yes", then just add a single flow entry in the return result.
+ //
+ if (dpid_src.equals(dpid_dest)) {
+ FlowEntry flowEntry = new FlowEntry();
+ flowEntry.setDpid(src.dpid());
+ flowEntry.setInPort(src.port());
+ flowEntry.setOutPort(dest.port());
+ result_data_path.flowEntries().add(flowEntry);
+ return result_data_path;
+ }
+
+ //
+ // Implement the Shortest Path computation by using Breath First Search
+ //
+ Set<Node> visitedSet = new HashSet<Node>();
+ Queue<Node> processingList = new LinkedList<Node>();
+ Map<Node, Node.Link> previousVertexMap = new HashMap<Node, Node.Link>();
+ processingList.add(v_src);
+ visitedSet.add(v_src);
+ Boolean path_found = false;
+ while (! processingList.isEmpty()) {
+ Node nextVertex = processingList.poll();
+ if (v_dest == nextVertex) {
+ path_found = true;
+ break;
+ }
+ for (Node.Link link : nextVertex.links) {
+ Node child = link.neighbor;
+ if (! visitedSet.contains(child)) {
+ previousVertexMap.put(child, link);
+ visitedSet.add(child);
+ processingList.add(child);
+ }
+ }
+ }
+ if (! path_found)
+ return null; // No path found
+
+ // Collect the path as a list of links
+ List<Node.Link> resultPath = new LinkedList<Node.Link>();
+ Node previousVertex = v_dest;
+ while (! v_src.equals(previousVertex)) {
+ Node.Link currentLink = previousVertexMap.get(previousVertex);
+ resultPath.add(currentLink);
+ previousVertex = currentLink.me;
+ }
+ Collections.reverse(resultPath);
+
+ //
+ // Loop through the result and prepare the return result
+ // as a list of Flow Entries.
+ //
+ Port inPort = new Port(src.port().value());
+ Port outPort;
+ for (Node.Link link: resultPath) {
+ // Setup the outgoing port, and add the Flow Entry
+ outPort = new Port(link.neighborPort);
+
+ FlowEntry flowEntry = new FlowEntry();
+ flowEntry.setDpid(new Dpid(link.me.nodeId));
+ flowEntry.setInPort(inPort);
+ flowEntry.setOutPort(outPort);
+ result_data_path.flowEntries().add(flowEntry);
+
+ // Setup the next incoming port
+ inPort = new Port(link.neighborPort);
+ }
+ if (resultPath.size() > 0) {
+ // Add the last Flow Entry
+ FlowEntry flowEntry = new FlowEntry();
+ flowEntry.setDpid(new Dpid(dest.dpid().value()));
+ flowEntry.setInPort(inPort);
+ flowEntry.setOutPort(dest.port());
+ result_data_path.flowEntries().add(flowEntry);
+ }
+
+ if (result_data_path.flowEntries().size() > 0)
+ return result_data_path;
+
+ return null;
+ }
+
+ /**
+ * Get the shortest path from a source to a destination.
+ *
+ * @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.
+ */
@Override
public DataPath getShortestPath(SwitchPort src, SwitchPort dest) {
DataPath result_data_path = new DataPath();
@@ -99,43 +409,16 @@
result_data_path.setSrcPort(src);
result_data_path.setDstPort(dest);
- TitanGraph titanGraph = swStore.graph;
+ TitanGraph titanGraph = TitanFactory.open("/tmp/cassandra.titan");
+ TitanTransaction titanTransaction = titanGraph.startTransaction();
String dpid_src = src.dpid().toString();
String dpid_dest = dest.dpid().toString();
- //
- // Implement the Shortest Path between two vertices by using
- // the following Gremlin CLI code:
- // v_src.as("x").out("on").out("link").in("on").dedup().loop("x"){it.object.dpid != v_dest.dpid}.path(){it.dpid}{it.number}{it.number}
- // The equivalent code used here is:
- // results = []; v_src.as("x").out("on").out("link").in("on").dedup().loop("x"){it.object.dpid != v_dest.dpid}.path().fill(results)
- //
-
- String gremlin = "v_src.as(\"x\").out(\"on\").out(\"link\").in(\"on\").dedup().loop(\"x\"){it.object.dpid != v_dest.dpid}.path().fill(results)";
-
- // Get the source vertex
- Iterator<Vertex> iter = titanGraph.getVertices("dpid", dpid_src).iterator();
- if (! iter.hasNext()) {
- // titanGraph.stopTransaction(Conclusion.SUCCESS);
- return null; // Source vertex not found
- }
- Vertex v_src = iter.next();
-
- // Get the destination vertex
- iter = titanGraph.getVertices("dpid", dpid_dest).iterator();
- if (! iter.hasNext()) {
- // titanGraph.stopTransaction(Conclusion.SUCCESS);
- return null; // Destination vertex not found
- }
- Vertex v_dest = iter.next();
//
// Test whether we are computing a path from/to the same DPID.
// If "yes", then just add a single flow entry in the return result.
- // NOTE: The return value will change in the future to return
- // a single hop/entry instead of two. Currently, we need
- // both entries to capture the source and destination ports.
//
if (dpid_src.equals(dpid_dest)) {
FlowEntry flowEntry = new FlowEntry();
@@ -143,106 +426,151 @@
flowEntry.setInPort(src.port());
flowEntry.setOutPort(dest.port());
result_data_path.flowEntries().add(flowEntry);
- // titanGraph.stopTransaction(Conclusion.SUCCESS);
+ titanTransaction.stopTransaction(Conclusion.SUCCESS);
+ // titanTransaction.shutdown();
return result_data_path;
}
- //
- // Implement the Gremlin script and run it
- //
- ScriptEngine engine = new GremlinGroovyScriptEngine();
- ArrayList<ArrayList<Vertex>> results = new ArrayList<ArrayList<Vertex>>();
- engine.getBindings(ScriptContext.ENGINE_SCOPE).put("g", titanGraph);
- engine.getBindings(ScriptContext.ENGINE_SCOPE).put("v_src", v_src);
- engine.getBindings(ScriptContext.ENGINE_SCOPE).put("v_dest", v_dest);
- engine.getBindings(ScriptContext.ENGINE_SCOPE).put("results", results);
+ // Get the source vertex
- try {
- engine.eval(gremlin);
- } catch (ScriptException e) {
- System.err.println("Caught ScriptException running Gremlin script: " + e.getMessage());
- // titanGraph.stopTransaction(Conclusion.SUCCESS);
- return null;
+ ISwitchObject srcSwitch = conn.utils().searchSwitch(conn, dpid_src);
+ ISwitchObject destSwitch = conn.utils().searchSwitch(conn, dpid_dest);
+
+ if (srcSwitch == null || destSwitch == null) {
+ return null;
}
+
+ Vertex v_src = srcSwitch.asVertex();
+ Vertex v_dest = destSwitch.asVertex();
+
//
- // Loop through the result and collect the list
- // of <dpid, port> tuples.
+ // Implement the Shortest Path computation by using Breath First Search
+ //
+ Set<Vertex> visitedSet = new HashSet<Vertex>();
+ Queue<Vertex> processingList = new LinkedList<Vertex>();
+ Map<Vertex, Vertex> previousVertexMap = new HashMap<Vertex, Vertex>();
+
+ processingList.add(v_src);
+ visitedSet.add(v_src);
+ Boolean path_found = false;
+ while (! processingList.isEmpty()) {
+ Vertex nextVertex = processingList.poll();
+ if (v_dest.equals(nextVertex)) {
+ path_found = true;
+ break;
+ }
+ for (Vertex parentPort : nextVertex.getVertices(Direction.OUT, "on")) {
+ for (Vertex childPort : parentPort.getVertices(Direction.OUT, "link")) {
+ for (Vertex child : childPort.getVertices(Direction.IN, "on")) {
+ if (! visitedSet.contains(child)) {
+ previousVertexMap.put(parentPort, nextVertex);
+ previousVertexMap.put(childPort, parentPort);
+ previousVertexMap.put(child, childPort);
+ visitedSet.add(child);
+ processingList.add(child);
+ }
+ }
+ }
+ }
+ }
+ if (! path_found)
+ return null; // No path found
+
+ List<Vertex> resultPath = new LinkedList<Vertex>();
+ Vertex previousVertex = v_dest;
+ resultPath.add(v_dest);
+ while (! v_src.equals(previousVertex)) {
+ Vertex currentVertex = previousVertexMap.get(previousVertex);
+ resultPath.add(currentVertex);
+ previousVertex = currentVertex;
+ }
+ Collections.reverse(resultPath);
+
+
+
+ //
+ // Loop through the result and prepare the return result
+ // as a list of Flow Entries.
//
long nodeId = 0;
short portId = 0;
Port inPort = new Port(src.port().value());
Port outPort = new Port();
- for (ArrayList<Vertex> lv : results) {
- int idx = 0;
- for (Vertex v: lv) {
- String type = v.getProperty("type").toString();
- System.out.println("type: " + type);
- if (type.equals("port")) {
- String number = v.getProperty("number").toString();
- System.out.println("number: " + number);
+ int idx = 0;
+ for (Vertex v: resultPath) {
+ String type = v.getProperty("type").toString();
+ // System.out.println("type: " + type);
+ if (type.equals("port")) {
+ String number = v.getProperty("number").toString();
+ // System.out.println("number: " + number);
- Object obj = v.getProperty("number");
- // String class_str = obj.getClass().toString();
- if (obj instanceof Short) {
- portId = (Short)obj;
- } else if (obj instanceof Integer) {
- Integer int_nodeId = (Integer)obj;
- portId = int_nodeId.shortValue();
- // int int_nodeId = (Integer)obj;
- // portId = (short)int_nodeId.;
- }
- } else if (type.equals("switch")) {
- String dpid = v.getProperty("dpid").toString();
- nodeId = HexString.toLong(dpid);
+ Object obj = v.getProperty("number");
+ // String class_str = obj.getClass().toString();
+ if (obj instanceof Short) {
+ portId = (Short)obj;
+ } else if (obj instanceof Integer) {
+ Integer int_nodeId = (Integer)obj;
+ portId = int_nodeId.shortValue();
+ // int int_nodeId = (Integer)obj;
+ // portId = (short)int_nodeId.;
+ }
+ } else if (type.equals("switch")) {
+ String dpid = v.getProperty("dpid").toString();
+ nodeId = HexString.toLong(dpid);
- System.out.println("dpid: " + dpid);
- }
- idx++;
- if (idx == 1) {
- continue;
- }
- int mod = idx % 3;
- if (mod == 0) {
- // Setup the incoming port
- inPort = new Port(portId);
- continue;
- }
- if (mod == 2) {
- // Setup the outgoing port, and add the Flow Entry
- outPort = new Port(portId);
-
- FlowEntry flowEntry = new FlowEntry();
- flowEntry.setDpid(new Dpid(nodeId));
- flowEntry.setInPort(inPort);
- flowEntry.setOutPort(outPort);
- result_data_path.flowEntries().add(flowEntry);
- continue;
- }
+ // System.out.println("dpid: " + dpid);
}
+ idx++;
+ if (idx == 1) {
+ continue;
+ }
+ int mod = idx % 3;
+ if (mod == 0) {
+ // Setup the incoming port
+ inPort = new Port(portId);
+ continue;
+ }
+ if (mod == 2) {
+ // Setup the outgoing port, and add the Flow Entry
+ outPort = new Port(portId);
- if (idx > 0) {
- // Add the last Flow Entry
FlowEntry flowEntry = new FlowEntry();
flowEntry.setDpid(new Dpid(nodeId));
flowEntry.setInPort(inPort);
- flowEntry.setOutPort(dest.port());
+ flowEntry.setOutPort(outPort);
result_data_path.flowEntries().add(flowEntry);
+ continue;
}
}
- // titanGraph.stopTransaction(Conclusion.SUCCESS);
+ if (idx > 0) {
+ // Add the last Flow Entry
+ FlowEntry flowEntry = new FlowEntry();
+ flowEntry.setDpid(new Dpid(nodeId));
+ flowEntry.setInPort(inPort);
+ flowEntry.setOutPort(dest.port());
+ result_data_path.flowEntries().add(flowEntry);
+ }
+
+ titanTransaction.stopTransaction(Conclusion.SUCCESS);
+ // titanTransaction.shutdown();
if (result_data_path.flowEntries().size() > 0)
return result_data_path;
return null;
}
+ /**
+ * Test whether a route exists from a source to a destination.
+ *
+ * @param src the source node for the test.
+ * @param dest the destination node for the test.
+ * @return true if a route exists, otherwise false.
+ */
@Override
public Boolean routeExists(SwitchPort src, SwitchPort dest) {
DataPath dataPath = getShortestPath(src, dest);
- if (dataPath != null)
- return true;
- return false;
+ return (dataPath != null);
}
}
diff --git a/src/main/java/net/floodlightcontroller/util/FlowEntry.java b/src/main/java/net/floodlightcontroller/util/FlowEntry.java
index 64c32b4..717be4e 100644
--- a/src/main/java/net/floodlightcontroller/util/FlowEntry.java
+++ b/src/main/java/net/floodlightcontroller/util/FlowEntry.java
@@ -26,8 +26,12 @@
private FlowEntryMatch flowEntryMatch; // The Flow Entry Match
private ArrayList<FlowEntryAction> flowEntryActions; // The Flow Entry Actions
private Dpid dpid; // The Switch DPID
- private Port inPort; // The Switch incoming port
- private Port outPort; // The Switch outgoing port
+ private Port inPort; // The Switch incoming port. Used only
+ // when the entry is used to return
+ // Shortest Path computation.
+ private Port outPort; // The Switch outgoing port. Used only
+ // when the entry is used to return
+ // Shortest Path computation.
private FlowEntryUserState flowEntryUserState; // The Flow Entry User state
private FlowEntrySwitchState flowEntrySwitchState; // The Flow Entry Switch state
// The Flow Entry Error state (if FlowEntrySwitchState is FE_SWITCH_FAILED)
diff --git a/src/main/java/net/floodlightcontroller/util/FlowPath.java b/src/main/java/net/floodlightcontroller/util/FlowPath.java
index 11f23fe..7fcb2e6 100644
--- a/src/main/java/net/floodlightcontroller/util/FlowPath.java
+++ b/src/main/java/net/floodlightcontroller/util/FlowPath.java
@@ -9,7 +9,7 @@
/**
* The class representing the Flow Path.
*/
-public class FlowPath {
+public class FlowPath implements Comparable<FlowPath> {
private FlowId flowId; // The Flow ID
private CallerId installerId; // The Caller ID of the path installer
private DataPath dataPath; // The data path
@@ -91,4 +91,13 @@
ret += "]";
return ret;
}
+
+ /**
+ * CompareTo method to order flowPath by Id
+ */
+ @Override
+ public int compareTo(FlowPath f) {
+ return (int) (this.flowId.value() - f.flowId.value());
+ }
+
}
diff --git a/src/main/java/net/onrc/onos/registry/controller/SwitchRegistryResource.java b/src/main/java/net/onrc/onos/registry/controller/SwitchRegistryResource.java
index d6f3dee..0a7ac5d 100644
--- a/src/main/java/net/onrc/onos/registry/controller/SwitchRegistryResource.java
+++ b/src/main/java/net/onrc/onos/registry/controller/SwitchRegistryResource.java
@@ -26,11 +26,11 @@
switches = new HashMap<String, List<ControllerRegistryEntry>>();
}
- for (List<ControllerRegistryEntry> list: switches.values()){
+ /*for (List<ControllerRegistryEntry> list: switches.values()){
for (ControllerRegistryEntry en : list) {
log.debug("Controller id {}", en.getControllerId());
}
- }
+ }*/
return switches;
}
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 5f6ef78..b666db7 100644
--- a/src/main/java/net/onrc/onos/registry/controller/ZookeeperRegistry.java
+++ b/src/main/java/net/onrc/onos/registry/controller/ZookeeperRegistry.java
@@ -111,7 +111,7 @@
@Override
public void childEvent(CuratorFramework client,
PathChildrenCacheEvent event) throws Exception {
- log.debug("Root switch path cache got {} event", event.getType());
+ //log.debug("Root switch path cache got {} event", event.getType());
String strSwitch = null;
if (event.getData() != null){
@@ -158,7 +158,7 @@
if (switches.get(dpidStr) != null){
log.debug("Already contesting {}, returning", HexString.toHexString(dpid));
- return;
+ throw new RegistryException("Already contesting control for " + dpidStr);
}
LeaderLatch latch = new LeaderLatch(client, latchPath, controllerId);
@@ -355,7 +355,8 @@
new ArrayList<ControllerRegistryEntry>();
if (entry.getValue().getCurrentData().size() < 1){
- log.info("Switch entry with no leader elections: {}", entry.getKey());
+ //TODO prevent even having the PathChildrenCache in this case
+ //log.info("Switch entry with no leader elections: {}", entry.getKey());
continue;
}
diff --git a/src/main/java/net/onrc/onos/util/GraphDBConnection.java b/src/main/java/net/onrc/onos/util/GraphDBConnection.java
index 724095b..ee50cd0 100644
--- a/src/main/java/net/onrc/onos/util/GraphDBConnection.java
+++ b/src/main/java/net/onrc/onos/util/GraphDBConnection.java
@@ -85,7 +85,7 @@
}
public void close() {
-
+ graph.stopTransaction(Conclusion.SUCCESS);
}
}
diff --git a/src/main/java/net/onrc/onos/util/GraphDBUtils.java b/src/main/java/net/onrc/onos/util/GraphDBUtils.java
index 097cfa0..ba48103 100644
--- a/src/main/java/net/onrc/onos/util/GraphDBUtils.java
+++ b/src/main/java/net/onrc/onos/util/GraphDBUtils.java
@@ -1,5 +1,8 @@
package net.onrc.onos.util;
+import java.util.ArrayList;
+import java.util.List;
+
import com.thinkaurelius.titan.core.TitanGraph;
import com.tinkerpop.blueprints.Vertex;
import com.tinkerpop.frames.FramedGraph;
@@ -11,12 +14,26 @@
import net.floodlightcontroller.core.INetMapTopologyObjects.IFlowPath;
import net.floodlightcontroller.core.INetMapTopologyObjects.IPortObject;
import net.floodlightcontroller.core.INetMapTopologyObjects.ISwitchObject;
+import net.floodlightcontroller.core.ISwitchStorage.SwitchState;
import net.floodlightcontroller.util.FlowEntryId;
import net.floodlightcontroller.util.FlowId;
public class GraphDBUtils implements IDBUtils {
+
+ @Override
+ public ISwitchObject newSwitch(GraphDBConnection conn) {
+ FramedGraph<TitanGraph> fg = conn.getFramedGraph();
+ ISwitchObject obj = fg.addVertex(null,ISwitchObject.class);
+ return obj;
+ }
@Override
+ public void removeSwitch(GraphDBConnection conn, ISwitchObject sw) {
+ FramedGraph<TitanGraph> fg = conn.getFramedGraph();
+ fg.removeVertex(sw.asVertex());
+ }
+
+ @Override
public ISwitchObject searchSwitch(GraphDBConnection conn, String dpid) {
// TODO Auto-generated method stub
FramedGraph<TitanGraph> fg = conn.getFramedGraph();
@@ -46,11 +63,24 @@
}
@Override
+ public IPortObject newPort(GraphDBConnection conn) {
+ FramedGraph<TitanGraph> fg = conn.getFramedGraph();
+ IPortObject obj = fg.addVertex(null,IPortObject.class);
+ return obj;
+ }
+
+ @Override
public IDeviceObject newDevice(GraphDBConnection conn) {
FramedGraph<TitanGraph> fg = conn.getFramedGraph();
IDeviceObject obj = fg.addVertex(null,IDeviceObject.class);
return obj;
}
+
+ @Override
+ public void removePort(GraphDBConnection conn, IPortObject port) {
+ FramedGraph<TitanGraph> fg = conn.getFramedGraph();
+ fg.removeVertex(port.asVertex());
+ }
@Override
public void removeDevice(GraphDBConnection conn, IDeviceObject dev) {
@@ -136,4 +166,39 @@
return fg.getVertices("type", "flow_entry", IFlowEntry.class);
}
+
+ @Override
+ public Iterable<ISwitchObject> getActiveSwitches(GraphDBConnection conn) {
+ FramedGraph<TitanGraph> fg = conn.getFramedGraph();
+ Iterable<ISwitchObject> switches = fg.getVertices("type","switch",ISwitchObject.class);
+ List<ISwitchObject> activeSwitches = new ArrayList<ISwitchObject>();
+
+ for (ISwitchObject sw: switches) {
+ if(sw.getState().equals(SwitchState.ACTIVE.toString())) {
+ activeSwitches.add(sw);
+ }
+ }
+ return activeSwitches;
+ }
+
+ @Override
+ public Iterable<ISwitchObject> getAllSwitches(GraphDBConnection conn) {
+ FramedGraph<TitanGraph> fg = conn.getFramedGraph();
+ Iterable<ISwitchObject> switches = fg.getVertices("type","switch",ISwitchObject.class);
+ return switches;
+ }
+
+ @Override
+ public Iterable<ISwitchObject> getInactiveSwitches(GraphDBConnection conn) {
+ FramedGraph<TitanGraph> fg = conn.getFramedGraph();
+ Iterable<ISwitchObject> switches = fg.getVertices("type","switch",ISwitchObject.class);
+ List<ISwitchObject> inactiveSwitches = new ArrayList<ISwitchObject>();
+
+ for (ISwitchObject sw: switches) {
+ if(sw.getState().equals(SwitchState.INACTIVE.toString())) {
+ inactiveSwitches.add(sw);
+ }
+ }
+ return inactiveSwitches;
+ }
}
diff --git a/src/main/java/net/onrc/onos/util/IDBUtils.java b/src/main/java/net/onrc/onos/util/IDBUtils.java
index 48d5946..864e227 100644
--- a/src/main/java/net/onrc/onos/util/IDBUtils.java
+++ b/src/main/java/net/onrc/onos/util/IDBUtils.java
@@ -10,6 +10,11 @@
public interface IDBUtils {
public ISwitchObject searchSwitch(GraphDBConnection conn, String dpid);
+ public Iterable<ISwitchObject> getActiveSwitches(GraphDBConnection conn);
+ public Iterable<ISwitchObject> getAllSwitches(GraphDBConnection conn);
+ public Iterable<ISwitchObject> getInactiveSwitches(GraphDBConnection conn);
+
+
public IDeviceObject searchDevice(GraphDBConnection conn, String macAddr);
public IDeviceObject newDevice(GraphDBConnection conn);
public void removeDevice(GraphDBConnection conn, IDeviceObject dev);
@@ -27,4 +32,8 @@
public void removeFlowEntry(GraphDBConnection conn,
IFlowEntry flowEntry);
public Iterable<IFlowEntry> getAllFlowEntries(GraphDBConnection conn);
+ public IPortObject newPort(GraphDBConnection conn);
+ ISwitchObject newSwitch(GraphDBConnection conn);
+ void removePort(GraphDBConnection conn, IPortObject port);
+ void removeSwitch(GraphDBConnection conn, ISwitchObject sw);
}
diff --git a/src/main/resources/floodlightdefault.properties b/src/main/resources/floodlightdefault.properties
index 498fce5..9e4fc02 100644
--- a/src/main/resources/floodlightdefault.properties
+++ b/src/main/resources/floodlightdefault.properties
@@ -2,9 +2,6 @@
net.floodlightcontroller.core.FloodlightProvider,\
net.floodlightcontroller.threadpool.ThreadPool,\
net.floodlightcontroller.devicemanager.internal.DeviceManagerImpl,\
-net.floodlightcontroller.staticflowentry.StaticFlowEntryPusher,\
-net.floodlightcontroller.firewall.Firewall,\
-net.floodlightcontroller.forwarding.Forwarding,\
net.floodlightcontroller.jython.JythonDebugInterface,\
net.floodlightcontroller.counter.CounterStore,\
net.floodlightcontroller.perfmon.PktInProcessingTime,\
@@ -17,3 +14,4 @@
net.floodlightcontroller.forwarding.Forwarding.idletimeout = 5
net.floodlightcontroller.forwarding.Forwarding.hardtimeout = 0
net.floodlightcontroller.onoslistener.OnosPublisher.dbconf = /tmp/cassandra.titan
+net.floodlightcontroller.onoslistener.OnosPublisher.EnableCleanup = True
diff --git a/src/test/java/net/floodlightcontroller/core/internal/SwitchStorageImplTest.java b/src/test/java/net/floodlightcontroller/core/internal/SwitchStorageImplTest.java
index b4a044f..a187d4c 100644
--- a/src/test/java/net/floodlightcontroller/core/internal/SwitchStorageImplTest.java
+++ b/src/test/java/net/floodlightcontroller/core/internal/SwitchStorageImplTest.java
@@ -5,6 +5,8 @@
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
+import java.util.Collection;
+import java.util.List;
import java.util.ArrayList;
import java.util.Iterator;
@@ -39,7 +41,7 @@
titanGraph = TestDatabaseManager.getTestDatabase();
TestDatabaseManager.populateTestData(titanGraph);
- switchStorage = new TestableSwitchStorageImpl(titanGraph);
+ switchStorage = new TestableSwitchStorageImpl();
}
@After
@@ -161,7 +163,7 @@
}
public Boolean compute(LoopBundle<Vertex> bundle) {
Boolean output = false;
- if (bundle.getObject().getProperty("dpid") != dpid) {
+ if (! bundle.getObject().getProperty("dpid").equals(dpid)) {
output = true;
}
return output;
@@ -181,10 +183,6 @@
// results = []; v_src.as("x").out("on").out("link").in("on").dedup().loop("x"){it.object.dpid != v_dest.dpid}.path().fill(results)
//
- String gremlin = "v_src.as(\"x\").out(\"on\").out(\"link\").in(\"on\").dedup().loop(\"x\"){it.object.dpid != v_dest.dpid}.path().fill(results)";
-
- String gremlin_nopath = "v_src.as(\"x\").out(\"on\").out(\"link\").in(\"on\").dedup().loop(\"x\"){it.object.dpid != \"NO-SUCH-DPID\"}.path().fill(results)";
-
// Get the source vertex
Iterator<Vertex> iter = titanGraph.getVertices("dpid", dpid_src).iterator();
if (! iter.hasNext())
@@ -196,12 +194,19 @@
if (! iter.hasNext())
return; // Destination vertex not found
Vertex v_dest = iter.next();
-
+
//
// Implement the Gremlin script and run it
//
- ScriptEngine engine = new GremlinGroovyScriptEngine();
+ // NOTE: This mechanism is slower. The code is kept here
+ // for future reference.
+ //
+ /*
+ String gremlin = "v_src.as(\"x\").out(\"on\").out(\"link\").in(\"on\").dedup().loop(\"x\"){it.object.dpid != v_dest.dpid}.path().fill(results)";
+ String gremlin_nopath = "v_src.as(\"x\").out(\"on\").out(\"link\").in(\"on\").dedup().loop(\"x\"){it.object.dpid != \"NO-SUCH-DPID\"}.path().fill(results)";
+
+ ScriptEngine engine = new GremlinGroovyScriptEngine();
ArrayList<ArrayList<Vertex>> results = new ArrayList<ArrayList<Vertex>>();
engine.getBindings(ScriptContext.ENGINE_SCOPE).put("g", titanGraph);
engine.getBindings(ScriptContext.ENGINE_SCOPE).put("v_src", v_src);
@@ -215,14 +220,26 @@
return;
}
+ for (ArrayList<Vertex> lv : results) {
+ ...
+ }
+ */
+
+ MyLoopFunction whileFunction = new MyLoopFunction(dpid_dest);
+ GremlinPipeline<Vertex, Vertex> pipe = new GremlinPipeline<Vertex, Vertex>();
+ Collection<List> results = new ArrayList<List>();
+ GremlinPipeline<Vertex, List> path;
+ path = pipe.start(v_src).as("x").out("on").out("link").in("on").dedup().loop("x", whileFunction).path();
+ path.fill(results);
+
//
// Extract the result and compose it into a string
//
String results_str = "";
// System.out.println("BEGIN " + results.size());
- for (ArrayList<Vertex> lv : results) {
- // System.out.println(lv);
- for (Vertex v: lv) {
+ for (List l : results) {
+ for (Object o: l) {
+ Vertex v = (Vertex)(o);
// System.out.println(v);
String type = v.getProperty("type").toString();
results_str += "[type: " + type;
@@ -241,35 +258,21 @@
}
// System.out.println("END\n");
System.out.println(results_str);
-
+
+ //
+ // Check the result
+ //
String expected_result = "[type: switch dpid: 00:00:00:00:00:00:0a:01][type: port number: 2][type: port number: 1][type: switch dpid: 00:00:00:00:00:00:0a:03][type: port number: 2][type: port number: 2][type: switch dpid: 00:00:00:00:00:00:0a:04][type: port number: 3][type: port number: 1][type: switch dpid: 00:00:00:00:00:00:0a:06]";
-
- // Pipe<Vertex, Vertex> pipe = Gremlin.compile(gremlin);
- // pipe.setStarts(new SingleIterator<Vertex>(v1));
-
- //
- // XXX: An alternative (faster?) solution that fails to compile
- //
- // MyLoopFunction whileFunction = new MyLoopFunction(dpid_dest);
- // GremlinPipeline<Vertex, Vertex> pipe = new GremlinPipeline<Vertex, Vertex>();
- // ArrayList<ArrayList<Vertex>> results2 = new ArrayList<ArrayList<Vertex>>();
- // TODO: The statement below doesn't compile
- // pipe.start(v_src).as("x").out("on").out("link").in("on").dedup().loop("x", whileFunction).path().fill(results2);
-
- // Check the result
assertEquals(results_str, expected_result);
//
// Test Shortest-Path computation to non-existing destination
//
results.clear();
- try {
- engine.eval(gremlin_nopath);
- } catch (ScriptException e) {
- System.err.println("Caught ScriptException running Gremlin script: " + e.getMessage());
- return;
- }
+ MyLoopFunction noDestWhileFunction = new MyLoopFunction("NO-SUCH-DPID");
+ path = pipe.start(v_src).as("x").out("on").out("link").in("on").dedup().loop("x", noDestWhileFunction).path();
+ path.fill(results);
assertTrue(results.size() == 0);
}
}
diff --git a/src/test/java/net/floodlightcontroller/core/internal/TestableSwitchStorageImpl.java b/src/test/java/net/floodlightcontroller/core/internal/TestableSwitchStorageImpl.java
index 0d429e6..73d517f 100644
--- a/src/test/java/net/floodlightcontroller/core/internal/TestableSwitchStorageImpl.java
+++ b/src/test/java/net/floodlightcontroller/core/internal/TestableSwitchStorageImpl.java
@@ -18,21 +18,13 @@
public class TestableSwitchStorageImpl extends SwitchStorageImpl {
- public TestableSwitchStorageImpl(TitanGraph graph){
- this.graph = graph;
+ public TestableSwitchStorageImpl(){
}
@Override
public void init(String conf){
- Set<String> s = graph.getIndexedKeys(Vertex.class);
- if (!s.contains("dpid")) {
- graph.createKeyIndex("dpid", Vertex.class);
- graph.stopTransaction(Conclusion.SUCCESS);
- }
- if (!s.contains("type")) {
- graph.createKeyIndex("type", Vertex.class);
- graph.stopTransaction(Conclusion.SUCCESS);
- }
+
+ super.init(conf);
}
}
diff --git a/start-onos.sh b/start-onos.sh
index 166ce74..b8e89e2 100755
--- a/start-onos.sh
+++ b/start-onos.sh
@@ -3,6 +3,7 @@
# Set paths
FL_HOME=`dirname $0`
FL_JAR="${FL_HOME}/target/floodlight.jar"
+FL_ONLY_JAR="${FL_HOME}/target/floodlight-only.jar"
FL_LOGBACK="${FL_HOME}/logback.xml"
LOGDIR=${FL_HOME}/onos-logs
FL_LOG="${LOGDIR}/onos.`hostname`.log"
@@ -19,33 +20,9 @@
#JVM_OPTS="$JVM_OPTS -Dpython.security.respectJavaAccessibility=false"
# Set classpath to include titan libs
-CLASSPATH=`echo ${FL_HOME}/lib/*.jar ${FL_HOME}/lib/titan/*.jar | sed 's/ /:/g'`
-
-# Create a logback file if required
-cat <<EOF_LOGBACK >${FL_LOGBACK}
-<configuration scan="true" debug="true">
-<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
-<encoder>
-<pattern>%level [%logger:%thread] %msg%n</pattern>
-</encoder>
-</appender>
-
-<appender name="FILE" class="ch.qos.logback.core.FileAppender">
-<file>${FL_LOG}</file>
-<encoder>
-<pattern>%date %level [%thread] %logger{10} [%file:%line] %msg%n</pattern>
-</encoder>
-</appender>
-
-<logger name="org" level="WARN"/>
-<logger name="LogService" level="WARN"/> <!-- Restlet access logging -->
-<logger name="net.floodlightcontroller.logging" level="WARN"/>
-
-<root level="DEBUG">
-<appender-ref ref="FILE" />
-</root>
-</configuration>
-EOF_LOGBACK
+#CLASSPATH=`echo ${FL_HOME}/lib/*.jar ${FL_HOME}/lib/titan/*.jar | sed 's/ /:/g'`
+CLASSPATH="${FL_ONLY_JAR}:${FL_HOME}/lib/*:${FL_HOME}/lib/titan/*"
+MAIN_CLASS="net.floodlightcontroller.core.Main"
#<logger name="net.floodlightcontroller.linkdiscovery.internal" level="TRACE"/>
#<appender-ref ref="STDOUT" />
@@ -75,10 +52,39 @@
fi
done
+# Create a logback file if required
+ cat <<EOF_LOGBACK >${FL_LOGBACK}
+<configuration scan="true" debug="true">
+<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
+<encoder>
+<pattern>%level [%logger:%thread] %msg%n</pattern>
+</encoder>
+</appender>
+
+<appender name="FILE" class="ch.qos.logback.core.FileAppender">
+<file>${FL_LOG}</file>
+<encoder>
+<pattern>%date %level [%thread] %logger{10} [%file:%line] %msg%n</pattern>
+</encoder>
+</appender>
+
+<logger name="org" level="WARN"/>
+<logger name="LogService" level="WARN"/> <!-- Restlet access logging -->
+<logger name="net.floodlightcontroller.logging" level="WARN"/>
+
+<root level="DEBUG">
+<appender-ref ref="FILE" />
+</root>
+</configuration>
+EOF_LOGBACK
+
# Run floodlight
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} -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 &
+
+
# 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
}
diff --git a/start-rest.sh b/start-rest.sh
index 1f801fe..3006e5e 100755
--- a/start-rest.sh
+++ b/start-rest.sh
@@ -52,6 +52,7 @@
case "$1" in
start)
stop
+ sleep 2
start
;;
stop)
diff --git a/web/clear_flow.py b/web/clear_flow.py
index 2646498..df6302e 100755
--- a/web/clear_flow.py
+++ b/web/clear_flow.py
@@ -45,7 +45,7 @@
if __name__ == "__main__":
usage_msg = "Clear flow state from the ONOS Network Map\n"
- usage_msg = usage_msg + "Usage: %s <flow_id>\n" % (sys.argv[0])
+ usage_msg = usage_msg + "Usage: %s <begin-flow-id> [<end-flow-id>]\n" % (sys.argv[0])
# app.debug = False;
@@ -58,7 +58,14 @@
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_arg = int(sys.argv[1], 0)
- clear_flow_path(flow_id_arg);
+ 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 f8e037f..6d26548 100755
--- a/web/delete_flow.py
+++ b/web/delete_flow.py
@@ -45,7 +45,7 @@
if __name__ == "__main__":
usage_msg = "Delete flow state from the ONOS Network Map and the switches\n"
- usage_msg = usage_msg + "Usage: %s <flow_id>\n" % (sys.argv[0])
+ usage_msg = usage_msg + "Usage: %s <begin-flow-id> [<end-flow-id>]\n" % (sys.argv[0])
# app.debug = False;
@@ -58,7 +58,14 @@
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_arg = int(sys.argv[1], 0)
- delete_flow_path(flow_id_arg);
+ 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/generate_flows.py b/web/generate_flows.py
new file mode 100755
index 0000000..953fc03
--- /dev/null
+++ b/web/generate_flows.py
@@ -0,0 +1,81 @@
+#! /usr/bin/env python
+# -*- Mode: python; py-indent-offset: 4; tab-width: 8; indent-tabs-mode: t; -*-
+
+#
+# A script for generating a number of flows.
+#
+# The output of the script should be saved to a file, and the flows from
+# that file should be added by the following command:
+#
+# web/add_flow.py -f filename
+#
+# NOTE: Currently, some of the parameters fo the flows are hard-coded,
+# and all flows are between same source and destination DPID and ports
+# (differentiated by different matchSrcMac and matchDstMac).
+#
+
+import copy
+import pprint
+import os
+import sys
+import subprocess
+import json
+import argparse
+import io
+import time
+
+## Global Var ##
+
+DEBUG=0
+pp = pprint.PrettyPrinter(indent=4)
+
+## Worker Functions ##
+def log_error(txt):
+ print '%s' % (txt)
+
+def debug(txt):
+ if DEBUG:
+ print '%s' % (txt)
+
+
+if __name__ == "__main__":
+ usage_msg = "Usage: %s <begin-flow-id> <end-flow-id>\n" % (sys.argv[0])
+
+ # app.debug = False;
+
+ # Usage info
+ if len(sys.argv) > 1 and (sys.argv[1] == "-h" or sys.argv[1] == "--help"):
+ print(usage_msg)
+ exit(0)
+
+ # Check arguments
+ if len(sys.argv) < 3:
+ log_error(usage_msg)
+ exit(1)
+
+ # Extract the arguments
+ begin_flow_id = int(sys.argv[1], 0)
+ end_flow_id = int(sys.argv[2], 0)
+ if begin_flow_id > end_flow_id:
+ log_error(usage_msg)
+ exit(1)
+
+ #
+ # Do the work
+ #
+ # NOTE: Currently, up to 65536 flows are supported.
+ # More flows can be supported by iterating by, say, iterating over some of
+ # the other bytes of the autogenereated source/destination MAC addresses.
+ #
+ flow_id = begin_flow_id
+ idx = 0
+ while flow_id <= end_flow_id:
+ mac3 = idx / 255
+ mac4 = idx % 255
+ str_mac3 = "%0.2x" % mac3
+ str_mac4 = "%0.2x" % mac4
+ src_mac = "00:00:" + str_mac3 + ":" + str_mac4 + ":00:00";
+ dst_mac = "00:01:" + str_mac3 + ":" + str_mac4 + ":00:00";
+ print "%s FOOBAR 00:00:00:00:00:00:01:01 1 00:00:00:00:00:00:01:0b 1 matchSrcMac %s matchDstMac %s" % (flow_id, src_mac, dst_mac)
+ flow_id = flow_id + 1
+ idx = idx + 1
diff --git a/web/js/onos-topology.js b/web/js/onos-topology.js
index 6905c3a..cea8f1a 100644
--- a/web/js/onos-topology.js
+++ b/web/js/onos-topology.js
@@ -250,7 +250,7 @@
.attr("y", ".31em")
.text(function(d) {
l=d.name.split(":").length
- return d.name.split(":")[l-3] + ":" + d.name.split(":")[l-2] + ":" + d.name.split(":")[l-1]
+ return d.name.split(":")[l-2] + ":" + d.name.split(":")[l-1]
});
circle.append("title")
diff --git a/web/rest-test.sh b/web/rest-test.sh
index 241915a..2551f12 100755
--- a/web/rest-test.sh
+++ b/web/rest-test.sh
@@ -2,7 +2,7 @@
rm -f rest.json
touch rest.json
-urls="'http://localhost:8080/wm/core/topology/switches/all/json' 'http://localhost:8080/wm/core/topology/links/json' 'http://localhost:8080/wm/registry/controllers/json' 'http://localhost:8080/wm/registry/switches/json'"
+urls="http://localhost:8080/wm/core/topology/switches/all/json http://localhost:8080/wm/core/topology/links/json http://localhost:8080/wm/registry/controllers/json http://localhost:8080/wm/registry/switches/json"
for url in $urls; do
echo "---REST CALL---" >> rest.json