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