Merge "[CORD-642] Support IPv6 in fabric emulation"
diff --git a/TestON/requirements.txt b/TestON/requirements.txt
index 86c8741..28f7503 100644
--- a/TestON/requirements.txt
+++ b/TestON/requirements.txt
@@ -11,3 +11,4 @@
 pylint==1.1.0
 requests==2.2.1
 scapy==2.3.1
+ipaddress==1.0.16
diff --git a/TestON/tests/USECASE/SegmentRouting/dependencies/cord_fabric.py b/TestON/tests/USECASE/SegmentRouting/dependencies/cord_fabric.py
index 6348632..2c91806 100755
--- a/TestON/tests/USECASE/SegmentRouting/dependencies/cord_fabric.py
+++ b/TestON/tests/USECASE/SegmentRouting/dependencies/cord_fabric.py
@@ -2,8 +2,9 @@
 
 import os
 import re
+import math
 from optparse import OptionParser
-
+from ipaddress import IPv6Network, IPv4Network
 from mininet.net import Mininet
 from mininet.topo import Topo
 from mininet.node import RemoteController, UserSwitch, Host, OVSBridge
@@ -26,12 +27,77 @@
                        help='number of ONOS Instances, default=0, 0 means localhost, 1 will use OC1 and so on' )
     parser.add_option( '--vlan', dest='vlan', type='int', default=-1,
                        help='vid of cross connect, default=-1, -1 means utilize default value' )
+    parser.add_option( '--ipv6', action="store_true", dest='ipv6',
+                       help='hosts are capable to use also ipv6' )
     (options, args) = parser.parse_args( )
     return options, args
 
 
 opts, args = parseOptions( )
 
+IP6_SUBNET_CLASS = 120
+IP4_SUBNET_CLASS = 24
+
+class LeafAndSpine6( Topo ):
+    """
+    Create Leaf and Spine Topology for IPv4/IPv6 tests.
+    """
+    def __init__( self, spine=2, leaf=2, fanout=2, **opts ):
+        Topo.__init__( self, **opts )
+        spines          = {}
+        leafs           = {}
+        """
+        We calculate the offset from /120 and from /24 in order to have
+        a number of /120 and /24 subnets == leaf
+        """
+        offset          = int(math.ceil(math.sqrt( leaf )))
+        """
+        We calculate the subnets to use and set options
+        """
+        ipv6SubnetClass = unicode('2000::/%s' % (IP6_SUBNET_CLASS - offset))
+        ipv6Subnets     = list(IPv6Network(ipv6SubnetClass).subnets( new_prefix = IP6_SUBNET_CLASS ))
+        ipv4SubnetClass = unicode('10.0.0.0/%s' % (IP4_SUBNET_CLASS - offset))
+        ipv4Subnets     = list(IPv4Network(ipv4SubnetClass).subnets( new_prefix = IP4_SUBNET_CLASS ))
+        linkopts        = dict( bw=100 )
+        """
+        We create the spine switches
+        """
+        for s in range( spine ):
+            spines[ s ] = self.addSwitch( 'spine10%s' % (s + 1),
+                                          dpid="00000000010%s" % (s + 1) )
+        """
+        We create the leaf switches
+        """
+        for ls in range( leaf ):
+            leafs[ ls ] = self.addSwitch( 'leaf%s' % (ls + 1),
+                                          dpid="00000000000%s" % (1 + ls) )
+            ipv6Subnet  = ipv6Subnets[ ls ]
+            ipv6Hosts   = list(ipv6Subnet.hosts())
+            ipv4Subnet  = ipv4Subnets[ ls ]
+            ipv4Hosts   = list(ipv4Subnet.hosts())
+            """
+            We add the hosts
+            """
+            for f in range( fanout ):
+                ipv6 = ipv6Hosts[ f ]
+                ipv6Gateway = ipv6Hosts[ len( ipv6Hosts ) - 1 ]
+                ipv4 = ipv4Hosts[ f ]
+                ipv4Gateway = ipv4Hosts[ len( ipv4Hosts ) - 1 ]
+                host = self.addHost(
+                    name='h%s' % (ls * fanout + f + 1),
+                    cls=Ipv6Host,
+                    ip="%s/%s" %(ipv4, IP4_SUBNET_CLASS),
+                    gateway='%s' % ipv4Gateway,
+                    ipv6="%s/%s" %(ipv6, IP6_SUBNET_CLASS),
+                    ipv6Gateway="%s" % ipv6Gateway
+                    )
+                self.addLink( host, leafs[ ls ], **linkopts )
+            """
+            Connect leaf to all spines
+            """
+            for s in range( spine ):
+                switch = spines[ s ]
+                self.addLink( leafs[ ls ], switch, **linkopts )
 
 class LeafAndSpine( Topo ):
     def __init__( self, spine=2, leaf=2, fanout=2, **opts ):
@@ -92,6 +158,22 @@
         self.cmd( mtu )
         self.cmd( 'ip route add default via %s' % self.gateway )
 
+class Ipv6Host( IpHost ):
+    """
+    Abstraction to model an augmented host with a ipv6
+    functionalities as well
+    """
+    def __init__( self, name, *args, **kwargs ):
+        IpHost.__init__(self, name, *args, **kwargs)
+
+    def config( self, **kwargs ):
+        IpHost.config( self, **kwargs )
+        ipv6Cmd         = 'ifconfig %s-eth0 inet6 add %s' % (self.name, kwargs['ipv6'])
+        ipv6GatewayCmd  = 'ip -6 route add default via %s' % kwargs['ipv6Gateway']
+        ipv6MtuCmd      = 'ifconfig %s-eth0 inet6 mtu 1490' % (self.name)
+        self.cmd( ipv6Cmd )
+        self.cmd( ipv6GatewayCmd )
+        self.cmd( ipv6MtuCmd )
 
 class VLANHost( Host ):
     "Host connected to VLAN interface"
@@ -143,15 +225,34 @@
         link = self.mn.addLink( host, switch )
         host.config(**params)
 
+    def do_pingall6( self, line ):
+        "Ping6 between all hosts."
+        self.mn.pingAll6( line )
+
 def config( opts ):
     spine = opts.spine
     leaf = opts.leaf
     fanout = opts.fanout
     vlan = opts.vlan
+    ipv6 = opts.ipv6
     controllers = [ os.environ[ 'OC%s' % i ] for i in
                     range( 1, opts.onos + 1 ) ] if (opts.onos) else [
         '127.0.0.1' ]
-    topo = LeafAndSpine( spine=spine, leaf=leaf, fanout=fanout, vlan=vlan )
+    if not ipv6:
+        topo = LeafAndSpine(
+            spine=spine,
+            leaf=leaf,
+            fanout=fanout,
+            vlan=vlan,
+            )
+    else:
+        topo = LeafAndSpine6(
+            spine=spine,
+            leaf=leaf,
+            fanout=fanout,
+            vlan=vlan,
+            ipv6=ipv6
+            )
     net = Mininet( topo=topo, link=TCLink, build=False,
                    switch=UserSwitch, controller=None, autoSetMacs=True )
     i = 0
@@ -160,12 +261,12 @@
         i += 1;
     net.build( )
     net.start( )
-    out1 = net.get( 'out1' )
-    out1.cmd( "arp -s 10.0.9.254 10:00:00:00:00:01 -i %s " % (out1.intf()) )
-    CLI(net)
+    if not ipv6:
+        out1 = net.get( 'out1' )
+        out1.cmd( "arp -s 10.0.9.254 10:00:00:00:00:01 -i %s " % (out1.intf()) )
+    ExtendedCLI(net)
     net.stop( )
 
-
 if __name__ == '__main__':
     setLogLevel( 'info' )
     config( opts )