1# Copyright (c) 2012 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, threading, time 6 7from autotest_lib.client.cros.crash.crash_test import CrashTest 8from autotest_lib.server import autotest, test 9from autotest_lib.client.common_lib import error 10 11_WAIT_DELAY = 15 12_LONG_TIMEOUT = 200 13_SUSPEND_TIME = 30 14_CRASH_PATHS = [CrashTest._SYSTEM_CRASH_DIR.replace("/crash",""), 15 CrashTest._FALLBACK_USER_CRASH_DIR.replace("/crash",""), 16 CrashTest._USER_CRASH_DIRS.replace("/crash","")] 17 18class platform_ExternalUsbPeripherals(test.test): 19 """Uses servo to repeatedly connect/remove USB devices during boot.""" 20 version = 1 21 22 23 def getPluggedUsbDevices(self): 24 """Determines the external USB devices plugged 25 26 @returns plugged_list: List of plugged usb devices names 27 28 """ 29 lsusb_output = self.host.run('lsusb').stdout.strip() 30 items = lsusb_output.split('\n') 31 plugged_list = [] 32 unnamed_device_count = 1 33 for item in items: 34 columns = item.split(' ') 35 if len(columns) == 6 or len(' '.join(columns[6:]).strip()) == 0: 36 logging.debug('Unnamed device located, adding generic name.') 37 name = 'Unnamed device %d' % unnamed_device_count 38 unnamed_device_count += 1 39 else: 40 name = ' '.join(columns[6:]).strip() 41 #Avoid servo components 42 if not name.startswith('Standard Microsystems Corp'): 43 plugged_list.append(name) 44 return plugged_list 45 46 47 def set_hub_power(self, on=True): 48 """Setting USB hub power status 49 50 @param on: To power on the servo-usb hub or not 51 52 """ 53 reset = 'off' 54 if not on: 55 reset = 'on' 56 self.host.servo.set('dut_hub1_rst1', reset) 57 self.pluged_status = on 58 59 60 def action_login(self): 61 """Login i.e. runs running client test 62 63 @exception TestFail failed to login within timeout. 64 65 """ 66 self.autotest_client.run_test(self.client_autotest, 67 exit_without_logout=True) 68 69 70 def wait_for_cmd_output(self, cmd, check, timeout, timeout_msg): 71 """Waits till command output is meta 72 73 @param cmd: executed command 74 @param check: string to be checked for in cmd output 75 @param timeout: max time in sec to wait for output 76 @param timeout_msg: timeout failure message 77 78 @returns True if check is found in command output; False otherwise 79 """ 80 start_time = int(time.time()) 81 time_delta = 0 82 command = '%s %s' % (cmd, check) 83 logging.debug('Command: %s', command) 84 while(self.host.run(command, ignore_status=True).exit_status != 0): 85 time_delta = int(time.time()) - start_time 86 if time_delta > timeout: 87 self.add_failure('%s - %d sec' % (timeout_msg, timeout)) 88 return False 89 time.sleep(0.5) 90 logging.debug('Succeeded in :%d sec', time_delta) 91 return True 92 93 94 def suspend_for_time(self, suspend_time=_SUSPEND_TIME): 95 """Calls the host method suspend with suspend_time argument. 96 97 @param suspend_time: time to suspend the device for. 98 99 """ 100 try: 101 self.host.suspend(suspend_time=suspend_time) 102 except error.AutoservSuspendError: 103 pass 104 105 106 def action_suspend(self): 107 """Suspend i.e. powerd_dbus_suspend and wait 108 109 @returns boot_id for the following resume 110 """ 111 boot_id = self.host.get_boot_id() 112 thread = threading.Thread(target=self.suspend_for_time) 113 thread.start() 114 self.host.test_wait_for_sleep(_LONG_TIMEOUT) 115 logging.debug('--- Suspended') 116 self.suspend_status = True 117 return boot_id 118 119 120 def action_resume(self, boot_id): 121 """Resume i.e. press power key and wait 122 123 @param boot_id: boot id obtained prior to suspending 124 125 """ 126 self.host.test_wait_for_resume(boot_id, _LONG_TIMEOUT) 127 logging.debug('--- Resumed') 128 self.suspend_status = False 129 130 def close_lid(self): 131 """Close lid through servo to suspend the device.""" 132 boot_id = self.host.get_boot_id() 133 logging.info('Closing lid...') 134 self.host.servo.lid_close() 135 self.host.test_wait_for_sleep(_LONG_TIMEOUT) 136 self.suspend_status = True 137 return boot_id 138 139 140 def open_lid(self, boot_id): 141 """Open lid through servo to resume.""" 142 logging.info('Opening lid...') 143 self.host.servo.lid_open() 144 self.host.test_wait_for_resume(boot_id, _LONG_TIMEOUT) 145 self.suspend_status = False 146 147 148 def crash_not_detected(self, crash_path): 149 """Check for kernel, browser, process crashes 150 151 @param crash_path: Crash files path 152 153 @returns True if there were not crashes; False otherwise 154 """ 155 result = True 156 if str(self.host.run('ls %s' % crash_path, 157 ignore_status=True)).find('crash') != -1: 158 crash_out = self.host.run('ls %s/crash/' % crash_path, 159 ignore_status=True).stdout 160 crash_files = crash_out.strip().split('\n') 161 for crash_file in crash_files: 162 if crash_file.find('.meta') != -1 and \ 163 crash_file.find('kernel_warning') == -1: 164 self.add_failure('CRASH DETECTED in %s/crash: %s' % 165 (crash_path, crash_file)) 166 result = False 167 return result 168 169 170 def check_plugged_usb_devices(self): 171 """Checks the plugged peripherals match device list. 172 173 @returns True if expected USB peripherals are detected; False otherwise 174 """ 175 result = True 176 if self.pluged_status and self.usb_list != None: 177 # Check for mandatory USb devices passed by usb_list flag 178 for usb_name in self.usb_list: 179 found = self.wait_for_cmd_output( 180 'lsusb | grep -E ', usb_name, _WAIT_DELAY * 4, 181 'Not detecting %s' % usb_name) 182 result = result and found 183 time.sleep(_WAIT_DELAY) 184 on_now = self.getPluggedUsbDevices() 185 if self.pluged_status: 186 if not self.diff_list.issubset(on_now): 187 missing = str(self.diff_list.difference(on_now)) 188 self.add_failure('Missing connected peripheral(s) ' 189 'when plugged: %s ' % missing) 190 result = False 191 else: 192 present = self.diff_list.intersection(on_now) 193 if len(present) > 0: 194 self.add_failure('Still presented peripheral(s) ' 195 'when unplugged: %s ' % str(present)) 196 result = False 197 return result 198 199 200 def check_usb_peripherals_details(self): 201 """Checks the effect from plugged in USB peripherals. 202 203 @returns True if command line output is matched successfuly; Else False 204 """ 205 usb_check_result = True 206 for cmd in self.usb_checks.keys(): 207 out_match_list = self.usb_checks.get(cmd) 208 if cmd.startswith('loggedin:'): 209 if not self.login_status: 210 continue 211 cmd = cmd.replace('loggedin:','') 212 # Run the usb check command 213 for out_match in out_match_list: 214 match_result = self.wait_for_cmd_output( 215 cmd, out_match, _WAIT_DELAY * 4, 216 'USB CHECKS DETAILS failed at %s %s:' % (cmd, out_match)) 217 usb_check_result = usb_check_result and match_result 218 return usb_check_result 219 220 221 def check_status(self): 222 """Performs checks after each action: 223 - for USB detected devices 224 - for generated crash files 225 - peripherals effect checks on cmd line 226 227 @returns True if all of the iteration checks pass; False otherwise. 228 """ 229 result = True 230 if not self.suspend_status: 231 # Detect the USB peripherals 232 result = self.check_plugged_usb_devices() 233 # Check for crash files 234 if self.crash_check: 235 for crash_path in _CRASH_PATHS: 236 result = result and self.crash_not_detected(crash_path) 237 if self.pluged_status and (self.usb_checks != None): 238 # Check for plugged USB devices details 239 result = result and self.check_usb_peripherals_details() 240 return result 241 242 243 def remove_crash_data(self): 244 """Delete crash meta files if present""" 245 for crash_path in _CRASH_PATHS: 246 if not self.crash_not_detected(crash_path): 247 self.host.run('rm -rf %s/crash' % crash_path, 248 ignore_status=True) 249 250 251 def add_failure(self, reason): 252 """ Adds a failure reason to list of failures to be reported at end 253 254 @param reason: failure reason to record 255 256 """ 257 if self.action_step is not None: 258 self.fail_reasons.append('%s FAILS - %s' % 259 (self.action_step, reason)) 260 261 262 263 def cleanup(self): 264 """Disconnect servo hub""" 265 self.set_hub_power(False) 266 self.host.servo.set('usb_mux_sel3', 'servo_sees_usbkey') 267 self.host.reboot() 268 269 270 def run_once(self, host, client_autotest, action_sequence, repeat, 271 usb_list=None, usb_checks=None, 272 crash_check=False, stress_rack=False): 273 self.client_autotest = client_autotest 274 self.host = host 275 self.autotest_client = autotest.Autotest(self.host) 276 self.usb_list = usb_list 277 self.usb_checks = usb_checks 278 self.crash_check = crash_check 279 280 self.suspend_status = False 281 self.login_status = False 282 self.fail_reasons = list() 283 self.action_step = None 284 285 self.host.servo.switch_usbkey('dut') 286 self.set_hub_power(False) 287 if (stress_rack): 288 self.host.servo.set('usb_mux_sel1', 'dut_sees_usbkey') 289 else: 290 self.host.servo.set('usb_mux_sel1', 'servo_sees_usbkey') 291 self.host.servo.set('usb_mux_oe2', 'off') 292 self.host.servo.set('usb_mux_oe4', 'off') 293 self.host.servo.set('usb_mux_sel3', 'dut_sees_usbkey') 294 time.sleep(_WAIT_DELAY) 295 296 # Collect USB peripherals when unplugged 297 self.set_hub_power(False) 298 time.sleep(_WAIT_DELAY) 299 off_list = self.getPluggedUsbDevices() 300 301 # Collect USB peripherals when plugged 302 self.set_hub_power(True) 303 time.sleep(_WAIT_DELAY * 2) 304 on_list = self.getPluggedUsbDevices() 305 306 self.diff_list = set(on_list).difference(set(off_list)) 307 if len(self.diff_list) == 0: 308 # Fail if no devices detected after 309 raise error.TestError('No connected devices were detected. Make ' 310 'sure the devices are connected to USB_KEY ' 311 'and DUT_HUB1_USB on the servo board.') 312 logging.debug('Connected devices list: %s', self.diff_list) 313 314 board = host.get_board().split(':')[1] 315 action_sequence = action_sequence.upper() 316 actions = action_sequence.split(',') 317 boot_id = 0 318 self.remove_crash_data() 319 320 for iteration in xrange(1, repeat + 1): 321 step = 0 322 for action in actions: 323 step += 1 324 action = action.strip() 325 self.action_step = 'STEP %d.%d. %s' % (iteration, step, action) 326 logging.info(self.action_step) 327 328 if action == 'RESUME': 329 self.action_resume(boot_id) 330 time.sleep(_WAIT_DELAY) 331 elif action == 'OPENLID': 332 self.open_lid(boot_id) 333 time.sleep(_WAIT_DELAY) 334 elif action == 'UNPLUG': 335 self.set_hub_power(False) 336 elif action == 'PLUG': 337 self.set_hub_power(True) 338 elif self.suspend_status == False: 339 if action.startswith('LOGIN'): 340 if self.login_status: 341 logging.debug('Skipping login. Already logged in.') 342 continue 343 else: 344 self.action_login() 345 self.login_status = True 346 elif action == 'REBOOT': 347 self.host.reboot() 348 time.sleep(_WAIT_DELAY * 3) 349 self.login_status = False 350 elif action == 'SUSPEND': 351 boot_id = self.action_suspend() 352 elif action == 'CLOSELID': 353 boot_id = self.close_lid() 354 else: 355 logging.info('WRONG ACTION: %s .', self.action_step) 356 357 self.check_status() 358 359 if self.fail_reasons: 360 raise error.TestFail('Failures reported: %s' % 361 str(self.fail_reasons)) 362