Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(24)

Side by Side Diff: server/site_wifitest.py

Issue 669118: automated WiFi test framework and start of tests (Closed)
Patch Set: revert correctly Created 10 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « server/site_tests/network_WiFiSecMat/control ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 # Copyright (c) 2010 The Chromium OS Authors. All rights reserved.
2 # Use of this source code is governed by a BSD-style license that can be
3 # found in the LICENSE file.
4
5 import common, fnmatch, logging, os, re, string, threading, time
6
7 from autotest_lib.server import autotest, subcommand
8 from autotest_lib.server import site_bsd_router
9 #from autotest_lib.server import site_linux_router
10
11 class HelperThread(threading.Thread):
12 # Class that wraps a ping command in a thread so it can run in the bg.
13 def __init__(self, client, cmd):
14 threading.Thread.__init__(self)
15 self.client = client
16 self.cmd = cmd
17
18 def run(self):
19 # NB: set ignore_status as we're always terminated w/ pkill
20 self.client.run(self.cmd, ignore_status=True)
21
22
23 class WiFiTest(object):
24 """
25 WiFi Test.
26
27 Each test is specified as a dict. There must be a "name" entry that
28 gives the test name (a string) and a "steps" entry that has an ordered
29 tuple of test steps, where each step is a tuple [func, {args}].
30
31 Step funcs are one of:
32 config configure the router/AP using the specified params
33 (ssid automatically supplied); params are give as
34 BSD ifconfig(8) parameters and translated to match
35 the target router/AP's cli syntax
36 deconfig de-configure/shut-off the router/AP
37 connect connect client to AP using specified parameters
38 (ssid automatically supplied)
39 disconnect disconnect client from AP
40 client_check_config check the client network connection to verify
41 state is setup as expected
42 sleep pause on the autotest server for a time
43 client_ping ping the server on the client machine
44 server_ping ping the client on the server machine
45 client_iperf run iperf on the client to the server
46 server_iperf run iperf on the server to the client
47 client_netperf run netperf on the client to the server
48 server_netperf run netperf on the server to the client
49
50 Steps that are done on the client or server machine are implemented in
51 this class. Steps that are done on the wifi router are implemented in
52 a separate class that knows how to control the router. There are presently
53 two classes: BSDRouter for routers based on FreeBSD and LinuxRouter for
54 those based on Linux/mac80211. Additional router support can be added
55 by adding a new class and auto-selecting it in __init__.
56
57 The WiFiTest class could be generalized to handle clients other than
58 ChromeOS; this would useful for systems that use Network Manager or
59 wpa_supplicant directly.
60 """
61
62 def __init__(self, name, steps, router, client, server):
63 self.name = name
64 self.steps = steps
65 self.router = router['host']
66 self.client = client['host']
67 self.client_at = autotest.Autotest(self.client)
68 self.client_wifi_ip = None # client's IP address on wifi net
69 self.server = server['host']
70 self.server_at = autotest.Autotest(self.server)
71 # server's IP address on wifi net (XXX assume same for now)
72 self.server_wifi_ip = self.server.ip
73
74 # NB: truncate SSID to 32 characters
75 self.defssid = self.__get_defssid()[0:32]
76
77 # XXX auto-detect router type
78 self.wifi = site_bsd_router.BSDRouter(self.router, router, self.defssid)
79
80 # potential bg thread for ping untilstop
81 self.ping_thread = None
82
83
84 def setup(self):
85 self.job.setup_dep(['netperf'])
86 # XXX enable once built by build_autotest
87 # self.job.setup_dep(['iperf'])
88 # create a empty srcdir to prevent the error that checks .version
89 if not os.path.exists(self.srcdir):
90 os.mkdir(self.srcdir)
91
92
93 def cleanup(self, params):
94 """ Cleanup state: disconnect client and destroy ap """
95 self.disconnect({})
96 self.wifi.destroy({})
97
98
99 def __get_defssid(self):
100 #
101 # Calculate ssid based on test name; this lets us track progress
102 # by watching beacon frames.
103 #
104 # XXX truncate to 32 chars
105 return re.sub('[^a-zA-Z0-9_]', '_', \
106 "%s_%s" % (self.name, self.router.ip))
107
108
109 def run(self):
110 """
111 Run a WiFi test. Each step is interpreted as a method either
112 in this class or the ancillary router class and invoked with
113 the supplied parameter dictionary.
114 """
115 for s in self.steps:
116 method = s[0]
117 if len(s) > 1:
118 params = s[1]
119 else:
120 params = {}
121
122 logging.info("%s: step '%s' params %s" % \
123 (self.name, method, params))
124
125 func = getattr(self, method, None)
126 if func is None:
127 func = getattr(self.wifi, method, None)
128 if func is not None:
129 try:
130 func(params)
131 except Exception, e:
132 logging.error("%s: Step '%s' failed: %s; abort test" % \
133 (self.name, method, str(e)))
134 self.cleanup(params)
135 break
136 else:
137 logging.error("%s: Step '%s' unknown; abort test" % \
138 (self.name, method))
139 self.cleanup(params)
140 break
141
142
143 def __get_connect_script(self, params):
144 return '\
145 import dbus, dbus.mainloop.glib, gobject, logging, re, sys, time\n\
146 \
147 ssid = "' + params['ssid'] + '"\n\
148 security = "' + params['security'] + '"\n\
149 psk = "' + params.get('psk', "") + '"\n\
150 assoc_timeout = ' + params.get('assoc_timeout', "15") + '\n\
151 config_timeout = ' + params.get('config_timeout', "15") + '\n\
152 \
153 bus_loop = dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)\n\
154 bus = dbus.SystemBus(mainloop=bus_loop)\n\
155 manager = dbus.Interface(bus.get_object("org.moblin.connman", "/"),\n\
156 "org.moblin.connman.Manager")\n\
157 \
158 try:\n\
159 path = manager.GetService(({\n\
160 "Type": "wifi",\n\
161 "Mode": "managed",\n\
162 "SSID": ssid,\n\
163 "Security": security,\n\
164 "Passphrase": psk }))\n\
165 service = dbus.Interface(\n\
166 bus.get_object("org.moblin.connman", path),\n\
167 "org.moblin.connman.Service")\n\
168 except Exception, e:\n\
169 print "FAIL(GetService): ssid %s exception %s" %(ssid, e)\n\
170 sys.exit(1)\n\
171 \
172 try:\n\
173 service.Connect()\n\
174 except Exception, e:\n\
175 print "FAIL(Connect): ssid %s exception %s" %(ssid, e)\n\
176 sys.exit(2)\n\
177 \
178 status = ""\n\
179 assoc_time = 0\n\
180 # wait up to assoc_timeout seconds to associate\n\
181 while assoc_time < assoc_timeout:\n\
182 properties = service.GetProperties()\n\
183 status = properties.get("State", None)\n\
184 # print>>sys.stderr, "time %3.1f state %s" % (assoc_time, status)\n\
185 if status == "failure":\n\
186 print "FAIL(assoc): ssid %s assoc %3.1f secs props %s" %(ssid, assoc_tim e, properties)\n\
187 sys.exit(3)\n\
188 if status == "configuration" or status == "ready":\n\
189 break\n\
190 time.sleep(.5)\n\
191 assoc_time += .5\n\
192 if assoc_time >= assoc_timeout:\n\
193 print "TIMEOUT(assoc): ssid %s assoc %3.1f secs" %(ssid, assoc_time)\n\
194 sys.exit(4)\n\
195 \
196 # wait another config_timeout seconds to get an ip address\n\
197 config_time = 0\n\
198 if status != "ready":\n\
199 while config_time < config_timeout:\n\
200 properties = service.GetProperties()\n\
201 status = properties.get("State", None)\n\
202 # print>>sys.stderr, "time %3.1f state %s" % (config_time, status)\n\
203 if status == "failure":\n\
204 print "FAIL(config): ssid %s assoc %3.1f config %3.1f secs" \\\n\
205 %(ssid, assoc_time, config_time)\n\
206 sys.exit(5)\n\
207 if status == "ready":\n\
208 break\n\
209 time.sleep(.5)\n\
210 config_time += .5\n\
211 if config_time >= config_timeout:\n\
212 print "TIMEOUT(config): ssid %s assoc %3.1f config %3.1f secs"\\\n\
213 %(ssid, assoc_time, config_time)\n\
214 sys.exit(6)\n\
215 print "assoc %3.1f secs config %3.1f secs" % (assoc_time, config_time)\n\
216 sys.exit(0)'
217
218
219 def __get_ipaddr(self, host, ifnet):
220 # XXX gotta be a better way to do this
221 result = host.run("ifconfig %s" % ifnet)
222 m = re.search('inet addr:([^ ]*)', result.stdout)
223 if m is None:
224 raise Except, "No inet address found"
225 return m.group(1)
226
227
228 def connect(self, params):
229 """ Connect client to AP/router """
230 if 'ssid' not in params:
231 params['ssid'] = self.defssid
232 script = self.__get_connect_script(params)
233 result = self.client.run("python<<'EOF'\n%s\nEOF\n" % script)
234 print "%s: %s" % (self.name, result.stdout[0:-1])
235
236 # fetch IP address of wireless device
237 # XXX wlan0 hard coded
238 self.client_wifi_ip = self.__get_ipaddr(self.client, "wlan0")
239 logging.info("%s: client WiFi-IP is %s" % \
240 (self.name, self.client_wifi_ip))
241
242
243 def __make_disconnect_script(self, params):
244 return '\
245 import dbus, dbus.mainloop.glib, gobject, logging, sys, time\n\
246 \n\
247 interface = "' + params.get('psk', "") + '"\n\
248 \n\
249 bus_loop = dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)\n\
250 bus = dbus.SystemBus(mainloop=bus_loop)\n\
251 manager = dbus.Interface(bus.get_object("org.moblin.connman", "/"),\n\
252 "org.moblin.connman.Manager")\n\
253 \n\
254 sys.exit(0)'
255
256
257 def disconnect(self, params):
258 """ Disconnect previously connected client """
259 self.client_ping_bg_stop({})
260 # script = self.__make_disconnect_script(params)
261 # self.client.run("python<<'EOF'\n%s\nEOF\n" % script)
262
263
264 def sleep(self, params):
265 time.sleep(float(params['time']))
266
267
268 def __ping_args(self, params):
269 args = ""
270 if 'count' in params:
271 args += " -c %s" % params['count']
272 if 'size' in params:
273 args += " -s %s" % params['size']
274 if 'bcast' in params:
275 args += " -b"
276 if 'flood' in params:
277 args += " -f"
278 if 'interval' in params:
279 args += " -i %s" % params['interval']
280 if 'qos' in params:
281 ac = string.lower(params['qos'])
282 if ac == 'be':
283 args += " -Q 0x04"
284 elif ac == 'bk':
285 args += " -Q 0x02"
286 elif ac == 'vi':
287 args += " -Q 0x08"
288 elif ac == 'vo':
289 args += " -Q 0x10"
290 else:
291 args += " -Q %s" % ac
292 return args
293
294
295 def __get_pingstats(self, str):
296 stats = {}
297 m = re.search('([0-9]*) packets transmitted,[ ]*([0-9]*)[ ]'
298 'received, ([0-9]*)', str)
299 if m is not None:
300 stats['xmit'] = m.group(1)
301 stats['recv'] = m.group(2)
302 stats['loss'] = m.group(3)
303 m = re.search('rtt min[^=]*= ([0-9.]*)/([0-9.]*)/([0-9.]*)', str)
304 if m is not None:
305 stats['min'] = m.group(1)
306 stats['avg'] = m.group(2)
307 stats['max'] = m.group(3)
308 return stats
309
310
311 def __print_pingstats(self, label, stats):
312 logging.info("%s: %s%s/%s, %s%% loss, rtt %s/%s/%s" % \
313 (self.name, label, stats['xmit'], stats['recv'], stats['loss'],
314 stats['min'], stats['avg'], stats['max']))
315
316
317 def client_ping(self, params):
318 """ Ping the server from the client """
319 ping_ip = params.get('ping_ip', self.server_wifi_ip)
320 count = params.get('count', 10)
321 # set timeout for 3s / ping packet
322 result = self.client.run("ping %s %s" % \
323 (self.__ping_args(params), ping_ip), timeout=3*int(count))
324
325 self.__print_pingstats("client_ping ",
326 self.__get_pingstats(result.stdout))
327
328
329 def client_ping_bg(self, params):
330 """ Ping the server from the client """
331 ping_ip = params.get('ping_ip', self.server_wifi_ip)
332 cmd = "ping %s %s" % (self.__ping_args(params), ping_ip)
333 self.ping_thread = HelperThread(self.client, cmd)
334 self.ping_thread.start()
335
336
337 def client_ping_bg_stop(self, params):
338 if self.ping_thread is not None:
339 self.client.run("pkill ping")
340 self.ping_thread.join()
341 self.ping_thread = None
342
343
344 def server_ping(self, params):
345 """ Ping the client from the server """
346 ping_ip = params.get('ping_ip', self.client_wifi_ip)
347 # XXX 30 second timeout
348 result = self.server.run("ping %s %s" % \
349 (self.__ping_args(params), ping_ip), timeout=30)
350
351 self.__print_pingstats("server_ping ",
352 self.__get_pingstats(result.stdout))
353
354
355
356 def __run_iperf(self, client_ip, server_ip, params):
357 template = ''.join(["job.run_test('iperf', \
358 server_ip='%s', client_ip='%s', role='%s'%s"])
359 if 'udp' in params:
360 template += ", udp=True"
361 if 'bidir' in params:
362 template += ", bidirectional=True"
363 if 'time' in params:
364 template += ", test_time=%s" % params['time']
365 template += ")"
366
367 server_control_file = template % (server_ip, client_ip, 'server')
368 server_command = subcommand.subcommand(self.server_at.run,
369 [server_control_file, self.server.hostname])
370
371 client_control_file = template % (server_ip, client_ip, 'client')
372 client_command = subcommand.subcommand(self.client_at.run,
373 [client_control_file, self.client.hostname])
374
375 logging.info("%s: iperf %s => %s" % (self.name, client_ip, server_ip))
376
377 # XXX 30 sec timeout for now
378 subcommand.parallel([server_command, client_command], timeout=30)
379
380
381 def client_iperf(self, params):
382 """ Run iperf on the client against the server """
383 self.__run_iperf(self.client_wifi_ip, self.server_wifi_ip, params)
384
385
386 def server_iperf(self, params):
387 """ Run iperf on the server against the client """
388 self.__run_iperf(self.server_wifi_ip, self.client_wifi_ip, params)
389
390
391 def __run_netperf(self, client_ip, server_ip, params):
392 template = ''.join(["job.run_test('netperf2', \
393 server_ip='%s', client_ip='%s', role='%s'"])
394 if 'test' in params:
395 template += ", test='%s'" % params['test']
396 if 'bidir' in params:
397 template += ", bidi=True"
398 if 'time' in params:
399 template += ", test_time=%s" % params['time']
400 template += ")"
401
402 server_control_file = template % (server_ip, client_ip, 'server')
403 server_command = subcommand.subcommand(self.server_at.run,
404 [server_control_file, self.server.hostname])
405
406 client_control_file = template % (server_ip, client_ip, 'client')
407 client_command = subcommand.subcommand(self.client_at.run,
408 [client_control_file, self.client.hostname])
409
410 logging.info("%s: netperf %s => %s" % (self.name, client_ip, server_ip))
411
412 # XXX 30 sec timeout for now
413 subcommand.parallel([server_command, client_command], timeout=60)
414
415
416 def client_netperf(self, params):
417 """ Run netperf on the client against the server """
418 self.__run_netperf(self.client_wifi_ip, self.server_wifi_ip, params)
419
420
421 def server_netperf(self, params):
422 """ Run netperf on the server against the client """
423 self.__run_netperf(self.server_wifi_ip, self.client_wifi_ip, params)
424
425
426 def __byfile(a, b):
427 if a['file'] < b['file']:
428 return -1
429 elif a['file'] > b['file']:
430 return 1
431 else:
432 return 0
433
434 def read_tests(dir, pat):
435 """
436 Collect WiFi test tuples from files. File names are used to
437 sort the test objects so the convention is to name them NNN<test>
438 where NNN is a decimal number used to sort and <test> is an
439 identifying name for the test; e.g. 000Check11b
440 """
441 tests = []
442 for file in os.listdir(dir):
443 if fnmatch.fnmatch(file, pat):
444 fd = open(os.path.join(dir, file));
445 try:
446 test = eval(fd.read())
447 except Exception, e:
448 logging.error("%s: %s" % (os.path.join(dir, file), str(e)))
449 raise e
450 test['file'] = file
451 tests.append(test)
452 # use filenames to sort
453 return sorted(tests, cmp=__byfile)
454
OLDNEW
« no previous file with comments | « server/site_tests/network_WiFiSecMat/control ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698