#!/usr/bin/env python

"""Package: mininet
   Test creation and pings for topologies with link and/or CPU options."""

import unittest

from mininet.net import Mininet
from mininet.node import OVSKernelSwitch
from mininet.node import CPULimitedHost
from mininet.link import TCLink
from mininet.topo import Topo
from mininet.log import setLogLevel


SWITCH = OVSKernelSwitch
# Number of hosts for each test
N = 2


class SingleSwitchOptionsTopo(Topo):
    "Single switch connected to n hosts."
    def __init__(self, n=2, hopts=None, lopts=None):
        if not hopts:
            hopts = {}
        if not lopts:
            lopts = {}
        Topo.__init__(self, hopts=hopts, lopts=lopts)
        switch = self.addSwitch('s1')
        for h in range(n):
            host = self.addHost('h%s' % (h + 1))
            self.addLink(host, switch)


class testOptionsTopo( unittest.TestCase ):
    "Verify ability to create networks with host and link options."

    def runOptionsTopoTest( self, n, hopts=None, lopts=None ):
        "Generic topology-with-options test runner."
        mn = Mininet( topo=SingleSwitchOptionsTopo( n=n, hopts=hopts,
                                                    lopts=lopts ),
                      host=CPULimitedHost, link=TCLink )
        dropped = mn.run( mn.ping )
        self.assertEqual( dropped, 0 )

    def assertWithinTolerance(self, measured, expected, tolerance_frac):
        """Check that a given value is within a tolerance of expected
        tolerance_frac: less-than-1.0 value; 0.8 would yield 20% tolerance.
        """
        self.assertTrue( float(measured) >= float(expected) * tolerance_frac )
        self.assertTrue( float(measured) >= float(expected) * tolerance_frac )

    def testCPULimits( self ):
        "Verify topology creation with CPU limits set for both schedulers."
        CPU_FRACTION = 0.1
        CPU_TOLERANCE = 0.8  # CPU fraction below which test should fail
        hopts = { 'cpu': CPU_FRACTION }
        #self.runOptionsTopoTest( N, hopts=hopts )

        mn = Mininet( SingleSwitchOptionsTopo( n=N, hopts=hopts ),
                      host=CPULimitedHost )
        mn.start()
        results = mn.runCpuLimitTest( cpu=CPU_FRACTION )
        mn.stop()
        for cpu in results:
            self.assertWithinTolerance( cpu, CPU_FRACTION, CPU_TOLERANCE )

    def testLinkBandwidth( self ):
        "Verify that link bandwidths are accurate within a bound."
        BW = 5  # Mbps
        BW_TOLERANCE = 0.8  # BW fraction below which test should fail
        # Verify ability to create limited-link topo first;
        lopts = { 'bw': BW, 'use_htb': True }
        # Also verify correctness of limit limitng within a bound.
        mn = Mininet( SingleSwitchOptionsTopo( n=N, lopts=lopts ),
                      link=TCLink )
        bw_strs = mn.run( mn.iperf )
        for bw_str in bw_strs:
            bw = float( bw_str.split(' ')[0] )
            self.assertWithinTolerance( bw, BW, BW_TOLERANCE )

    def testLinkDelay( self ):
        "Verify that link delays are accurate within a bound."
        DELAY_MS = 15
        DELAY_TOLERANCE = 0.8  # Delay fraction below which test should fail
        lopts = { 'delay': '%sms' % DELAY_MS, 'use_htb': True }
        mn = Mininet( SingleSwitchOptionsTopo( n=N, lopts=lopts ),
                      link=TCLink )
        ping_delays = mn.run( mn.pingFull )
        test_outputs = ping_delays[0]
        # Ignore unused variables below
        # pylint: disable-msg=W0612
        node, dest, ping_outputs = test_outputs
        sent, received, rttmin, rttavg, rttmax, rttdev = ping_outputs
        self.assertEqual( sent, received )
        # pylint: enable-msg=W0612
        for rttval in [rttmin, rttavg, rttmax]:
            # Multiply delay by 4 to cover there & back on two links
            self.assertWithinTolerance( rttval, DELAY_MS * 4.0,
                                        DELAY_TOLERANCE)

    def testLinkLoss( self ):
        "Verify that we see packet drops with a high configured loss rate."
        LOSS_PERCENT = 99
        REPS = 1
        lopts = { 'loss': LOSS_PERCENT, 'use_htb': True }
        mn = Mininet( topo=SingleSwitchOptionsTopo( n=N, lopts=lopts ),
                      host=CPULimitedHost, link=TCLink )
        # Drops are probabilistic, but the chance of no dropped packets is
        # 1 in 100 million with 4 hops for a link w/99% loss.
        dropped_total = 0
        mn.start()
        for _ in range(REPS):
            dropped_total += mn.ping(timeout='1')
        mn.stop()
        self.assertTrue(dropped_total > 0)

    def testMostOptions( self ):
        "Verify topology creation with most link options and CPU limits."
        lopts = { 'bw': 10, 'delay': '5ms', 'use_htb': True }
        hopts = { 'cpu': 0.5 / N }
        self.runOptionsTopoTest( N, hopts=hopts, lopts=lopts )


if __name__ == '__main__':
    setLogLevel( 'warning' )
    unittest.main()

@@ -1250,35 +1250,50 @@
 ost, link=TCLink
+, switch=SWITCH
  )%0A
-
         dropped 
@@ -2149,16 +2149,31 @@
 itedHost
+, switch=SWITCH
  )%0A     
@@ -2826,32 +2826,47 @@
      link=TCLink
+, switch=SWITCH
  )%0A        bw_st
@@ -3380,32 +3380,47 @@
      link=TCLink
+, switch=SWITCH
  )%0A        ping_
@@ -4270,32 +4270,32 @@
  lopts=lopts ),%0A
-
                 
@@ -4332,16 +4332,31 @@
 k=TCLink
+, switch=SWITCH
  )%0A     

use SWITCH to pick the switch class