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