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