blob: bb17577c14958cddda6c5fd4987dc966b66e721c [file] [log] [blame]
Roshan Piusc6db3d02015-04-28 15:15:48 -07001# Copyright 2015 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
5import logging
6import os
7import time
8import re
9
10import common
11from autotest_lib.client.common_lib.cros.network import ap_constants
12from autotest_lib.client.common_lib.cros.network import iw_runner
13from autotest_lib.server import hosts
14from autotest_lib.server import frontend
15from autotest_lib.server import site_utils
16from autotest_lib.server.cros.ap_configurators import ap_configurator
17from autotest_lib.server.cros.ap_configurators import ap_cartridge
18from autotest_lib.server.cros.ap_configurators import ap_spec as ap_spec_module
19
20
21def allocate_packet_capturer(lock_manager, hostname):
22 """Allocates a machine to capture packets.
23
24 Locks the allocated machine if the machine was discovered via AFE
25 to prevent tests stomping on each other.
26
27 @param lock_manager HostLockManager object.
28 @param hostname string optional hostname of a packet capture machine.
29
30 @return: An SSHHost object representing a locked packet_capture machine.
31 """
32 if hostname is not None:
33 return hosts.SSHHost(hostname)
34
35 afe = frontend.AFE(server='cautotest', debug=True)
36 return hosts.SSHHost(site_utils.lock_host_with_labels(
37 afe, lock_manager, labels=['packet_capture']) + '.cros')
38
39
Tien Changd2ae9742015-06-03 20:26:26 -070040def allocate_webdriver_instance(lock_manager):
41 """Allocates a machine to capture webdriver instance.
42
43 Locks the allocated machine if the machine was discovered via AFE
44 to prevent tests stomping on each other.
45
46 @param lock_manager HostLockManager object.
47
48 @return string hostname of locked webdriver instance
49 """
50 afe = frontend.AFE(debug=True)
51 webdriver_hostname = site_utils.lock_host_with_labels(afe, lock_manager,
52 labels=['webdriver'])
53 if webdriver_hostname is not None:
54 return webdriver_hostname
55 logging.error("Unable to allocate VM instance")
56 return None
57
58
59def power_on_VM(master, instance):
60 """Power on VM
61
62 @param master: chaosvmmaster SSHHost
63 @param instance: locked webdriver instance
64
65 """
66 logging.debug('Powering on %s VM', instance)
67 power_on_cmd = 'VBoxManage startvm %s' % instance
68 master.run(power_on_cmd)
69
70
71def power_off_VM(master, instance):
72 """Power off VM
73
74 @param master: chaosvmmaster SSHHost
75 @param instance: locked webdriver instance
76
77 """
78 logging.debug('Powering off %s VM', instance)
79 power_off_cmd = 'VBoxManage controlvm %s poweroff' % instance
80 master.run(power_off_cmd)
81
82
Roshan Piusc6db3d02015-04-28 15:15:48 -070083def power_down_aps(aps, broken_pdus=[]):
84 """Powers down a list of aps.
85
86 @param aps: a list of APConfigurator objects.
87 @param broken_pdus: a list of broken PDUs identified.
88 """
89 cartridge = ap_cartridge.APCartridge()
90 for ap in aps:
91 ap.power_down_router()
92 cartridge.push_configurator(ap)
93 cartridge.run_configurators(broken_pdus)
94
95
96def configure_aps(aps, ap_spec, broken_pdus=[]):
97 """Configures a given list of APs.
98
99 @param aps: a list of APConfigurator objects.
100 @param ap_spec: APSpec object corresponding to the AP configuration.
101 @param broken_pdus: a list of broken PDUs identified.
102 """
103 cartridge = ap_cartridge.APCartridge()
104 for ap in aps:
105 ap.set_using_ap_spec(ap_spec)
106 cartridge.push_configurator(ap)
107 cartridge.run_configurators(broken_pdus)
108
109
110def is_dut_healthy(client, ap):
111 """Returns if iw scan is working properly.
112
113 Sometimes iw scan will die, especially on the Atheros chips.
114 This works around that bug. See crbug.com/358716.
115
116 @param client: a wifi_client for the DUT
117 @param ap: ap_configurator object
118
119 @returns True if the DUT is healthy (iw scan works); False otherwise.
120 """
121 # The SSID doesn't matter, all that needs to be verified is that iw
122 # works.
123 networks = client.iw_runner.wait_for_scan_result(
Kris Rambish6b8a9552015-06-09 11:02:59 -0700124 client.wifi_if, ssids=[ap.ssid])
Roshan Piusc6db3d02015-04-28 15:15:48 -0700125 if networks == None:
126 return False
127 return True
128
129
130def is_conn_worker_healthy(conn_worker, ap, assoc_params, job):
131 """Returns if the connection worker is working properly.
132
133 From time to time the connection worker will fail to establish a
134 connection to the APs.
135
136 @param conn_worker: conn_worker object
137 @param ap: an ap_configurator object
138 @param assoc_params: the connection association parameters
139 @param job: the Autotest job object
140
141 @returns True if the worker is healthy; False otherwise
142 """
143 if conn_worker is None:
144 return True
145 conn_status = conn_worker.connect_work_client(assoc_params)
146 if not conn_status:
147 job.run_test('network_WiFi_ChaosConfigFailure', ap=ap,
148 error_string=ap_constants.WORK_CLI_CONNECT_FAIL,
149 tag=ap.ssid)
150 # Obtain the logs from the worker
151 log_dir_name = str('worker_client_logs_%s' % ap.ssid)
152 log_dir = os.path.join(job.resultdir, log_dir_name)
153 conn_worker.host.collect_logs(
154 '/var/log', log_dir, ignore_errors=True)
155 return False
156 return True
157
158
159def release_ap(ap, batch_locker, broken_pdus=[]):
160 """Powers down and unlocks the given AP.
161
162 @param ap: the APConfigurator under test.
163 @param batch_locker: the batch locker object.
164 @param broken_pdus: a list of broken PDUs identified.
165 """
166 ap.power_down_router()
167 try:
168 ap.apply_settings()
169 except ap_configurator.PduNotResponding as e:
170 if ap.pdu not in broken_pdus:
171 broken_pdus.append(ap.pdu)
172 batch_locker.unlock_one_ap(ap.host_name)
173
174
175def filter_quarantined_and_config_failed_aps(aps, batch_locker, job,
176 broken_pdus=[]):
177 """Filter out all PDU quarantined and config failed APs.
178
179 @param aps: the list of ap_configurator objects to filter
180 @param batch_locker: the batch_locker object
181 @param job: an Autotest job object
182 @param broken_pdus: a list of broken PDUs identified.
183
184 @returns a list of ap_configuration objects.
185 """
186 aps_to_remove = list()
187 for ap in aps:
188 failed_ap = False
189 if ap.pdu in broken_pdus:
190 ap.configuration_success = ap_constants.PDU_FAIL
191 if (ap.configuration_success == ap_constants.PDU_FAIL):
192 failed_ap = True
193 error_string = ap_constants.AP_PDU_DOWN
194 tag = ap.host_name + '_PDU'
195 elif (ap.configuration_success == ap_constants.CONFIG_FAIL):
196 failed_ap = True
197 error_string = ap_constants.AP_CONFIG_FAIL
198 tag = ap.host_name
199 if failed_ap:
200 tag += '_' + str(int(round(time.time())))
201 job.run_test('network_WiFi_ChaosConfigFailure',
202 ap=ap,
203 error_string=error_string,
204 tag=tag)
205 aps_to_remove.append(ap)
206 if error_string == ap_constants.AP_CONFIG_FAIL:
207 release_ap(ap, batch_locker, broken_pdus)
208 else:
209 # Cannot use _release_ap, since power_down will fail
210 batch_locker.unlock_one_ap(ap.host_name)
211 return list(set(aps) - set(aps_to_remove))
212
213
214def get_security_from_scan(ap, networks, job):
215 """Returns a list of securities determined from the scan result.
216
217 @param ap: the APConfigurator being testing against.
218 @param networks: List of matching networks returned from scan.
219 @param job: an Autotest job object
220
221 @returns a list of possible securities for the given network.
222 """
223 securities = list()
224 # Sanitize MIXED security setting for both Static and Dynamic
225 # configurators before doing the comparison.
226 security = networks[0].security
227 if (security == iw_runner.SECURITY_MIXED and
228 ap.configurator_type == ap_spec_module.CONFIGURATOR_STATIC):
229 securities = [iw_runner.SECURITY_WPA, iw_runner.SECURITY_WPA2]
230 # We have only seen WPA2 be backwards compatible, and we want
231 # to verify the configurator did the right thing. So we
232 # promote this to WPA2 only.
233 elif (security == iw_runner.SECURITY_MIXED and
234 ap.configurator_type == ap_spec_module.CONFIGURATOR_DYNAMIC):
235 securities = [iw_runner.SECURITY_WPA2]
236 else:
237 securities = [security]
238 return securities
239
240
241def scan_for_networks(ssid, capturer, ap_spec):
242 """Returns a list of matching networks after running iw scan.
243
244 @param ssid: the SSID string to look for in scan.
245 @param capturer: a packet capture device.
246 @param ap_spec: APSpec object corresponding to the AP configuration.
247
248 @returns a list of the matching networks; if no networks are found at
249 all, returns None.
250 """
251 # Setup a managed interface to perform scanning on the
252 # packet capture device.
253 freq = ap_spec_module.FREQUENCY_TABLE[ap_spec.channel]
254 wifi_if = capturer.get_wlanif(freq, 'managed')
255 capturer.host.run('%s link set %s up' % (capturer.cmd_ip, wifi_if))
256 # We have some APs that need a while to come on-line
257 networks = capturer.iw_runner.wait_for_scan_result(
Kris Rambish6b8a9552015-06-09 11:02:59 -0700258 wifi_if, ssids=[ssid], timeout_seconds=300)
Roshan Piusc6db3d02015-04-28 15:15:48 -0700259 capturer.remove_interface(wifi_if)
260 return networks
261
262
263def return_available_networks(ap, capturer, job, ap_spec):
264 """Returns a list of networks configured as described by an APSpec.
265
266 @param ap: the APConfigurator being testing against.
267 @param capturer: a packet capture device
268 @param job: an Autotest job object.
269 @param ap_spec: APSpec object corresponding to the AP configuration.
270
271 @returns a list of networks returned from _scan_for_networks().
272 """
273 for i in range(2):
274 networks = scan_for_networks(ap.ssid, capturer, ap_spec)
275 if networks is None:
276 return None
277 if len(networks) == 0:
278 # The SSID wasn't even found, abort
279 logging.error('The ssid %s was not found in the scan', ap.ssid)
280 job.run_test('network_WiFi_ChaosConfigFailure', ap=ap,
281 error_string=ap_constants.AP_SSID_NOTFOUND,
282 tag=ap.ssid)
283 return list()
284 security = get_security_from_scan(ap, networks, job)
285 if ap_spec.security in security:
286 return networks
287 if i == 0:
288 # The SSID exists but the security is wrong, give the AP time
289 # to possible update it.
290 time.sleep(60)
291 if ap_spec.security not in security:
292 logging.error('%s was the expected security but got %s: %s',
293 ap_spec.security,
294 str(security).strip('[]'),
295 networks)
296 job.run_test('network_WiFi_ChaosConfigFailure',
297 ap=ap,
298 error_string=ap_constants.AP_SECURITY_MISMATCH,
299 tag=ap.ssid)
300 networks = list()
301 return networks
302
303
304def sanitize_client(host):
305 """Clean up logs and reboot the DUT.
306
307 @param host: the cros host object to use for RPC calls.
308 """
309 host.run('rm -rf /var/log')
310 host.reboot()
311
312
313def get_firmware_ver(host):
314 """Get firmware version of DUT from /var/log/messages.
315
316 WiFi firmware version is matched against list of known firmware versions
317 from ToT.
318
319 @param host: the cros host object to use for RPC calls.
320
321 @returns the WiFi firmware version as a string, None if the version
322 cannot be found.
323 """
Roshan Pius10b27ca2015-12-01 14:01:23 -0800324 # TODO(rpius): Need to find someway to get this info for Android/Brillo.
325 if host.get_os_type() != 'cros':
326 return None
327
Roshan Piusc6db3d02015-04-28 15:15:48 -0700328 # Firmware versions manually aggregated by installing ToT on each device
329 known_firmware_ver = ['Atheros', 'mwifiex', 'loaded firmware version',
330 'brcmf_c_preinit_dcmds']
331 # Find and return firmware version in logs
332 for firmware_ver in known_firmware_ver:
333 result_str = host.run(
334 'awk "/%s/ {print}" /var/log/messages' % firmware_ver).stdout
335 if not result_str:
336 continue
337 else:
338 if 'Atheros' in result_str:
339 pattern = '%s \w+ Rev:\d' % firmware_ver
340 elif 'mwifiex' in result_str:
341 pattern = '%s [\d.]+ \([\w.]+\)' % firmware_ver
342 elif 'loaded firmware version' in result_str:
343 pattern = '(\d+\.\d+\.\d+.\d)'
344 elif 'Firmware version' in result_str:
345 pattern = '\d+\.\d+\.\d+ \([\w.]+\)'
346 else:
347 logging.info('%s does not match known firmware versions.',
348 result_str)
349 return None
350 result = re.search(pattern, result_str)
351 if result:
352 return result.group(0)
353 return None