blob: 41505f2efd22456ac3e13aec52d12b1c99757b3b [file] [log] [blame]
adminbae64d82013-08-01 10:50:15 -07001#!/usr/bin/env python
2'''
3Created on 31-May-2013
4
5@author: Anil Kumar (anilkumar.s@paxterrasolutions.com)
6
7 TestON is free software: you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation, either version 2 of the License, or
10 (at your option) any later version.
11
12 TestON is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with TestON. If not, see <http://www.gnu.org/licenses/>.
19
20
21'''
22import time
23import pexpect
24import struct, fcntl, os, sys, signal
25import sys
26import re
27import json
28sys.path.append("../")
29from drivers.common.clidriver import CLI
30
31class OnosCliDriver(CLI):
32
33 def __init__(self):
34 super(CLI, self).__init__()
35
36 def connect(self,**connectargs):
37 '''
38 Creates ssh handle for ONOS.
39 '''
40 for key in connectargs:
41 vars(self)[key] = connectargs[key]
42
43
44 self.name = self.options['name']
45 self.handle = super(OnosCliDriver,self).connect(user_name = self.user_name, ip_address = self.ip_address,port = self.port, pwd = self.pwd)
46
47 if self.handle:
48 #self.start()
49 #self.start_rest()
50 return self.handle
51 else :
52 main.log.info("NO HANDLE")
53 return main.FALSE
54
55 def start(self):
56 '''
57 Starts ONOS on remote machine.
58 Returns false if any errors were encountered.
59 '''
60 self.handle.sendline("")
61 self.handle.expect("\$")
62 self.handle.sendline("cd ~/ONOS")
63 self.handle.expect("ONOS\$")
64 self.handle.sendline("./start-onos.sh start")
adminaeedddd2013-08-02 15:14:15 -070065 self.handle.expect("onos.sh start")
adminbae64d82013-08-01 10:50:15 -070066 i=self.handle.expect(["Starting\sONOS\scontroller","Cassandra\sis\snot\srunning"])
67 if i==0:
adminaeedddd2013-08-02 15:14:15 -070068 try:
69 self.handle.expect("ONOS\$", timeout=60)
adminbae64d82013-08-01 10:50:15 -070070 main.log.info("ONOS Started ")
adminaeedddd2013-08-02 15:14:15 -070071 except:
adminbae64d82013-08-01 10:50:15 -070072 main.log.info("ONOS NOT Started, stuck while waiting for it to start ")
73 return main.FALSE
adminaeedddd2013-08-02 15:14:15 -070074 return main.TRUE
adminbae64d82013-08-01 10:50:15 -070075 elif i==1:
76 main.log.error("ONOS didn't start because cassandra wasn't running.")
77 return main.FALSE
78
79 main.log.error("ONOS expect script missed something... ")
80 return main.FALSE
81
82 def link_down(self, **linkParams):
83 '''
84 Specifically used for the ONOS demo on the HW.
85 Should be replaced by actual switch drivers in the future.
86 '''
87 args = utilities.parse_args(["SDPID","SPORT","DDPID","DPORT"], **linkParams)
88 sdpid = args["SDPID"] if args["SDPID"] != None else "00:00:00:00:ba:5e:ba:13"
89 sport = args["SPORT"] if args["SPORT"] != None else "22"
90 ddpid = args["DDPID"] if args["DDPID"] != None else "00:00:20:4e:7f:51:8a:35"
91 dport = args["DPORT"] if args["DPORT"] != None else "22"
92
93 cmd = "curl -s 10.128.4.11:9000/gui/link/down/" + sdpid + "/" + sport + "/" + ddpid + "/" + dport + " > /tmp/log &"
94 os.popen( cmd )
95
96 def link_up(self, **linkParams):
97 '''
98 Specifically used for the ONOS demo on the HW.
99 Should be replaced by actual switch drivers in the future.
100 '''
101 args = utilities.parse_args(["SDPID","SPORT","DDPID","DPORT"], **linkParams)
102 sdpid = args["SDPID"] if args["SDPID"] != None else "00:00:00:00:ba:5e:ba:13"
103 sport = args["SPORT"] if args["SPORT"] != None else "22"
104 ddpid = args["DDPID"] if args["DDPID"] != None else "00:00:20:4e:7f:51:8a:35"
105 dport = args["DPORT"] if args["DPORT"] != None else "22"
106
107 cmd = "curl -s 10.128.4.11:9000/gui/link/up/" + sdpid + "/" + sport + "/" + ddpid + "/" + dport + " > /tmp/log &"
108 os.popen( cmd )
109
110 def start_rest(self):
111 '''
112 Starts the rest server on ONOS.
113 '''
114 self.execute(cmd="cd ONOS",prompt="ONOS\$",timeout=10)
115 response = self.execute(cmd="start-rest.sh start",prompt="\$",timeout=10)
116 if re.search("admin",response):
117 main.log.info("Rest Server Started Successfully")
118 time.sleep(5)
119 return main.TRUE
120 else :
121 main.log.warn("Failed to start Rest Server")
122 return main.FALSE
123
124 def status(self):
125 '''
126 Called start-onos.sh status and returns TRUE/FALSE accordingly
127 '''
128 self.execute(cmd="\r",prompt="\$",timeout=10)
129 response = self.execute(cmd="~/ONOS/start-onos.sh status ",prompt="\d+\sinstance\sof\sonos\srunning",timeout=10)
130 self.execute(cmd="\r",prompt="\$",timeout=10)
131 if re.search("1\sinstance\sof\sonos\srunning",response):
132 return main.TRUE
133 elif re.search("0\sinstance\sof\sonos\srunning",response):
134 return main.FALSE
135 else :
136 return main.FALSE
137
138 def isup(self):
139 '''
140 A more complete check to see if ONOS is up and running properly.
141 First, it checks if the process is up.
142 Second, it reads the logs for "Exception: Connection refused"
143 Third, it makes sure the logs are actually moving.
144 returns TRUE/FALSE accordingly.
145 '''
146 self.execute(cmd="\r",prompt="\$",timeout=10)
147 response = self.execute(cmd="~/ONOS/start-onos.sh status ",prompt="running",timeout=10)
148 self.execute(cmd="\r",prompt="\$",timeout=10)
149 tail1 = self.execute(cmd="tail ~/ONOS/onos-logs/onos.*.log",prompt="\$",timeout=10)
150 time.sleep(2)
151 self.execute(cmd="\r",prompt="\$",timeout=10)
152 tail2 = self.execute(cmd="tail ~/ONOS/onos-logs/onos.*.log",prompt="\$",timeout=10)
153 pattern = '(.*)1 instance(.*)'
154 pattern2 = '(.*)Exception: Connection refused(.*)'
155 if utilities.assert_matches(expect=pattern,actual=response,onpass="ONOS process is running...",onfail="ONOS process not running..."):
156 if tail1 == tail2:
157 main.log.error("ONOS is frozen...")
158 return main.FALSE
159 elif re.search( pattern2,tail1 ):
160 main.log.info("Connection Refused found in onos log")
161 return main.FALSE
162 elif re.search( pattern2,tail2 ):
163 main.log.info("Connection Refused found in onos log")
164 return main.FALSE
165 else:
166 main.log.info("Onos log is moving! It's looking good!")
167 return main.TRUE
168 else:
169 return main.FALSE
170
171
172 def rest_status(self):
173 '''
174 Checks if the rest server is running.
175 '''
176 response = self.execute(cmd="~/ONOS/start-rest.sh status ",prompt="running",timeout=10)
177 if re.search("rest\sserver\sis\srunning",response):
178 main.log.info("Rest Server is running")
179 elif re.search("rest\sserver\sis\snot\srunning",response):
180 main.log.warn("Rest Server is not Running")
181 else :
182 main.log.error("No response" +response)
183 self.execute(cmd="\r",prompt="\$",timeout=10)
184
185 return response
186
187 def stop(self):
188 '''
189 Runs ./start-onos.sh stop to stop ONOS
190 '''
191 self.handle.sendline("")
192 self.handle.expect("\$")
193 self.handle.sendline("cd ~/ONOS")
194 self.handle.expect("ONOS\$")
195 self.handle.sendline("./start-onos.sh stop")
196 self.handle.expect("stop", 2)
adminaeedddd2013-08-02 15:14:15 -0700197 result = self.handle.before
adminbae64d82013-08-01 10:50:15 -0700198 self.handle.expect("ONOS\$", 60)
adminaeedddd2013-08-02 15:14:15 -0700199 if re.search("Killed", result):
adminbae64d82013-08-01 10:50:15 -0700200 main.log.info("ONOS Killed Successfully")
201 return main.TRUE
202 else :
203 main.log.warn("ONOS wasn't running")
204 return main.FALSE
205
206 def rest_stop(self):
207 '''
208 Runs ./start-rest.sh stop to stop ONOS rest server
209 '''
210 response = self.execute(cmd="~/ONOS/start-rest.sh stop ",prompt="killing",timeout=10)
211 self.execute(cmd="\r",prompt="\$",timeout=10)
212 if re.search("killing", response):
213 main.log.info("Rest Server Stopped")
214 return main.TRUE
215 else :
216 main.log.error("Failed to Stop, Rest Server is not Running")
217 return main.FALSE
218
219 def disconnect(self):
220 '''
adminaeedddd2013-08-02 15:14:15 -0700221 Called when Test is complete to disconnect the ONOS handle.
adminbae64d82013-08-01 10:50:15 -0700222 '''
adminaeedddd2013-08-02 15:14:15 -0700223 response = ''
224 try:
adminbae64d82013-08-01 10:50:15 -0700225 self.handle.sendline("exit")
adminaeedddd2013-08-02 15:14:15 -0700226 self.handle.expect("closed")
227 except:
adminbae64d82013-08-01 10:50:15 -0700228 main.log.error("Connection failed to the host")
229 response = main.FALSE
adminaeedddd2013-08-02 15:14:15 -0700230 return response
231
adminbae64d82013-08-01 10:50:15 -0700232 def get_version(self):
233 '''
234 Writes the COMMIT number to the report to be parsed by Jenkins data collecter.
235 '''
236 self.handle.sendline("cat /home/admin/ONOS/.git/ORIG_HEAD")
237 self.handle.expect("ORIG_HEAD")
238 self.handle.expect("\$")
239 main.log.report( "COMMIT: " + str(self.handle.before + self.handle.after))
240
241 def add_flow(self, path):
242 '''
243 Copies the flowdef file from TestStation -> ONOS machine
244 Then runs ./add_flow.py to add the flows to ONOS
245 '''
246 main.log.info("Adding Flows...")
247 self.handle.sendline("scp admin@10.128.7.7:%s /tmp/flowtmp" % path)
248 self.handle.expect("100%")
249 self.handle.expect("\$", 30)
250 self.handle.sendline("~/ONOS/web/add_flow.py -m onos -f /tmp/flowtmp")
251 self.handle.expect("add_flow")
252 self.handle.expect("\$", 1000)
253 main.log.info("Flows added")
254
255 def delete_flow(self, *delParams):
256 '''
257 Deletes a specific flow, a range of flows, or all flows.
258 '''
259 if len(delParams)==1:
260 if str(delParams[0])=="all":
261 main.log.info("Deleting ALL flows...")
262 #self.execute(cmd="~/ONOS/scripts/TestON_delete_flow.sh all",prompt="done",timeout=150)
263 self.handle.sendline("~/ONOS/web/delete_flow.py all")
264 self.handle.expect("delete_flow")
265 self.handle.expect("\$",1000)
266 self.handle.sendline("~/ONOS/web/clear_flow.py all")
267 self.handle.expect("clear_flow")
268 self.handle.expect("\$",1000)
269 main.log.info("Flows deleted")
270 else:
271 main.log.info("Deleting flow "+str(delParams[0])+"...")
272 #self.execute(cmd="~/ONOS/scripts/TestON_delete_flow.sh "+str(delParams[0]),prompt="done",timeout=150)
273 #self.execute(cmd="\n",prompt="\$",timeout=60)
274 self.handle.sendline("~/ONOS/web/delete_flow.py %d" % int(delParams[0]))
275 self.handle.expect("delete_flow")
276 self.handle.expect("\$",60)
277 self.handle.sendline("~/ONOS/web/clear_flow.py %d" % int(delParams[0]))
278 self.handle.expect("clear_flow")
279 self.handle.expect("\$",60)
280 main.log.info("Flow deleted")
281 elif len(delParams)==2:
282 main.log.info("Deleting flows "+str(delParams[0])+" through "+str(delParams[1])+"...")
283 #self.execute(cmd="~/ONOS/scripts/TestON_delete_flow.sh "+str(delParams[0])+" "+str(delParams[1]),prompt="done",timeout=150)
284 #self.execute(cmd="\n",prompt="\$",timeout=60)
285 self.handle.sendline("~/ONOS/web/delete_flow.py %d %d" % (int(delParams[0]), int(delParams[1])))
286 self.handle.expect("delete_flow")
287 self.handle.expect("\$",600)
288 self.handle.sendline("~/ONOS/web/clear_flow.py %d %d" % (int(delParams[0]), int(delParams[1])))
289 self.handle.expect("clear_flow")
290 self.handle.expect("\$",600)
291 main.log.info("Flows deleted")
292
293 def check_flow(self):
294 '''
295 Calls the ./get_flow.py all and checks:
296 - If each FlowPath has at least one FlowEntry
297 - That there are no "NOT"s found
298 returns TRUE/FALSE
299 '''
300 flowEntryDetect = 1
301 count = 0
302 self.handle.sendline("clear")
303 time.sleep(1)
304 self.handle.sendline("~/ONOS/web/get_flow.py all")
305 self.handle.expect("get_flow")
306 while 1:
307 i=self.handle.expect(['FlowPath','FlowEntry','NOT','\$',pexpect.TIMEOUT],timeout=180)
308 if i==0:
309 count = count + 1
310 if flowEntryDetect == 0:
311 main.log.info("FlowPath without FlowEntry")
312 return main.FALSE
313 else:
314 flowEntryDetect = 0
315 elif i==1:
316 flowEntryDetect = 1
317 elif i==2:
318 main.log.error("Found a NOT")
319 return main.FALSE
320 elif i==3:
321 if count == 0:
322 main.log.info("There don't seem to be any flows here...")
323 return main.FALSE
324 else:
325 main.log.info("All flows pass")
326 main.log.info("Number of FlowPaths: "+str(count))
327 return main.TRUE
328 elif i==4:
329 main.log.error("Command Timeout!")
330 return main.FALSE
331
332 def get_flow(self, *flowParams):
333 '''
334 Returns verbose output of ./get_flow.py
335 '''
336 if len(flowParams)==1:
337 if str(flowParams[0])=="all":
338 self.execute(cmd="\n",prompt="\$",timeout=60)
339 main.log.info("Getting all flow data...")
340 data = self.execute(cmd="~/ONOS/scripts/TestON_get_flow.sh all",prompt="done",timeout=150)
341 self.execute(cmd="\n",prompt="\$",timeout=60)
342 return data
343 else:
344 main.log.info("Retrieving flow "+str(flowParams[0])+" data...")
345 data = self.execute(cmd="~/ONOS/scripts/TestON_get_flow.sh "+str(flowParams[0]),prompt="done",timeout=150)
346 self.execute(cmd="\n",prompt="\$",timeout=60)
347 return data
348 elif len(flowParams)==5:
349 main.log.info("Retrieving flow installer data...")
350 data = self.execute(cmd="~/ONOS/scripts/TestON_get_flow.sh "+str(flowParams[0])+" "+str(flowParams[1])+" "+str(flowParams[2])+" "+str(flowParams[3])+" "+str(flowParams[4]),prompt="done",timeout=150)
351 self.execute(cmd="\n",prompt="\$",timeout=60)
352 return data
353 elif len(flowParams)==4:
354 main.log.info("Retrieving flow endpoints...")
355 data = self.execute(cmd="~/ONOS/scripts/TestON_get_flow.sh "+str(flowParams[0])+" "+str(flowParams[1])+" "+str(flowParams[2])+" "+str(flowParams[3]),prompt="done",timeout=150)
356 self.execute(cmd="\n",prompt="\$",timeout=60)
357 return data
358
359
360# http://localhost:8080/wm/core/topology/switches/all/json
361# http://localhost:8080/wm/core/topology/links/json
362# http://localhost:8080/wm/registry/controllers/json
363# http://localhost:8080/wm/registry/switches/json"
364
365 def get_json(self, url):
366 '''
367 Helper functions used to parse the json output of a rest call
368 '''
369 try:
370 command = "curl -s %s" % (url)
371 result = os.popen(command).read()
372 parsedResult = json.loads(result)
373 except:
374 print "REST IF %s has issue" % command
375 parsedResult = ""
376
377 if type(parsedResult) == 'dict' and parsedResult.has_key('code'):
378 print "REST %s returned code %s" % (command, parsedResult['code'])
379 parsedResult = ""
380 return parsedResult
381
382 def check_switch(self,RestIP,correct_nr_switch ):
383 '''
384 Used by check_status
385 '''
386 buf = ""
387 retcode = 0
388 RestPort="8080"
389 url="http://%s:%s/wm/core/topology/switches/all/json" % (RestIP, RestPort)
390 parsedResult = self.get_json(url)
391 if parsedResult == "":
392 retcode = 1
393 return (retcode, "Rest API has an issue")
394 url = "http://%s:%s/wm/registry/switches/json" % (RestIP, RestPort)
395 registry = self.get_json(url)
396
397 if registry == "":
398 retcode = 1
399 return (retcode, "Rest API has an issue")
400
401 cnt = 0
402 active = 0
403
404 for s in parsedResult:
405 cnt += 1
406 if s['state'] == "ACTIVE":
407 active += 1
408
409 buf += "switch: network %d : %d switches %d active\n" % (0+1, cnt, active)
410 if correct_nr_switch != cnt:
411 buf += "switch fail: network %d should have %d switches but has %d\n" % (1, correct_nr_switch, cnt)
412 retcode = 1
413
414 if correct_nr_switch != active:
415 buf += "switch fail: network %d should have %d active switches but has %d\n" % (1, correct_nr_switch, active)
416 retcode = 1
417
418 return (retcode, buf)
419
420 def check_link(self,RestIP, nr_links):
421 '''
422 Used by check_status
423 '''
424 RestPort = "8080"
425 buf = ""
426 retcode = 0
427
428 url = "http://%s:%s/wm/core/topology/links/json" % (RestIP, RestPort)
429 parsedResult = self.get_json(url)
430
431 if parsedResult == "":
432 retcode = 1
433 return (retcode, "Rest API has an issue")
434
435 buf += "link: total %d links (correct : %d)\n" % (len(parsedResult), nr_links)
436 intra = 0
437 interlink=0
438
439 for s in parsedResult:
440 intra = intra + 1
441
442 if intra != nr_links:
443 buf += "link fail\n"
444 retcode = 1
445
446 return (retcode, buf)
447
448 def check_status_report(self, ip, numoswitch, numolink):
449 '''
450 Checks the number of swithes & links that ONOS sees against the supplied values.
451 Writes to the report log.
452 '''
453 main.log.info("Making some rest calls...")
454 switch = self.check_switch(ip, int(numoswitch))
455 link = self.check_link(ip, int(numolink))
456 value = switch[0]
457 value += link[0]
458 main.log.report( "\n-----\n%s%s-----\n" % ( switch[1], link[1]) )
459 if value != 0:
460 return 0
461 else:
462 # "PASS"
463 return 1
464
465 def check_status(self, ip, numoswitch, numolink):
466 '''
467 Checks the number of swithes & links that ONOS sees against the supplied values.
468 Writes to the main log.
469 '''
470 main.log.info("Making some rest calls...")
471 switch = self.check_switch(ip, int(numoswitch))
472 link = self.check_link(ip, int(numolink))
473 value = switch[0]
474 value += link[0]
475 main.log.info( "\n-----\n%s%s-----\n" % ( switch[1], link[1]) )
476 if value != 0:
477 return 0
478 else:
479 # "PASS"
480 return 1
481
482 def drop_keyspace(self):
483 '''
484 Drops the ONOS keyspace
485 '''
486 self.handle.sendline("~/ONOS/scripts/test-drop-keyspace.sh")
487 self.handle.expect("keyspace")
488 self.handle.sendline("")
489 self.handle.expect("\$")
490 self.handle.expect("\$")
491
492 def ctrl_none(self):
493 '''
494 Points all the mininet swithces to no controllers
495 *NOTE will only work if CLUSTER is set up on ONOS nodes
496 '''
497 self.execute(cmd="switch none", prompt="\$",timeout=10)
498
499 def ctrl_one(self, ip):
500 '''
501 Points all the mininet swithces to all controllers
502 *NOTE will only work if CLUSTER is set up on ONOS nodes
503 '''
504 self.execute(cmd="switch one", prompt="\$",timeout=10)
505
506 def check_for_no_exceptions(self):
507 '''
508 Used by CassndraCheck.py to scan ONOS logs for Exceptions
509 '''
510 self.handle.sendline(r"dsh 'grep Exception ~/ONOS/onos-logs/onos.*.log'")
511 self.handle.expect("\$ dsh")
512 self.handle.expect("\$")
513 output = self.handle.before
514 main.log.info( output )
515 if re.search("Exception",output):
516 return main.FALSE
517 else :
518 return main.TRUE
519
520 def git_pull(self):
521 '''
522 Stops the ONOS, pulls the latest code, and builds with mvn.
523 Assumes that "git pull" works without login
524 '''
525 main.log.info("Stopping onos")
526 self.stop()
527 self.handle.sendline("cd ~/ONOS")
528 self.handle.expect("ONOS\$")
529 self.handle.sendline("git pull")
530
531 i=self.handle.expect(['fatal','Username\sfor\s(.*):\s','Unpacking\sobjects',pexpect.TIMEOUT,'Already up-to-date','Aborting'],timeout=180)
532 if i==0:
533 main.log.error("Git pull had some issue...")
534 return main.FALSE
535 elif i==1:
536 main.log.error("Asking for username!!! BADD!")
537 return false
538
539 self.handle.expect('Password\sfor\s(.*):\s')
540 j = self.handle.expect(['Unpacking\sobjects','Already up-to-date'])
541 if j == 0:
542 main.log.info("pulling repository now")
543 elif j == 1:
544 main.log.info("Up to date!")
545 else:
546 main.log.error("something went wrong")
547 return main.FALSE
548 self.handle.expect("ONOS\$", 30)
549 elif i==2:
550 main.log.info("pulling repository now")
551 self.handle.expect("ONOS\$", 30)
552 elif i==3:
553 main.log.error("TIMEOUT")
554 return main.FALSE
555 elif i==4:
556 main.log.info("Already up to date")
557 elif i==5:
558 main.log.info("Aborting... Are there conflicting git files?")
559 return main.FALSE
560
561
562 main.log.info("./setup-local-maven.sh")
563 self.handle.sendline("./setup-local-maven.sh")
564 self.handle.expect("local-maven.sh")
565 while 1:
566 i=self.handle.expect(['BUILD\sFAILURE','BUILD\sSUCCESS','ONOS\$',pexpect.TIMEOUT],timeout=90)
567 if i == 0:
568 main.log.error("Build failure!")
569 return main.FALSE
570 elif i == 1:
571 main.log.info("Build success!")
572 elif i == 2:
573 main.log.info("Build complete")
574 break;
575 elif i == 3:
576 main.log.error("TIMEOUT!")
577 return main.FALSE
578
579 main.log.info("mvn clean")
580 self.handle.sendline("mvn clean")
581 while 1:
582 i=self.handle.expect(['BUILD\sFAILURE','BUILD\sSUCCESS','ONOS\$',pexpect.TIMEOUT],timeout=30)
583 if i == 0:
584 main.log.error("Build failure!")
585 return main.FALSE
586 elif i == 1:
587 main.log.info("Build success!")
588 elif i == 2:
589 main.log.info("Build complete")
590 break;
591 elif i == 3:
592 main.log.error("TIMEOUT!")
593 return main.FALSE
594
595 main.log.info("mvn compile")
596 self.handle.sendline("mvn compile")
597 while 1:
598 i=self.handle.expect(['BUILD\sFAILURE','BUILD\sSUCCESS','ONOS\$',pexpect.TIMEOUT],timeout=30)
599 if i == 0:
600 main.log.error("Build failure!")
601 return main.FALSE
602 elif i == 1:
603 main.log.info("Build success!")
604 elif i == 2:
605 main.log.info("Build complete")
606 break;
607 elif i == 3:
608 main.log.error("TIMEOUT!")
609 return main.FALSE
610