Package TestON :: Package drivers :: Package common :: Package cli :: Package emulator :: Module remotemininetdriver
[hide private]
[frames] | no frames]

Source Code for Module TestON.drivers.common.cli.emulator.remotemininetdriver

  1  #!/usr/bin/env python 
  2  """ 
  3  Created on 26-Oct-2012 
  4   
  5  author:: Anil Kumar ( anilkumar.s@paxterrasolutions.com ) 
  6   
  7   
  8      TestON is free software: you can redistribute it and/or modify 
  9      it under the terms of the GNU General Public License as published by 
 10      the Free Software Foundation, either version 2 of the License, or 
 11      ( at your option ) any later version. 
 12   
 13      TestON is distributed in the hope that it will be useful, 
 14      but WITHOUT ANY WARRANTY; without even the implied warranty of 
 15      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
 16      GNU General Public License for more details. 
 17   
 18      You should have received a copy of the GNU General Public License 
 19      along with TestON.  If not, see <http://www.gnu.org/licenses/>. 
 20   
 21   
 22  MininetCliDriver is the basic driver which will handle the Mininet functions 
 23  """ 
 24  import pexpect 
 25  import re 
 26  import sys 
 27  import os 
 28  from drivers.common.cli.emulatordriver import Emulator 
 29   
 30   
31 -class RemoteMininetDriver( Emulator ):
32 33 """ 34 RemoteMininetCliDriver is the basic driver which will handle the Mininet 35 functions. The main different between this and the MininetCliDriver is that 36 this one does not build the mininet. It assumes that there is already a 37 mininet running on the target. 38 """
39 - def __init__( self ):
40 super( Emulator, self ).__init__() 41 self.handle = self 42 self.name = None 43 self.wrapped = sys.modules[ __name__ ] 44 self.flag = 0
45
46 - def connect( self, **connectargs ):
47 """,user_name, ip_address, pwd,options ): 48 Here the main is the TestON instance after creating all the log 49 handles.""" 50 for key in connectargs: 51 vars( self )[ key ] = connectargs[ key ] 52 53 self.name = self.options[ 'name' ] 54 55 try: 56 if os.getenv( str( self.ip_address ) ) != None: 57 self.ip_address = os.getenv( str( self.ip_address ) ) 58 else: 59 main.log.info( self.name + 60 ": Trying to connect to " + 61 self.ip_address ) 62 63 except KeyError: 64 main.log.info( "Invalid host name," + 65 " connecting to local host instead" ) 66 self.ip_address = 'localhost' 67 except Exception as inst: 68 main.log.error( "Uncaught exception: " + str( inst ) ) 69 70 self.handle = super( 71 RemoteMininetDriver, 72 self ).connect( 73 user_name=self.user_name, 74 ip_address=self.ip_address, 75 port=None, 76 pwd=self.pwd ) 77 78 # Copying the readme file to process the 79 if self.handle: 80 return main.TRUE 81 82 else: 83 main.log.error( 84 "Connection failed to the host " + 85 self.user_name + 86 "@" + 87 self.ip_address ) 88 main.log.error( "Failed to connect to the Mininet" ) 89 return main.FALSE
90
91 - def checkForLoss( self, pingList ):
92 """ 93 Returns main.FALSE for 0% packet loss and 94 Returns main.ERROR if "found multiple mininet" is found and 95 Returns main.TRUE else 96 """ 97 self.handle.sendline( "" ) 98 self.handle.expect( "\$" ) 99 self.handle.sendline( "" ) 100 self.handle.expect( "\$" ) 101 self.handle.sendline( "cat " + pingList ) 102 self.handle.expect( pingList ) 103 self.handle.expect( "\$" ) 104 outputs = self.handle.before + self.handle.after 105 if re.search( " 0% packet loss", outputs ): 106 return main.FALSE 107 elif re.search( "found multiple mininet", outputs ): 108 return main.ERROR 109 else: 110 # TODO: Parse for failed pings, give some truncated output 111 main.log.error( "Error, unexpected output in the ping file" ) 112 main.log.warn( outputs ) 113 return main.TRUE
114
115 - def pingLong( self, **pingParams ):
116 """ 117 Starts a continuous ping on the mininet host outputting 118 to a file in the /tmp dir. 119 """ 120 self.handle.sendline( "" ) 121 self.handle.expect( "\$" ) 122 args = utilities.parse_args( 123 [ "SRC", "TARGET", "PINGTIME" ], **pingParams ) 124 precmd = "sudo rm /tmp/ping." + args[ "SRC" ] 125 self.execute( cmd=precmd, prompt="(.*)", timeout=10 ) 126 command = "sudo mininet/util/m " + args[ "SRC" ] + " ping " +\ 127 args[ "TARGET" ] + " -i .2 -w " +\ 128 str( args[ 'PINGTIME' ] ) + " -D > /tmp/ping." +\ 129 args[ "SRC" ] + " &" 130 main.log.info( command ) 131 self.execute( cmd=command, prompt="(.*)", timeout=10 ) 132 self.handle.sendline( "" ) 133 self.handle.expect( "\$" ) 134 return main.TRUE
135
136 - def pingstatus( self, **pingParams ):
137 """ 138 Tails the respective ping output file and check that 139 there is a moving "64 bytes" 140 """ 141 self.handle.sendline( "" ) 142 self.handle.expect( "\$" ) 143 args = utilities.parse_args( [ "SRC" ], **pingParams ) 144 self.handle.sendline( "tail /tmp/ping." + args[ "SRC" ] ) 145 self.handle.expect( "tail" ) 146 self.handle.expect( "\$" ) 147 result = self.handle.before + self.handle.after 148 self.handle.sendline( "" ) 149 self.handle.expect( "\$" ) 150 if re.search( 'Unreachable', result ): 151 main.log.info( "Unreachable found in ping logs..." ) 152 return main.FALSE 153 elif re.search( '64\sbytes', result ): 154 main.log.info( "Pings look good" ) 155 return main.TRUE 156 else: 157 main.log.info( "No, or faulty ping data..." ) 158 return main.FALSE
159
160 - def pingKill( self, testONUser, testONIP ):
161 """ 162 Kills all continuous ping processes. 163 Then copies all the ping files to the TestStation. 164 """ 165 self.handle.sendline( "" ) 166 self.handle.expect( "\$" ) 167 command = "sudo kill -SIGINT `pgrep ping`" 168 main.log.info( command ) 169 self.execute( cmd=command, prompt="(.*)", timeout=10 ) 170 171 main.log.info( "Transferring ping files to TestStation" ) 172 command = "scp /tmp/ping.* " + \ 173 str( testONUser ) + "@" + str( testONIP ) + ":/tmp/" 174 self.execute( cmd=command, prompt="100%", timeout=20 ) 175 # Make sure the output is cleared 176 self.handle.sendline( "" ) 177 self.handle.expect( "\$" ) 178 self.handle.sendline( "" ) 179 self.handle.expect( "\$" ) 180 self.handle.sendline( "" ) 181 i = self.handle.expect( [ "password", "\$" ] ) 182 if i == 0: 183 main.log.error( "Error, sudo asking for password" ) 184 main.log.error( self.handle.before ) 185 return main.FALSE 186 else: 187 return main.TRUE
188
189 - def pingLongKill( self ):
190 self.handle.sendline( "" ) 191 self.handle.expect( "\$" ) 192 command = "sudo kill -SIGING `pgrep ping`" 193 main.log.info( command ) 194 self.execute( cmd=command, prompt="(.*)", timeout=10 ) 195 self.handle.sendline( "" ) 196 self.handle.expect( "\$" ) 197 return main.TRUE
198
199 - def pingHostOptical( self, **pingParams ):
200 """ 201 This function is only for Packet Optical related ping 202 Use the next pingHost() function for all normal scenarios ) 203 Ping from one mininet host to another 204 Currently the only supported Params: SRC and TARGET 205 """ 206 args = utilities.parse_args( [ "SRC", "TARGET" ], **pingParams ) 207 command = args[ "SRC" ] + " ping " + \ 208 args[ "TARGET" ] + " -c 1 -i 1 -W 8" 209 try: 210 main.log.warn( "Sending: " + command ) 211 self.handle.sendline( command ) 212 i = self.handle.expect( [ command, pexpect.TIMEOUT ] ) 213 if i == 1: 214 main.log.error( 215 self.name + 216 ": timeout when waiting for response from mininet" ) 217 main.log.error( "response: " + str( self.handle.before ) ) 218 i = self.handle.expect( [ "mininet>", pexpect.TIMEOUT ] ) 219 if i == 1: 220 main.log.error( 221 self.name + 222 ": timeout when waiting for response from mininet" ) 223 main.log.error( "response: " + str( self.handle.before ) ) 224 response = self.handle.before 225 except pexpect.EOF: 226 main.log.error( self.name + ": EOF exception found" ) 227 main.log.error( self.name + ": " + self.handle.before ) 228 main.cleanup() 229 main.exit() 230 main.log.info( self.name + ": Ping Response: " + response ) 231 if re.search( ',\s0\%\spacket\sloss', response ): 232 main.log.info( self.name + ": no packets lost, host is reachable" ) 233 main.lastResult = main.TRUE 234 return main.TRUE 235 else: 236 main.log.error( 237 self.name + 238 ": PACKET LOST, HOST IS NOT REACHABLE" ) 239 main.lastResult = main.FALSE 240 return main.FALSE
241
242 - def pingHost( self, **pingParams ):
243 """ 244 Pings between two hosts on remote mininet 245 """ 246 self.handle.sendline( "" ) 247 self.handle.expect( "\$" ) 248 args = utilities.parse_args( [ "SRC", "TARGET" ], **pingParams ) 249 command = "mininet/util/m " + \ 250 args[ "SRC" ] + " ping " + args[ "TARGET" ] + " -c 4 -W 1 -i .2" 251 main.log.info( command ) 252 response = self.execute( cmd=command, prompt="rtt", timeout=10 ) 253 if utilities.assert_matches( 254 expect=',\s0\%\spacket\sloss', 255 actual=response, 256 onpass="No Packet loss", 257 onfail="Host is not reachable" ): 258 main.log.info( "NO PACKET LOSS, HOST IS REACHABLE" ) 259 main.lastResult = main.TRUE 260 return main.TRUE 261 else: 262 main.log.error( "PACKET LOST, HOST IS NOT REACHABLE" ) 263 main.lastResult = main.FALSE 264 return main.FALSE
265
266 - def checknum( self, num ):
267 """ 268 Verifies the correct number of switches are running 269 """ 270 if self.handle: 271 self.handle.sendline( "" ) 272 self.handle.expect( "\$" ) 273 self.handle.sendline( 'ifconfig -a | grep "sw.. " | wc -l' ) 274 self.handle.expect( "wc" ) 275 self.handle.expect( "\$" ) 276 response = self.handle.before 277 self.handle.sendline( 278 'ps -ef | grep "bash -ms mininet:sw" | grep -v color | wc -l' ) 279 self.handle.expect( "color" ) 280 self.handle.expect( "\$" ) 281 response2 = self.handle.before 282 283 if re.search( num, response ): 284 if re.search( num, response2 ): 285 return main.TRUE 286 else: 287 return main.FALSE 288 else: 289 return main.FALSE 290 else: 291 main.log.error( "Connection failed to the host" )
292
293 - def startTcpdump( 294 self, 295 filename, 296 intf="eth0", 297 port="port 6633", 298 user="admin" ):
299 """ 300 Runs tcpdump on an interface and saves the file 301 intf can be specified, or the default eth0 is used 302 """ 303 try: 304 self.handle.sendline( "" ) 305 self.handle.sendline( 306 "sudo tcpdump -n -i " + 307 intf + 308 " " + 309 port + 310 " -w " + 311 filename.strip() + 312 " -Z " + 313 user + 314 " &" ) 315 self.handle.sendline( "" ) 316 self.handle.sendline( "" ) 317 i = self.handle.expect( [ 'No\ssuch\device', 'listening\son', 318 pexpect.TIMEOUT, "\$" ], timeout=10 ) 319 main.log.info( self.handle.before + self.handle.after ) 320 if i == 0: 321 main.log.error( self.name + ": tcpdump - No such device exists.\ 322 tcpdump attempted on: " + intf ) 323 return main.FALSE 324 elif i == 1: 325 main.log.info( self.name + ": tcpdump started on " + intf ) 326 return main.TRUE 327 elif i == 2: 328 main.log.error( self.name + ": tcpdump command timed out!\ 329 Check interface name, given interface was: " + intf ) 330 return main.FALSE 331 elif i == 3: 332 main.log.info( self.name + ": " + self.handle.before ) 333 return main.TRUE 334 else: 335 main.log.error( self.name + ": tcpdump - unexpected response" ) 336 return main.FALSE 337 except pexpect.EOF: 338 main.log.error( self.name + ": EOF exception found" ) 339 main.log.error( self.name + ": " + self.handle.before ) 340 main.cleanup() 341 main.exit() 342 except Exception: 343 main.log.exception( self.name + ": Uncaught exception!" ) 344 main.cleanup() 345 main.exit()
346
347 - def stopTcpdump( self ):
348 """ 349 pkills tcpdump""" 350 try: 351 self.handle.sendline( "sudo pkill tcpdump" ) 352 self.handle.sendline( "" ) 353 self.handle.expect( "\$" ) 354 except pexpect.EOF: 355 main.log.error( self.name + ": EOF exception found" ) 356 main.log.error( self.name + ": " + self.handle.before ) 357 main.cleanup() 358 main.exit() 359 except Exception: 360 main.log.exception( self.name + ": Uncaught exception!" ) 361 main.cleanup() 362 main.exit()
363
364 - def runOpticalMnScript( self,name = 'onos', ctrllerIP = None ):
365 import time 366 import types 367 """ 368 Description: 369 This function is only meant for Packet Optical. 370 It runs python script "opticalTest.py" to create the 371 packet layer( mn ) and optical topology 372 Optional: 373 name - Name of onos directory. (ONOS | onos) 374 Required: 375 ctrllerIP = Controller(s) IP address 376 TODO: If no ctrllerIP is provided, a default 377 $OC1 can be accepted 378 """ 379 try: 380 self.handle.sendline( "" ) 381 self.handle.expect( "\$" ) 382 self.handle.sendline( "cd ~/" + name + "/tools/test/topos" ) 383 self.handle.expect( "topos\$" ) 384 if ctrllerIP == None: 385 main.log.info( "You need to specify the IP" ) 386 return main.FALSE 387 else: 388 controller = '' 389 if isinstance( ctrllerIP, types.ListType ): 390 for i in xrange( len( ctrllerIP ) ): 391 controller += ctrllerIP[i] + ' ' 392 main.log.info( "Mininet topology is being loaded with " + 393 "controllers: " + controller ) 394 elif isinstance( ctrllerIP, types.StringType ): 395 controller = ctrllerIP 396 main.log.info( "Mininet topology is being loaded with " + 397 "controller: " + controller ) 398 else: 399 main.log.info( "You need to specify a valid IP" ) 400 return main.FALSE 401 cmd = "sudo -E python opticalTest.py " + controller 402 main.log.info( self.name + ": cmd = " + cmd ) 403 self.handle.sendline( cmd ) 404 time.sleep(30) 405 self.handle.sendline( "" ) 406 self.handle.sendline( "" ) 407 self.handle.expect("mininet>") 408 return main.TRUE 409 except pexpect.EOF: 410 main.log.error( self.name + ": EOF exception found" ) 411 main.log.error( self.name + ": " + self.handle.before ) 412 return main.FALSE
413
414 - def attachLincOESession( self ):
415 """ 416 Since executing opticalTest.py will give you mininet 417 prompt, you would at some point require to get onto 418 console of LincOE ((linc@onosTestBench)1>) to execute 419 commands like bring a optical port up or down on a ROADM 420 You can attach to console of Linc-OE session by a cmd: 421 sudo ~/linc-oe/rel/linc/bin/linc attach 422 """ 423 try: 424 self.handle.sendline( "" ) 425 self.handle.expect( "\$" ) 426 self.handle.sendline( "sudo ~/linc-oe/rel/linc/bin/linc attach" ) 427 self.handle.expect( ">" ) 428 return main.TRUE 429 except pexpect.EOF: 430 main.log.error( self.name + ": EOF exception found" ) 431 main.log.error( self.name + ": " + self.handle.before ) 432 return main.FALSE
433
434 - def disconnect( self ):
435 """ 436 Called at the end of the test to disconnect the handle. 437 """ 438 if self.handle: 439 # Close the ssh connection 440 self.handle.sendline( "" ) 441 # self.handle.expect( "\$" ) 442 i = self.handle.expect( [ '\$', 'mininet>', pexpect.TIMEOUT, 443 pexpect.EOF ], timeout=2 ) 444 if i == 0: 445 self.handle.sendline( "exit" ) 446 self.handle.expect( "closed" ) 447 elif i == 1: 448 self.handle.sendline( "exit" ) 449 self.handle.expect( "exit" ) 450 self.handle.expect('\$') 451 self.handle.sendline( "exit" ) 452 self.handle.expect( "exit" ) 453 self.handle.expect( "closed" ) 454 else: 455 main.log.error( "Connection failed to the host" ) 456 return main.TRUE
457
458 - def setIpTablesOUTPUT( self, dstIp, dstPort, action='add', 459 packetType='tcp', rule='DROP' ):
460 """ 461 Description: 462 add or remove iptables rule to DROP ( default ) 463 packets from specific IP and PORT 464 Usage: 465 * specify action ( 'add' or 'remove' ) 466 when removing, pass in the same argument as you would add. It will 467 delete that specific rule. 468 * specify the destination ip to block with dstIp 469 * specify destination port to block to dstPort 470 * optional packet type to block ( default tcp ) 471 * optional iptables rule ( default DROP ) 472 WARNING: 473 * This function uses root privilege iptables command which may result 474 in unwanted network errors. USE WITH CAUTION 475 """ 476 import re 477 import time 478 479 # NOTE********* 480 # The strict checking methods of this driver function is intentional 481 # to discourage any misuse or error of iptables, which can cause 482 # severe network errors 483 # ************* 484 485 # NOTE: Sleep needed to give some time 486 # for rule to be added and registered 487 # to the instance 488 time.sleep( 5 ) 489 490 actionType = action.lower() 491 if actionType != 'add' and actionType != 'remove': 492 main.log.error( 493 "Invalid action type. 'add' or 'remove' table rule" ) 494 if rule != 'DROP' and rule != 'ACCEPT' and rule != 'LOG': 495 # NOTE: Currently only supports rules DROP, ACCEPT, and LOG 496 main.log.error( 497 "Invalid rule. 'DROP' or 'ACCEPT' or 'LOG' only." ) 498 return 499 return 500 else: 501 502 # If there is no existing rule in the iptables, we will see an 503 # 'iptables:'... message. We expect to see this message. 504 # Otherwise, if there IS an existing rule, we will get the prompt 505 # back, hence why we expect $ for remove type. We want to remove 506 # an already existing rule 507 508 if actionType == 'add': 509 # NOTE: "iptables:" expect is a result of 510 # return from the command 511 # iptables -C ... 512 # Any changes by the iptables command return string 513 # will result in failure of the function. ( deemed unlikely 514 # at the time of writing this function ) 515 # Check for existing rules on current input 516 self.handle.sendline( "" ) 517 self.handle.expect( "\$" ) 518 self.handle.sendline( 519 "sudo iptables -C OUTPUT -p " + 520 str( packetType ) + 521 " -d " + 522 str( dstIp ) + 523 " --dport " + 524 str( dstPort ) + 525 " -j " + 526 str( rule ) ) 527 i = self.handle.expect( [ "iptables:", "\$" ] ) 528 print i 529 print self.handle.before 530 print "after: " 531 print self.handle.after 532 533 elif actionType == 'remove': 534 # Check for existing rules on current input 535 self.handle.sendline( "" ) 536 self.handle.expect( "\$" ) 537 self.handle.sendline( 538 "sudo iptables -C OUTPUT -p " + 539 str( packetType ) + 540 " -d " + 541 str( dstIp ) + 542 " --dport " + 543 str( dstPort ) + 544 " -j " + 545 str( rule ) ) 546 self.handle.expect( "\$" ) 547 print "before: " 548 print self.handle.before 549 actualString = self.handle.after 550 expectString = "iptables:" 551 print "Actual String:" 552 print actualString 553 554 if re.search( expectString, actualString ): 555 matchResult = main.TRUE 556 else: 557 matchResult = main.FALSE 558 # If matchResult is main.TRUE, it means there is no matching rule. 559 560 # If tables does not exist and expected prompt is returned, 561 # go ahead and add iptables rule 562 if matchResult == main.TRUE: 563 # Ensure action type is add 564 if actionType == 'add': 565 # -A is the 'append' action of iptables 566 actionAdd = '-A' 567 try: 568 self.handle.sendline( "" ) 569 self.handle.sendline( 570 "sudo iptables " + 571 actionAdd + 572 " OUTPUT -p " + 573 str( packetType ) + 574 " -d " + 575 str( dstIp ) + 576 " --dport " + 577 str( dstPort ) + 578 " -j " + 579 str( rule ) ) 580 581 infoString = "Rules added to " + str( self.name ) 582 infoString += "iptables rule added to block IP: " + \ 583 str( dstIp ) 584 infoString += "Port: " + \ 585 str( dstPort ) + " Rule: " + str( rule ) 586 587 main.log.info( infoString ) 588 589 self.handle.expect( 590 [ "\$", pexpect.EOF, pexpect.TIMEOUT ] ) 591 except pexpect.TIMEOUT: 592 main.log.error( 593 self.name + 594 ": Timeout exception in setIpTables function" ) 595 except Exception: 596 main.log.exception( self.name + 597 ": Uncaught exception!" ) 598 main.cleanup() 599 main.exit() 600 else: 601 main.log.error( 602 "Given rule already exists, but attempted to add it" ) 603 # If matchResult is 0, it means there IS a matching rule provided 604 elif matchResult == main.FALSE: 605 # Ensure action type is remove 606 if actionType == 'remove': 607 # -D is the 'delete' rule of iptables 608 actionRemove = '-D' 609 try: 610 self.handle.sendline( "" ) 611 # Delete a specific rule specified into the function 612 self.handle.sendline( 613 "sudo iptables " + 614 actionRemove + 615 " OUTPUT -p " + 616 str( packetType ) + 617 " -d " + 618 str( dstIp ) + 619 " --dport " + 620 str( dstPort ) + 621 " -j " + 622 str( rule ) ) 623 624 infoString = "Rules removed from " + str( self.name ) 625 infoString += " iptables rule removed \ 626 from blocking IP: " + \ 627 str( dstIp ) 628 infoString += " Port: " + \ 629 str( dstPort ) + " Rule: " + str( rule ) 630 631 main.log.info( infoString ) 632 633 self.handle.expect( 634 [ "\$", pexpect.EOF, pexpect.TIMEOUT ] ) 635 except pexpect.TIMEOUT: 636 main.log.error( 637 self.name + 638 ": Timeout exception in setIpTables function" ) 639 except Exception: 640 main.log.exception( self.name + 641 ": Uncaught exception!" ) 642 main.cleanup() 643 main.exit() 644 else: 645 main.log.error( 646 "Given rule does not exist,\ 647 but attempted to remove it" ) 648 else: 649 # NOTE: If a bad usage of this function occurs, exit the entire 650 # test 651 main.log.error( "Bad rule given for iptables. Exiting..." ) 652 main.cleanup() 653 main.exit()
654 655 656 if __name__ != "__main__": 657 import sys 658 sys.modules[ __name__ ] = RemoteMininetDriver() 659