blob: f63b56601a04e3a2dc59272fe17793afdc6583fc [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
40def power_down_aps(aps, broken_pdus=[]):
41 """Powers down a list of aps.
42
43 @param aps: a list of APConfigurator objects.
44 @param broken_pdus: a list of broken PDUs identified.
45 """
46 cartridge = ap_cartridge.APCartridge()
47 for ap in aps:
48 ap.power_down_router()
49 cartridge.push_configurator(ap)
50 cartridge.run_configurators(broken_pdus)
51
52
53def configure_aps(aps, ap_spec, broken_pdus=[]):
54 """Configures a given list of APs.
55
56 @param aps: a list of APConfigurator objects.
57 @param ap_spec: APSpec object corresponding to the AP configuration.
58 @param broken_pdus: a list of broken PDUs identified.
59 """
60 cartridge = ap_cartridge.APCartridge()
61 for ap in aps:
62 ap.set_using_ap_spec(ap_spec)
63 cartridge.push_configurator(ap)
64 cartridge.run_configurators(broken_pdus)
65
66
67def is_dut_healthy(client, ap):
68 """Returns if iw scan is working properly.
69
70 Sometimes iw scan will die, especially on the Atheros chips.
71 This works around that bug. See crbug.com/358716.
72
73 @param client: a wifi_client for the DUT
74 @param ap: ap_configurator object
75
76 @returns True if the DUT is healthy (iw scan works); False otherwise.
77 """
78 # The SSID doesn't matter, all that needs to be verified is that iw
79 # works.
80 networks = client.iw_runner.wait_for_scan_result(
81 client.wifi_if, ssid=ap.ssid)
82 if networks == None:
83 return False
84 return True
85
86
87def is_conn_worker_healthy(conn_worker, ap, assoc_params, job):
88 """Returns if the connection worker is working properly.
89
90 From time to time the connection worker will fail to establish a
91 connection to the APs.
92
93 @param conn_worker: conn_worker object
94 @param ap: an ap_configurator object
95 @param assoc_params: the connection association parameters
96 @param job: the Autotest job object
97
98 @returns True if the worker is healthy; False otherwise
99 """
100 if conn_worker is None:
101 return True
102 conn_status = conn_worker.connect_work_client(assoc_params)
103 if not conn_status:
104 job.run_test('network_WiFi_ChaosConfigFailure', ap=ap,
105 error_string=ap_constants.WORK_CLI_CONNECT_FAIL,
106 tag=ap.ssid)
107 # Obtain the logs from the worker
108 log_dir_name = str('worker_client_logs_%s' % ap.ssid)
109 log_dir = os.path.join(job.resultdir, log_dir_name)
110 conn_worker.host.collect_logs(
111 '/var/log', log_dir, ignore_errors=True)
112 return False
113 return True
114
115
116def release_ap(ap, batch_locker, broken_pdus=[]):
117 """Powers down and unlocks the given AP.
118
119 @param ap: the APConfigurator under test.
120 @param batch_locker: the batch locker object.
121 @param broken_pdus: a list of broken PDUs identified.
122 """
123 ap.power_down_router()
124 try:
125 ap.apply_settings()
126 except ap_configurator.PduNotResponding as e:
127 if ap.pdu not in broken_pdus:
128 broken_pdus.append(ap.pdu)
129 batch_locker.unlock_one_ap(ap.host_name)
130
131
132def filter_quarantined_and_config_failed_aps(aps, batch_locker, job,
133 broken_pdus=[]):
134 """Filter out all PDU quarantined and config failed APs.
135
136 @param aps: the list of ap_configurator objects to filter
137 @param batch_locker: the batch_locker object
138 @param job: an Autotest job object
139 @param broken_pdus: a list of broken PDUs identified.
140
141 @returns a list of ap_configuration objects.
142 """
143 aps_to_remove = list()
144 for ap in aps:
145 failed_ap = False
146 if ap.pdu in broken_pdus:
147 ap.configuration_success = ap_constants.PDU_FAIL
148 if (ap.configuration_success == ap_constants.PDU_FAIL):
149 failed_ap = True
150 error_string = ap_constants.AP_PDU_DOWN
151 tag = ap.host_name + '_PDU'
152 elif (ap.configuration_success == ap_constants.CONFIG_FAIL):
153 failed_ap = True
154 error_string = ap_constants.AP_CONFIG_FAIL
155 tag = ap.host_name
156 if failed_ap:
157 tag += '_' + str(int(round(time.time())))
158 job.run_test('network_WiFi_ChaosConfigFailure',
159 ap=ap,
160 error_string=error_string,
161 tag=tag)
162 aps_to_remove.append(ap)
163 if error_string == ap_constants.AP_CONFIG_FAIL:
164 release_ap(ap, batch_locker, broken_pdus)
165 else:
166 # Cannot use _release_ap, since power_down will fail
167 batch_locker.unlock_one_ap(ap.host_name)
168 return list(set(aps) - set(aps_to_remove))
169
170
171def get_security_from_scan(ap, networks, job):
172 """Returns a list of securities determined from the scan result.
173
174 @param ap: the APConfigurator being testing against.
175 @param networks: List of matching networks returned from scan.
176 @param job: an Autotest job object
177
178 @returns a list of possible securities for the given network.
179 """
180 securities = list()
181 # Sanitize MIXED security setting for both Static and Dynamic
182 # configurators before doing the comparison.
183 security = networks[0].security
184 if (security == iw_runner.SECURITY_MIXED and
185 ap.configurator_type == ap_spec_module.CONFIGURATOR_STATIC):
186 securities = [iw_runner.SECURITY_WPA, iw_runner.SECURITY_WPA2]
187 # We have only seen WPA2 be backwards compatible, and we want
188 # to verify the configurator did the right thing. So we
189 # promote this to WPA2 only.
190 elif (security == iw_runner.SECURITY_MIXED and
191 ap.configurator_type == ap_spec_module.CONFIGURATOR_DYNAMIC):
192 securities = [iw_runner.SECURITY_WPA2]
193 else:
194 securities = [security]
195 return securities
196
197
198def scan_for_networks(ssid, capturer, ap_spec):
199 """Returns a list of matching networks after running iw scan.
200
201 @param ssid: the SSID string to look for in scan.
202 @param capturer: a packet capture device.
203 @param ap_spec: APSpec object corresponding to the AP configuration.
204
205 @returns a list of the matching networks; if no networks are found at
206 all, returns None.
207 """
208 # Setup a managed interface to perform scanning on the
209 # packet capture device.
210 freq = ap_spec_module.FREQUENCY_TABLE[ap_spec.channel]
211 wifi_if = capturer.get_wlanif(freq, 'managed')
212 capturer.host.run('%s link set %s up' % (capturer.cmd_ip, wifi_if))
213 # We have some APs that need a while to come on-line
214 networks = capturer.iw_runner.wait_for_scan_result(
215 wifi_if, ssid=ssid, timeout_seconds=300)
216 capturer.remove_interface(wifi_if)
217 return networks
218
219
220def return_available_networks(ap, capturer, job, ap_spec):
221 """Returns a list of networks configured as described by an APSpec.
222
223 @param ap: the APConfigurator being testing against.
224 @param capturer: a packet capture device
225 @param job: an Autotest job object.
226 @param ap_spec: APSpec object corresponding to the AP configuration.
227
228 @returns a list of networks returned from _scan_for_networks().
229 """
230 for i in range(2):
231 networks = scan_for_networks(ap.ssid, capturer, ap_spec)
232 if networks is None:
233 return None
234 if len(networks) == 0:
235 # The SSID wasn't even found, abort
236 logging.error('The ssid %s was not found in the scan', ap.ssid)
237 job.run_test('network_WiFi_ChaosConfigFailure', ap=ap,
238 error_string=ap_constants.AP_SSID_NOTFOUND,
239 tag=ap.ssid)
240 return list()
241 security = get_security_from_scan(ap, networks, job)
242 if ap_spec.security in security:
243 return networks
244 if i == 0:
245 # The SSID exists but the security is wrong, give the AP time
246 # to possible update it.
247 time.sleep(60)
248 if ap_spec.security not in security:
249 logging.error('%s was the expected security but got %s: %s',
250 ap_spec.security,
251 str(security).strip('[]'),
252 networks)
253 job.run_test('network_WiFi_ChaosConfigFailure',
254 ap=ap,
255 error_string=ap_constants.AP_SECURITY_MISMATCH,
256 tag=ap.ssid)
257 networks = list()
258 return networks
259
260
261def sanitize_client(host):
262 """Clean up logs and reboot the DUT.
263
264 @param host: the cros host object to use for RPC calls.
265 """
266 host.run('rm -rf /var/log')
267 host.reboot()
268
269
270def get_firmware_ver(host):
271 """Get firmware version of DUT from /var/log/messages.
272
273 WiFi firmware version is matched against list of known firmware versions
274 from ToT.
275
276 @param host: the cros host object to use for RPC calls.
277
278 @returns the WiFi firmware version as a string, None if the version
279 cannot be found.
280 """
281 # Firmware versions manually aggregated by installing ToT on each device
282 known_firmware_ver = ['Atheros', 'mwifiex', 'loaded firmware version',
283 'brcmf_c_preinit_dcmds']
284 # Find and return firmware version in logs
285 for firmware_ver in known_firmware_ver:
286 result_str = host.run(
287 'awk "/%s/ {print}" /var/log/messages' % firmware_ver).stdout
288 if not result_str:
289 continue
290 else:
291 if 'Atheros' in result_str:
292 pattern = '%s \w+ Rev:\d' % firmware_ver
293 elif 'mwifiex' in result_str:
294 pattern = '%s [\d.]+ \([\w.]+\)' % firmware_ver
295 elif 'loaded firmware version' in result_str:
296 pattern = '(\d+\.\d+\.\d+.\d)'
297 elif 'Firmware version' in result_str:
298 pattern = '\d+\.\d+\.\d+ \([\w.]+\)'
299 else:
300 logging.info('%s does not match known firmware versions.',
301 result_str)
302 return None
303 result = re.search(pattern, result_str)
304 if result:
305 return result.group(0)
306 return None