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