Merge pull request #24 from OPENNETWORKINGLAB/mininet_drivers

Some changes to mininet drivers
diff --git a/TestON/drivers/common/cli/emulator/mininetclidriver.py b/TestON/drivers/common/cli/emulator/mininetclidriver.py
index 9191b89..c888e23 100644
--- a/TestON/drivers/common/cli/emulator/mininetclidriver.py
+++ b/TestON/drivers/common/cli/emulator/mininetclidriver.py
@@ -153,26 +153,33 @@
         if self.handle :
             main.log.info(self.name+": Checking reachabilty to the hosts using pingall")
             try:
-                response = self.execute(cmd="pingall",prompt="mininet>",timeout=120)
-                print "response: " + str(response)
-            except pexpect.EOF:  
+                response = self.execute(cmd="pingall",prompt="mininet>",timeout=300)
+            except pexpect.EOF:
                 main.log.error(self.name + ": EOF exception found")
                 main.log.error(self.name + ":     " + self.handle.before)
-                #main.cleanup()
-                #main.exit()
-            pattern = 'Results\:\s0\%\sdropped\s'
-            #FIXME:Pending Mininet Pull Request #408
-            #pattern = 'Results\:\s0\.00\%\sdropped\s'
-            #if utilities.assert_matches(expect=pattern,actual=response,onpass="All hosts are reaching",onfail="Unable to reach all the hosts"):
+                main.cleanup()
+                main.exit()
+            except pexpect.TIMEOUT:
+                #We may not want to kill the test if pexpect times out
+                main.log.error(self.name + ": TIMEOUT exception found")
+                main.log.error(self.name + ":     " + str(self.handle.before) )
+            #NOTE: mininet's pingall rounds, so we will check the number of passed and number of failed
+            pattern = "Results\:\s0\%\sdropped\s\((?P<passed>[\d]+)/(?P=passed)"
             if re.search(pattern,response):
                 main.log.info(self.name+": All hosts are reachable")
                 return main.TRUE
             else:
                 main.log.error(self.name+": Unable to reach all the hosts")
+                main.log.info("Pingall ouput: " + str(response))
+                #NOTE: Send ctrl-c to make sure pingall is done
+                self.handle.send("\x03")
+                self.handle.expect("Interrupt")
+                self.handle.expect("mininet>")
                 return main.FALSE
         else :
             main.log.error(self.name+": Connection failed to the host") 
-            return main.FALSE
+            main.cleanup()
+            main.exit()
 
     def fpingHost(self,**pingParams):
         ''' 
@@ -735,9 +742,10 @@
             self.handle.expect("mininet>")
             self.handle.sendline("sh sudo tcpdump -n -i "+ intf + " " + port + " -w " + filename.strip() + "  &")
             self.handle.sendline("")
-            self.handle.sendline("")
             i=self.handle.expect(['No\ssuch\device','listening\son',pexpect.TIMEOUT,"mininet>"],timeout=10)
             main.log.warn(self.handle.before + self.handle.after)
+            self.handle.sendline("")
+            self.handle.expect("mininet>")
             if i == 0:
                 main.log.error(self.name + ": tcpdump - No such device exists. tcpdump attempted on: " + intf)
                 return main.FALSE
@@ -769,7 +777,7 @@
         "pkills tcpdump"
         try:
             self.handle.sendline("sh sudo pkill tcpdump")
-            self.handle.sendline("")
+            self.handle.expect("mininet>")
             self.handle.sendline("")
             self.handle.expect("mininet>")
         except pexpect.EOF:
@@ -857,7 +865,7 @@
         #FIXME: this does not look for extra ports in ONOS, only checks that ONOS has what is in MN
         import json
         from numpy import uint64
-        port_results = main.TRUE
+        ports_results = main.TRUE
         output = {"switches":[]}
         for switch in topo.graph.switches: #iterate through the MN topology and pull out switches and and port info
             ports = []
@@ -882,9 +890,12 @@
         for mn_switch in output['switches']:
             mn_ports = []
             onos_ports = []
+            switch_result = main.TRUE
             for port in mn_switch['ports']:
                 if port['enabled'] == True:
                     mn_ports.append(port['of_port'])
+                #else: #DEBUG only 
+                #    main.log.warn("Port %s on switch %s is down" % ( str(port['of_port']) , str(mn_switch['name'])) )
             for onos_switch in ports_json:
                 #print "Iterating through a new switch as seen by ONOS"
                 #print onos_switch
@@ -894,37 +905,57 @@
                             if port['isEnabled']:
                                 #print "Iterating through available ports on the switch"
                                 #print port
-                                onos_ports.append(int(port['port'])) 
+                                if port['port'] == 'local':
+                                    #onos_ports.append('local')
+                                    onos_ports.append(long(uint64(-2)))
+                                else:
+                                    onos_ports.append(int(port['port']))
+                                    '''
+                                else: #This is likely a new reserved port implemented
+                                    main.log.error("unkown port '" + str(port['port']) )
+                                    '''
+                           #else: #DEBUG
+                           #    main.log.warn("Port %s on switch %s is down" % ( str(port['port']) , str(onos_switch['device']['id'])) )
+                        break
             mn_ports.sort(key=float)
             onos_ports.sort(key=float)
             #print "\nPorts for Switch %s:" % (switch['name'])
             #print "\tmn_ports[] = ", mn_ports
             #print "\tonos_ports[] = ", onos_ports
-            
-            #NOTE:For OF1.3, the OFP_local port number is 0xfffffffe or 4294967294 instead of 0xfffe or 65534 in OF1.0,
-            #   ONOS topology sees the correct port number, however MN topology as read from line 151 of
-            #   https://github.com/ucb-sts/sts/blob/topology_refactoring2/sts/entities/teston_entities.py 
-            #   is 0xfffe which doesn't work correctly with OF1.3 switches.
-            
-            #NOTE: ONOS is abstracting port numbers to 64bit unsigned number(long). So we will be converting the 
-            #   OF reserved ports to these numbers
-
+            mn_ports_log = mn_ports
+            onos_ports_log = onos_ports
+            mn_ports = [x for x in mn_ports]
+            onos_ports = [x for x in onos_ports]
 
             #TODO: handle other reserved port numbers besides LOCAL
-            for mn_port,onos_port in zip(mn_ports,onos_ports):
-                #print "mn == onos port?"
-                #print mn_port, onos_port
-                if mn_port == onos_port or (mn_port == 65534 and onos_port == long(uint64(-2))):
-                    continue
+            #NOTE: List of Reserved Ports
+            #   Local port: -2 in unsigned int in Openflow, the size depends on
+            #       openflow version( 1.0 is 16 bit, 1.3 is 32 bit), ONOS shows
+            #       'local', we store as long(uint64(-2))
+            for mn_port in mn_ports_log:
+                if mn_port in onos_ports:
                     #don't set results to true here as this is just one of many checks and it might override a failure
-                else:  #the ports of this switch don't match
-                    port_results = main.FALSE
-                    break
-            if port_results == main.FALSE:
-                main.log.report("The list of ports for switch %s(%s) does not match:" % (mn_switch['name'], mn_switch['dpid']) )
-                main.log.report("mn_ports[] = " +  str(mn_ports))
-                main.log.report("onos_ports[] = " + str(onos_ports))
-        return port_results
+                    mn_ports.remove(mn_port)
+                    onos_ports.remove(mn_port)
+                #NOTE: OVS reports this as down since there is no link
+                #      So ignoring these for now
+                #TODO: Come up with a better way of handling these
+                if 65534 in mn_ports:
+                    mn_ports.remove(65534)
+                if long(uint64(-2)) in onos_ports:
+                    onos_ports.remove( long(uint64(-2))  )
+            if len(mn_ports):  #the ports of this switch don't match
+                switch_result = main.FALSE
+                main.log.warn("Ports in MN but not ONOS: " + str(mn_ports) )
+            if len(onos_ports):  #the ports of this switch don't match
+                switch_result = main.FALSE
+                main.log.warn("Ports in ONOS but not MN: " + str(onos_ports) )
+            if switch_result == main.FALSE:
+                main.log.report("The list of active ports for switch %s(%s) does not match:" % (mn_switch['name'], mn_switch['dpid']) )
+                main.log.warn("mn_ports[]  =  " + str(mn_ports_log))
+                main.log.warn("onos_ports[] = " + str(onos_ports_log))
+            ports_results = ports_results and switch_result
+        return ports_results
 
 
 
@@ -1007,7 +1038,7 @@
                     if int(onos_port1) == int(port1) and int(onos_port2) == int(port2):
                         first_dir = main.TRUE
                     else:
-                        main.log.report('The port numbers do not match for ' +str(link) +\
+                        main.log.warn('The port numbers do not match for ' +str(link) +\
                                 ' between ONOS and MN. When cheking ONOS for link '+\
                                 '%s/%s -> %s/%s' % (node1, port1, node2, port2)+\
                                 ' ONOS has the values %s/%s -> %s/%s' %\
@@ -1018,7 +1049,7 @@
                     if ( int(onos_port1) == int(port2) and int(onos_port2) == int(port1) ):
                         second_dir = main.TRUE
                     else:
-                        main.log.report('The port numbers do not match for ' +str(link) +\
+                        main.log.warn('The port numbers do not match for ' +str(link) +\
                                 ' between ONOS and MN. When cheking ONOS for link '+\
                                 '%s/%s -> %s/%s' % (node2, port2, node1, port1)+\
                                 ' ONOS has the values %s/%s -> %s/%s' %\
@@ -1065,18 +1096,23 @@
         '''
         #TODO: Add error checking. currently the mininet command has no output
         main.log.info("Updateing MN port information")
-        self.handle.sendline("")
-        self.handle.expect("mininet>")
-        
-        self.handle.sendline("update")
-        self.handle.expect("mininet>")
+        try:
+            self.handle.sendline("")
+            self.handle.expect("mininet>")
 
-        self.handle.sendline("")
-        self.handle.expect("mininet>")
+            self.handle.sendline("update")
+            self.handle.expect("update")
+            self.handle.expect("mininet>")
 
-        return main.TRUE 
+            self.handle.sendline("")
+            self.handle.expect("mininet>")
 
-        
+            return main.TRUE
+        except pexpect.EOF:
+            main.log.error(self.name + ": EOF exception found")
+            main.log.error(self.name + ":     " + self.handle.before)
+            main.cleanup()
+            main.exit()
 
 if __name__ != "__main__":
     import sys
diff --git a/TestON/drivers/common/cli/emulator/remotemininetdriver.py b/TestON/drivers/common/cli/emulator/remotemininetdriver.py
index bb03c07..5d2a7a3 100644
--- a/TestON/drivers/common/cli/emulator/remotemininetdriver.py
+++ b/TestON/drivers/common/cli/emulator/remotemininetdriver.py
@@ -92,7 +92,8 @@
         elif re.search("found multiple mininet",outputs):
             return main.ERROR
         else:
-            print outputs
+            main.log.error("Error, unexpected output in the ping file")
+            main.log.warn( outputs )
             return main.TRUE
 
 
@@ -155,6 +156,11 @@
         main.log.info( "Transferring ping files to TestStation" )
         command = "scp /tmp/ping.* "+ str(testONUser) + "@" + str(testONIP) + ":/tmp/" 
         self.execute(cmd=command,prompt="100%",timeout=20)
+        #Make sure the output is cleared
+        self.handle.sendline("")
+        self.handle.expect("\$")
+        self.handle.sendline("")
+        self.handle.expect("\$")
         self.handle.sendline("")
         self.handle.expect("\$")
         return main.TRUE
@@ -553,7 +559,7 @@
         response = ''
         #print "Disconnecting Mininet"
         if self.handle:
-            self.handle.sendline("exit") 
+            self.handle.sendline("exit")
             self.handle.expect("exit")
             self.handle.expect("(.*)")
             response = self.handle.before
@@ -561,13 +567,15 @@
         else :
             main.log.error("Connection failed to the host")
             response = main.FALSE
-        return response  
+        return response
 
     def get_flowTable(self, protoVersion, sw):
+        #TODO document usage
+        #FIXME: clean up print statements and such
         self.handle.sendline("cd")
-        #self.handle.expect(["\$",pexpect.EOF,pexpect.TIMEOUT])
-        print "cd expect status: " 
-        print self.handle.expect(["\$",pexpect.EOF,pexpect.TIMEOUT])
+        self.handle.expect(["\$",pexpect.EOF,pexpect.TIMEOUT])
+        #print "cd expect status: "
+        #print self.handle.expect(["\$",pexpect.EOF,pexpect.TIMEOUT])
         #TODO: Write seperate versions of the function for this, possibly a string that tells it which switch is in use?
         #For 1.0 version of OVS
         #command = "sudo ovs-ofctl dump-flows " + sw + " | awk '{OFS=\",\" ; print $1 $6 $7 }' |sort -n -k1"