blob: 099fd4448770c87ce5e3ad119970e1a088a0d8a2 [file] [log] [blame]
Tien Changa2581fb2016-05-06 15:10:09 -07001# Copyright 2016 The Chromium OS Authors. All rights reserved.
Roshan Piusc6db3d02015-04-28 15:15:48 -07002# 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
Dinesh Kumar Sunkara5c632a72020-01-28 19:51:08 -080010import codecs
Roshan Piusc6db3d02015-04-28 15:15:48 -070011
12import common
Tien Changd78ef9c2016-11-09 16:34:54 -080013from autotest_lib.client.common_lib import error
harpreet23370612018-11-16 17:22:19 -080014from autotest_lib.client.common_lib import utils
Roshan Piusc6db3d02015-04-28 15:15:48 -070015from autotest_lib.client.common_lib.cros.network import ap_constants
16from autotest_lib.client.common_lib.cros.network import iw_runner
17from autotest_lib.server import hosts
Roshan Piusc6db3d02015-04-28 15:15:48 -070018from autotest_lib.server import site_utils
19from autotest_lib.server.cros.ap_configurators import ap_configurator
20from autotest_lib.server.cros.ap_configurators import ap_cartridge
21from autotest_lib.server.cros.ap_configurators import ap_spec as ap_spec_module
Dinesh Kumar Sunkara5c632a72020-01-28 19:51:08 -080022from autotest_lib.server.cros.chaos_lib import chaos_datastore_utils
Roshan Piusc6db3d02015-04-28 15:15:48 -070023
24
Dinesh Kumar Sunkara5c632a72020-01-28 19:51:08 -080025def allocate_packet_capturer(lock_manager):
Dinesh Kumar Sunkara14570892019-10-25 16:11:57 -070026 """Finds a packet capturer to capture packets.
27
28 Locks the allocated pcap if it is discovered in datastore
29
30 @param lock_manager HostLockManager object.
31
32 @return: An SSHHost object representing a locked packet_capture machine.
33 """
Dinesh Kumar Sunkara5c632a72020-01-28 19:51:08 -080034 # Gets available unlocked PCAPs
35 dutils = chaos_datastore_utils.ChaosDataStoreUtils()
Dinesh Kumar Sunkara14570892019-10-25 16:11:57 -070036 available_pcaps = dutils.get_devices_by_type(ap_label='CrOS_PCAP',
37 lab_label='CrOS_Chaos')
38 for pcap in available_pcaps:
39 # Ensure the pcap and dut are in the same subnet
Dinesh Kumar Sunkara5c632a72020-01-28 19:51:08 -080040 # Encode response that's in unicode format
41 pcap_hostname = pcap['hostname'].encode("utf-8")
42 # Pass pcap hostname as set to lock_kmanager
43 pcap_host = set([pcap_hostname])
44 if lock_manager.lock(pcap_host):
Dinesh Kumar Sunkara14570892019-10-25 16:11:57 -070045 return hosts.SSHHost(pcap['hostname'] + '.cros')
Dinesh Kumar Sunkara14570892019-10-25 16:11:57 -070046 else:
47 logging.info('Unable to lock %s', pcap['hostname'])
48 continue
49 raise error.TestError('Unable to lock any pcaps - check datastore for '
50 'pcaps locked status')
51
52
Roshan Piusc6db3d02015-04-28 15:15:48 -070053def power_down_aps(aps, broken_pdus=[]):
54 """Powers down a list of aps.
55
56 @param aps: a list of APConfigurator objects.
57 @param broken_pdus: a list of broken PDUs identified.
58 """
59 cartridge = ap_cartridge.APCartridge()
60 for ap in aps:
61 ap.power_down_router()
62 cartridge.push_configurator(ap)
63 cartridge.run_configurators(broken_pdus)
64
65
66def configure_aps(aps, ap_spec, broken_pdus=[]):
67 """Configures a given list of APs.
68
69 @param aps: a list of APConfigurator objects.
70 @param ap_spec: APSpec object corresponding to the AP configuration.
71 @param broken_pdus: a list of broken PDUs identified.
72 """
73 cartridge = ap_cartridge.APCartridge()
74 for ap in aps:
75 ap.set_using_ap_spec(ap_spec)
76 cartridge.push_configurator(ap)
77 cartridge.run_configurators(broken_pdus)
78
79
80def is_dut_healthy(client, ap):
81 """Returns if iw scan is working properly.
82
83 Sometimes iw scan will die, especially on the Atheros chips.
84 This works around that bug. See crbug.com/358716.
85
86 @param client: a wifi_client for the DUT
87 @param ap: ap_configurator object
88
89 @returns True if the DUT is healthy (iw scan works); False otherwise.
90 """
91 # The SSID doesn't matter, all that needs to be verified is that iw
92 # works.
93 networks = client.iw_runner.wait_for_scan_result(
Kris Rambish6b8a9552015-06-09 11:02:59 -070094 client.wifi_if, ssids=[ap.ssid])
Roshan Piusc6db3d02015-04-28 15:15:48 -070095 if networks == None:
96 return False
97 return True
98
99
100def is_conn_worker_healthy(conn_worker, ap, assoc_params, job):
101 """Returns if the connection worker is working properly.
102
103 From time to time the connection worker will fail to establish a
104 connection to the APs.
105
106 @param conn_worker: conn_worker object
107 @param ap: an ap_configurator object
108 @param assoc_params: the connection association parameters
109 @param job: the Autotest job object
110
111 @returns True if the worker is healthy; False otherwise
112 """
113 if conn_worker is None:
114 return True
115 conn_status = conn_worker.connect_work_client(assoc_params)
116 if not conn_status:
117 job.run_test('network_WiFi_ChaosConfigFailure', ap=ap,
118 error_string=ap_constants.WORK_CLI_CONNECT_FAIL,
119 tag=ap.ssid)
120 # Obtain the logs from the worker
121 log_dir_name = str('worker_client_logs_%s' % ap.ssid)
122 log_dir = os.path.join(job.resultdir, log_dir_name)
123 conn_worker.host.collect_logs(
124 '/var/log', log_dir, ignore_errors=True)
125 return False
126 return True
127
128
129def release_ap(ap, batch_locker, broken_pdus=[]):
130 """Powers down and unlocks the given AP.
131
132 @param ap: the APConfigurator under test.
133 @param batch_locker: the batch locker object.
134 @param broken_pdus: a list of broken PDUs identified.
135 """
136 ap.power_down_router()
137 try:
138 ap.apply_settings()
139 except ap_configurator.PduNotResponding as e:
140 if ap.pdu not in broken_pdus:
141 broken_pdus.append(ap.pdu)
Dinesh Kumar Sunkara5c632a72020-01-28 19:51:08 -0800142 batch_locker.unlock_one_ap(ap.host_name)
Roshan Piusc6db3d02015-04-28 15:15:48 -0700143
144
145def filter_quarantined_and_config_failed_aps(aps, batch_locker, job,
146 broken_pdus=[]):
147 """Filter out all PDU quarantined and config failed APs.
148
149 @param aps: the list of ap_configurator objects to filter
150 @param batch_locker: the batch_locker object
151 @param job: an Autotest job object
152 @param broken_pdus: a list of broken PDUs identified.
153
154 @returns a list of ap_configuration objects.
155 """
156 aps_to_remove = list()
157 for ap in aps:
158 failed_ap = False
159 if ap.pdu in broken_pdus:
160 ap.configuration_success = ap_constants.PDU_FAIL
161 if (ap.configuration_success == ap_constants.PDU_FAIL):
162 failed_ap = True
163 error_string = ap_constants.AP_PDU_DOWN
164 tag = ap.host_name + '_PDU'
165 elif (ap.configuration_success == ap_constants.CONFIG_FAIL):
166 failed_ap = True
167 error_string = ap_constants.AP_CONFIG_FAIL
168 tag = ap.host_name
169 if failed_ap:
170 tag += '_' + str(int(round(time.time())))
171 job.run_test('network_WiFi_ChaosConfigFailure',
172 ap=ap,
173 error_string=error_string,
174 tag=tag)
175 aps_to_remove.append(ap)
176 if error_string == ap_constants.AP_CONFIG_FAIL:
177 release_ap(ap, batch_locker, broken_pdus)
178 else:
179 # Cannot use _release_ap, since power_down will fail
Dinesh Kumar Sunkara5c632a72020-01-28 19:51:08 -0800180 batch_locker.unlock_one_ap(ap.host_name)
Roshan Piusc6db3d02015-04-28 15:15:48 -0700181 return list(set(aps) - set(aps_to_remove))
182
183
184def get_security_from_scan(ap, networks, job):
185 """Returns a list of securities determined from the scan result.
186
187 @param ap: the APConfigurator being testing against.
188 @param networks: List of matching networks returned from scan.
189 @param job: an Autotest job object
190
191 @returns a list of possible securities for the given network.
192 """
193 securities = list()
194 # Sanitize MIXED security setting for both Static and Dynamic
195 # configurators before doing the comparison.
196 security = networks[0].security
197 if (security == iw_runner.SECURITY_MIXED and
198 ap.configurator_type == ap_spec_module.CONFIGURATOR_STATIC):
199 securities = [iw_runner.SECURITY_WPA, iw_runner.SECURITY_WPA2]
200 # We have only seen WPA2 be backwards compatible, and we want
201 # to verify the configurator did the right thing. So we
202 # promote this to WPA2 only.
203 elif (security == iw_runner.SECURITY_MIXED and
204 ap.configurator_type == ap_spec_module.CONFIGURATOR_DYNAMIC):
205 securities = [iw_runner.SECURITY_WPA2]
206 else:
207 securities = [security]
208 return securities
209
210
211def scan_for_networks(ssid, capturer, ap_spec):
212 """Returns a list of matching networks after running iw scan.
213
214 @param ssid: the SSID string to look for in scan.
215 @param capturer: a packet capture device.
216 @param ap_spec: APSpec object corresponding to the AP configuration.
217
218 @returns a list of the matching networks; if no networks are found at
219 all, returns None.
220 """
221 # Setup a managed interface to perform scanning on the
222 # packet capture device.
223 freq = ap_spec_module.FREQUENCY_TABLE[ap_spec.channel]
224 wifi_if = capturer.get_wlanif(freq, 'managed')
225 capturer.host.run('%s link set %s up' % (capturer.cmd_ip, wifi_if))
harpreetee94d052019-01-03 13:44:06 -0800226
227 logging.info("Scanning for network ssid: %s", ssid)
Roshan Piusc6db3d02015-04-28 15:15:48 -0700228 # We have some APs that need a while to come on-line
harpreetee94d052019-01-03 13:44:06 -0800229 networks = list()
230 try:
231 networks = utils.poll_for_condition(
232 condition=lambda: capturer.iw_runner.wait_for_scan_result(
233 wifi_if,
234 ssids=[ssid],
235 wait_for_all=True),
236 timeout=300,
237 sleep_interval=35,
238 desc='Timed out getting IWBSSes')
239 except utils.TimeoutError:
240 pass
241
Roshan Piusc6db3d02015-04-28 15:15:48 -0700242 capturer.remove_interface(wifi_if)
243 return networks
244
245
246def return_available_networks(ap, capturer, job, ap_spec):
247 """Returns a list of networks configured as described by an APSpec.
248
249 @param ap: the APConfigurator being testing against.
250 @param capturer: a packet capture device
251 @param job: an Autotest job object.
252 @param ap_spec: APSpec object corresponding to the AP configuration.
253
254 @returns a list of networks returned from _scan_for_networks().
255 """
256 for i in range(2):
257 networks = scan_for_networks(ap.ssid, capturer, ap_spec)
258 if networks is None:
259 return None
260 if len(networks) == 0:
261 # The SSID wasn't even found, abort
262 logging.error('The ssid %s was not found in the scan', ap.ssid)
263 job.run_test('network_WiFi_ChaosConfigFailure', ap=ap,
264 error_string=ap_constants.AP_SSID_NOTFOUND,
265 tag=ap.ssid)
266 return list()
267 security = get_security_from_scan(ap, networks, job)
268 if ap_spec.security in security:
269 return networks
270 if i == 0:
271 # The SSID exists but the security is wrong, give the AP time
272 # to possible update it.
273 time.sleep(60)
274 if ap_spec.security not in security:
275 logging.error('%s was the expected security but got %s: %s',
276 ap_spec.security,
277 str(security).strip('[]'),
278 networks)
279 job.run_test('network_WiFi_ChaosConfigFailure',
280 ap=ap,
281 error_string=ap_constants.AP_SECURITY_MISMATCH,
282 tag=ap.ssid)
283 networks = list()
284 return networks
285
286
287def sanitize_client(host):
288 """Clean up logs and reboot the DUT.
289
290 @param host: the cros host object to use for RPC calls.
291 """
292 host.run('rm -rf /var/log')
293 host.reboot()
294
295
296def get_firmware_ver(host):
297 """Get firmware version of DUT from /var/log/messages.
298
299 WiFi firmware version is matched against list of known firmware versions
300 from ToT.
301
302 @param host: the cros host object to use for RPC calls.
303
304 @returns the WiFi firmware version as a string, None if the version
305 cannot be found.
306 """
Roshan Pius10b27ca2015-12-01 14:01:23 -0800307 # TODO(rpius): Need to find someway to get this info for Android/Brillo.
308 if host.get_os_type() != 'cros':
309 return None
310
Roshan Piusc6db3d02015-04-28 15:15:48 -0700311 # Firmware versions manually aggregated by installing ToT on each device
312 known_firmware_ver = ['Atheros', 'mwifiex', 'loaded firmware version',
313 'brcmf_c_preinit_dcmds']
314 # Find and return firmware version in logs
315 for firmware_ver in known_firmware_ver:
316 result_str = host.run(
317 'awk "/%s/ {print}" /var/log/messages' % firmware_ver).stdout
318 if not result_str:
319 continue
320 else:
321 if 'Atheros' in result_str:
322 pattern = '%s \w+ Rev:\d' % firmware_ver
323 elif 'mwifiex' in result_str:
324 pattern = '%s [\d.]+ \([\w.]+\)' % firmware_ver
325 elif 'loaded firmware version' in result_str:
Tien Changa50d88d2016-01-11 15:54:44 -0800326 pattern = '(\d+\.\d+\.\d+)'
Roshan Piusc6db3d02015-04-28 15:15:48 -0700327 elif 'Firmware version' in result_str:
328 pattern = '\d+\.\d+\.\d+ \([\w.]+\)'
329 else:
330 logging.info('%s does not match known firmware versions.',
331 result_str)
332 return None
333 result = re.search(pattern, result_str)
334 if result:
335 return result.group(0)
336 return None
Bindu Mahadev36d9e6e2016-03-28 16:34:40 -0700337
338
339def collect_pcap_info(tracedir, pcap_filename, try_count):
340 """Gather .trc and .trc.log files into android debug directory.
341
342 @param tracedir: string name of the directory that has the trace files.
343 @param pcap_filename: string name of the pcap file.
344 @param try_count: int Connection attempt number.
345
346 """
347 pcap_file = os.path.join(tracedir, pcap_filename)
348 pcap_log_file = os.path.join(tracedir, '%s.log' % pcap_filename)
349 debug_dir = 'android_debug_try_%d' % try_count
350 debug_dir_path = os.path.join(tracedir, 'debug/%s' % debug_dir)
351 if os.path.exists(debug_dir_path):
352 pcap_dir_path = os.path.join(debug_dir_path, 'pcap')
353 if not os.path.exists(pcap_dir_path):
354 os.makedirs(pcap_dir_path)
355 shutil.copy(pcap_file, pcap_dir_path)
356 shutil.copy(pcap_log_file, pcap_dir_path)
357 logging.debug('Copied failed packet capture data to directory')