Merge pull request #402 from n-shiota/ONOS131-release

ONOS-131 Test cases for Registry
diff --git a/cluster-mgmt/template/onsdemo_core.py.devA b/cluster-mgmt/template/onsdemo_core.py.devA
new file mode 100755
index 0000000..ad74e4b
--- /dev/null
+++ b/cluster-mgmt/template/onsdemo_core.py.devA
@@ -0,0 +1,158 @@
+#!/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 )
+
+        sw5 = self.addSwitch('sw5', dpid='00000000ba5eba13')
+        sw2 = self.addSwitch('sw2', dpid='00000000ba5eba11')
+        sw3 = self.addSwitch('sw3', dpid='00000008a208f901')
+        sw4 = self.addSwitch('sw4', dpid='000000000000ba12')
+        sw6 = self.addSwitch('sw6', dpid='0000204e7f518a35')
+        sw1 = self.addSwitch('sw1', dpid='0000001697089a46')
+
+        host1 = self.addHost( 'host1' )
+        host2 = self.addHost( 'host2' )
+        host3 = self.addHost( 'host3' )
+        host4 = self.addHost( 'host4' )
+        host5 = self.addHost( 'host5' )
+        host6 = self.addHost( 'host6' )
+
+        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( sw4, sw6 )
+
+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')
+
+    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/src/test/java/net/floodlightcontroller/core/internal/RoleChangeCallbackTest.java b/src/test/java/net/floodlightcontroller/core/internal/RoleChangeCallbackTest.java
new file mode 100644
index 0000000..2aeb60e
--- /dev/null
+++ b/src/test/java/net/floodlightcontroller/core/internal/RoleChangeCallbackTest.java
@@ -0,0 +1,143 @@
+package net.floodlightcontroller.core.internal;
+
+import static org.junit.Assert.*;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.List;
+
+import org.easymock.EasyMock;
+import org.easymock.IAnswer;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+// Extends Controller class to access protected inner class
+public class RoleChangeCallbackTest extends Controller {
+	@Before
+	public void setUp() throws Exception {
+	}
+
+	@After
+	public void tearDown() throws Exception {
+	}
+
+	/**
+	 * Test if {@link RoleChangeCallback#controlChanged(long, boolean)} correctly calls {@link RoleChanger#submitRequest(Collection, net.floodlightcontroller.core.IFloodlightProviderService.Role)}
+	 * when connectedSwitch is not empty.
+	 * @throws Exception
+	 */
+	@SuppressWarnings("unchecked")
+	@Test
+	public void testNormalSwitches() throws Exception {
+		Long [] dpids = new Long [] { 1000L, 1001L, 1002L, 1003L };
+		final long dpidExist = 1000L;
+		final long dpidNotExist = 2000L;
+		
+		roleChanger = EasyMock.createMock(RoleChanger.class);
+		
+		// First call will be called with (dpidExist,true)
+		roleChanger.submitRequest(EasyMock.anyObject(Collection.class), EasyMock.anyObject(Role.class));
+		EasyMock.expectLastCall().andAnswer(new IAnswer<Object>() {
+			@Override
+			public Object answer() throws Throwable {
+				Collection<OFSwitchImpl> switches = (Collection<OFSwitchImpl>)EasyMock.getCurrentArguments()[0];
+				Role role = (Role)EasyMock.getCurrentArguments()[1];
+
+				List<Long> dpids = new ArrayList<Long>();
+				
+				for(OFSwitchImpl sw : switches) {
+					dpids.add(sw.getId());
+				}
+				assertTrue(dpids.contains(dpidExist));
+				assertEquals(role, Role.MASTER);
+				
+				return null;
+			}
+		}).once();
+
+		// Second call will be called with (dpidExist,false)
+		roleChanger.submitRequest(EasyMock.anyObject(Collection.class), EasyMock.anyObject(Role.class));
+		EasyMock.expectLastCall().andAnswer(new IAnswer<Object>() {
+			@Override
+			public Object answer() throws Throwable {
+				Collection<OFSwitchImpl> switches = (Collection<OFSwitchImpl>)EasyMock.getCurrentArguments()[0];
+				Role role = (Role)EasyMock.getCurrentArguments()[1];
+
+				List<Long> dpids = new ArrayList<Long>();
+				
+				for(OFSwitchImpl sw : switches) {
+					dpids.add(sw.getId());
+				}
+				assertTrue(dpids.contains(dpidExist));
+				assertEquals(role, Role.SLAVE);
+				
+				return null;
+			}
+		}).once();
+
+		EasyMock.replay(roleChanger);
+		
+		initNetwork(roleChanger, dpids);
+		
+		RoleChangeCallback callback = new RoleChangeCallback();
+		callback.controlChanged(dpidExist, true);
+		callback.controlChanged(dpidExist, false);
+		callback.controlChanged(dpidNotExist, true);
+		callback.controlChanged(dpidNotExist, false);
+		
+		EasyMock.verify(roleChanger);
+	}
+
+	/**
+	 * Test if {@link RoleChangeCallback#controlChanged(long, boolean)} doesn't call RoleChanger methods
+	 * when connectedSwitch is empty.
+	 * @throws Exception
+	 */
+	@Test
+	public void testEmptySwitches() throws Exception {
+		Long [] dpids = new Long [] {};
+		final long dpidToTest = 1000L;
+		
+		roleChanger = EasyMock.createMock(RoleChanger.class);
+		// roleChanger methods must not be used
+		EasyMock.replay(roleChanger);
+		
+		initNetwork(roleChanger, dpids);
+		
+		RoleChangeCallback callback = new RoleChangeCallback();
+		callback.controlChanged(dpidToTest, true);
+		callback.controlChanged(dpidToTest, false);
+		
+		EasyMock.verify(roleChanger);
+	}
+	
+	/**
+	 * Create mock OFSwitchImpl object.
+	 * @param id
+	 * @return
+	 */
+	private OFSwitchImpl createOFSwitchImplMock(Long id) {
+		OFSwitchImpl sw = EasyMock.createMock(OFSwitchImpl.class);
+
+		EasyMock.expect(sw.getId()).andReturn(id).anyTimes();
+		EasyMock.replay(sw);
+		
+		return sw;
+	}
+	
+	/**
+	 * Setup connectedSwitches
+	 * @param changer
+	 * @param ids
+	 * @throws Exception
+	 */
+	private void initNetwork(RoleChanger changer, Long [] ids) throws Exception {
+		connectedSwitches = new HashSet<OFSwitchImpl>();
+		
+		for(Long id : ids) {
+			connectedSwitches.add(createOFSwitchImplMock(id));
+		}
+	}
+}
diff --git a/web/config.json.devA b/web/config.json.devA
new file mode 100644
index 0000000..b20639e
--- /dev/null
+++ b/web/config.json.devA
@@ -0,0 +1,22 @@
+{
+    "LB": false, 
+    "TESTBED": "sw",
+    "ONOS_DEFAULT_HOST": "localhost",
+    "ONOS_GUI3_CONTROL_HOST": "http://10.128.4.51:9000", 
+    "ONOS_GUI3_HOST": "http://10.128.4.51:9000", 
+    "cluster_basename": "ONOS", 
+    "controllers": [
+        "DevA-ONOS1", 
+        "DevA-ONOS2", 
+        "DevA-ONOS3", 
+        "DevA-ONOS4" 
+    ], 
+ "core_switches": [
+        "00:00:00:00:ba:5e:ba:11", 
+        "00:00:00:00:00:00:ba:12", 
+        "00:00:20:4e:7f:51:8a:35", 
+        "00:00:00:00:ba:5e:ba:13", 
+        "00:00:00:08:a2:08:f9:01", 
+        "00:00:00:16:97:08:9a:46"
+    ]
+}
diff --git a/web/ons-demo/data/configuration.json.devA b/web/ons-demo/data/configuration.json.devA
new file mode 100644
index 0000000..f8a6e63
--- /dev/null
+++ b/web/ons-demo/data/configuration.json.devA
@@ -0,0 +1,78 @@
+{
+	"core": [
+		"00:00:00:08:a2:08:f9:01",
+		"00:00:00:00:ba:5e:ba:11",
+		"00:00:20:4e:7f:51:8a:35",
+		"00:00:00:00:00:00:ba:12",
+		"00:00:00:00:ba:5e:ba:13",
+		"00:00:00:16:97:08:9a:46"
+	],
+	"aggregation": [
+		"00:00:00:00:00:00:02:01",
+		"00:00:00:00:00:00:03:01",
+		"00:00:00:00:00:00:04:01",
+		"00:00:00:00:00:00:05:01",
+		"00:00:00:00:00:00:06:01",
+		"00:00:00:00:00:00:07:01",
+		"00:00:00:00:00:00:08:01"
+	],
+	"association": {
+		"00:00:20:4e:7f:51:8a:35": [
+			"00:00:00:00:00:00:07:01"
+		],
+		"00:00:00:00:ba:5e:ba:13": [
+			"00:00:00:00:00:00:06:01"
+		],
+		"00:00:00:08:a2:08:f9:01": [
+			"00:00:00:00:00:00:03:01"
+		],
+		"00:00:00:00:00:00:ba:12": [
+			"00:00:00:00:00:00:04:01",
+			"00:00:00:00:00:00:05:01"
+		],
+		"00:00:00:00:ba:5e:ba:11": [
+			"00:00:00:00:00:00:02:01"
+		],
+		"00:00:00:16:97:08:9a:46": [
+			"00:00:00:00:00:00:08:01"
+		]
+	},
+	"geo": {
+		"00:00:20:4e:7f:51:8a:35": {
+			"lat": 41.891033,
+			"lng": -87.628326,
+			"label": "CHI",
+			"fanOutAngle": 180
+		},
+		"00:00:00:00:ba:5e:ba:13": {
+			"lat": 47.611024,
+			"lng": -122.33242,
+			"label": "SEA",
+			"fanOutAngle": 270
+		},
+		"00:00:00:08:a2:08:f9:01": {
+			"lat": 33.758599,
+			"lng": -84.387360,
+			"label": "ATL",
+			"fanOutAngle": 5
+		},
+		"00:00:00:16:97:08:9a:46": {
+			"lat": 41.225925,
+			"lng": -74.00528,
+			"label": "NYC",
+			"fanOutAngle": 150
+		},
+		"00:00:00:00:ba:5e:ba:11": {
+			"lat": 37.901187,
+			"lng": -76.037163,
+			"label": "DC",
+			"fanOutAngle": 45
+		},
+		"00:00:00:00:00:00:ba:12": {
+			"lat": 34.102708,
+			"lng": -118.238983,
+			"label": "LA",
+			"fanOutAngle": 315
+		}
+	}
+}