# Copyright (c) 2012 The Chromium OS Authors. All rights reserved. # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. import logging, threading, time from autotest_lib.client.cros.crash_test import CrashTest from autotest_lib.server import autotest, test from autotest_lib.client.common_lib import error _WAIT_DELAY = 15 _LONG_TIMEOUT = 200 _SUSPEND_TIME = 30 _WAKE_PRESS_IN_SEC = 0.2 _CRASH_PATHS = [CrashTest._SYSTEM_CRASH_DIR.replace("/crash",""), CrashTest._FALLBACK_USER_CRASH_DIR.replace("/crash",""), CrashTest._USER_CRASH_DIRS.replace("/crash","")] class platform_ExternalUsbPeripherals(test.test): """Uses servo to repeatedly connect/remove USB devices during boot.""" version = 1 def getPluggedUsbDevices(self): """Determines the external USB devices plugged @returns plugged_list: List of plugged usb devices names """ lsusb_output = self.host.run('lsusb').stdout.strip() items = lsusb_output.split('\n') plugged_list = [] unnamed_device_count = 1 for item in items: columns = item.split(' ') if len(columns) == 6 or len(' '.join(columns[6:]).strip()) == 0: logging.debug('Unnamed device located, adding generic name.') name = 'Unnamed device %d' % unnamed_device_count unnamed_device_count += 1 else: name = ' '.join(columns[6:]).strip() #Avoid servo components if not name.startswith('Standard Microsystems Corp'): plugged_list.append(name) return plugged_list def set_hub_power(self, on=True): """Setting USB hub power status @param on: To power on the servo-usb hub or not """ reset = 'off' if not on: reset = 'on' self.host.servo.set('dut_hub1_rst1', reset) self.pluged_status = on def action_login(self): """Login i.e. runs running client test @exception TestFail failed to login within timeout. """ self.autotest_client.run_test(self.client_autotest, exit_without_logout=True) def wait_for_cmd_output(self, cmd, check, timeout, timeout_msg): """Waits till command output is meta @param cmd: executed command @param check: string to be checked for in cmd output @param timeout: max time in sec to wait for output @param timeout_msg: timeout failure message @returns True if check is found in command output; False otherwise """ start_time = int(time.time()) time_delta = 0 command = '%s %s' % (cmd, check) logging.debug('Command: %s', command) while(self.host.run(command, ignore_status=True).exit_status != 0): time_delta = int(time.time()) - start_time if time_delta > timeout: self.add_failure('%s - %d sec' % (timeout_msg, timeout)) return False time.sleep(0.5) logging.debug('Succeeded in :%d sec', time_delta) return True def suspend_for_time(self, suspend_time=_SUSPEND_TIME): """Calls the host method suspend with suspend_time argument. @param suspend_time: time to suspend the device for. """ try: self.host.suspend(suspend_time=suspend_time) except error.AutoservSuspendError: pass def action_suspend(self): """Suspend i.e. powerd_dbus_suspend and wait @returns boot_id for the following resume """ boot_id = self.host.get_boot_id() thread = threading.Thread(target=self.suspend_for_time) thread.start() self.host.test_wait_for_sleep(_LONG_TIMEOUT) logging.debug('--- Suspended') self.suspend_status = True return boot_id def action_resume(self, boot_id): """Resume i.e. press power key and wait @param boot_id: boot id obtained prior to suspending """ self.host.servo.power_key(_WAKE_PRESS_IN_SEC) self.host.test_wait_for_resume(boot_id, _LONG_TIMEOUT) logging.debug('--- Resumed') self.suspend_status = False def crash_not_detected(self, crash_path): """Check for kernel, browser, process crashes @param crash_path: Crash files path @returns True if there were not crashes; False otherwise """ result = True if str(self.host.run('ls %s' % crash_path, ignore_status=True)).find('crash') != -1: crash_out = self.host.run('ls %s/crash/' % crash_path).stdout crash_files = crash_out.strip().split('\n') for crash_file in crash_files: if crash_file.find('.meta') != -1 and \ crash_file.find('kernel_warning') == -1: self.add_failure('CRASH DETECTED in %s/crash: %s' % (crash_path, crash_file)) result = False return result def check_plugged_usb_devices(self): """Checks the plugged peripherals match device list. @returns True if expected USB peripherals are detected; False otherwise """ result = True if self.pluged_status and self.usb_list != None: # Check for mandatory USb devices passed by usb_list flag for usb_name in self.usb_list: found = self.wait_for_cmd_output( 'lsusb | grep -E ', usb_name, _WAIT_DELAY * 4, 'Not detecting %s' % usb_name) result = result and found time.sleep(_WAIT_DELAY) on_now = self.getPluggedUsbDevices() if self.pluged_status: if not self.diff_list.issubset(on_now): missing = str(self.diff_list.difference(on_now)) self.add_failure('Missing connected peripheral(s) ' 'when plugged: %s ' % missing) result = False else: present = self.diff_list.intersection(on_now) if len(present) > 0: self.add_failure('Still presented peripheral(s) ' 'when unplugged: %s ' % str(present)) result = False return result def check_usb_peripherals_details(self): """Checks the effect from plugged in USB peripherals. @returns True if command line output is matched successfuly; Else False """ usb_check_result = True for cmd in self.usb_checks.keys(): out_match_list = self.usb_checks.get(cmd) if cmd.startswith('loggedin:'): if not self.login_status: continue cmd = cmd.replace('loggedin:','') # Run the usb check command for out_match in out_match_list: match_result = self.wait_for_cmd_output( cmd, out_match, _WAIT_DELAY * 4, 'USB CHECKS DETAILS failed at %s %s:' % (cmd, out_match)) usb_check_result = usb_check_result and match_result return usb_check_result def check_status(self): """Performs checks after each action: - for USB detected devices - for generated crash files - peripherals effect checks on cmd line @returns True if all of the iteration checks pass; False otherwise. """ result = True if not self.suspend_status: # Detect the USB peripherals result = self.check_plugged_usb_devices() # Check for crash files if self.crash_check: for crash_path in _CRASH_PATHS: result = result and self.crash_not_detected(crash_path) if self.pluged_status and (self.usb_checks != None): # Check for plugged USB devices details result = result and self.check_usb_peripherals_details() return result def remove_crash_data(self): """Delete crash meta files if present""" for crash_path in _CRASH_PATHS: if not self.crash_not_detected(crash_path): self.host.run('rm -rf %s/crash' % crash_path, ignore_status=True) def add_failure(self, reason): """ Adds a failure reason to list of failures to be reported at end @param reason: failure reason to record """ if self.action_step is not None: self.fail_reasons.append('%s FAILS - %s' % (self.action_step, reason)) def cleanup(self): """Disconnect servo hub""" self.set_hub_power(False) self.host.servo.set('usb_mux_sel3', 'servo_sees_usbkey') def run_once(self, host, client_autotest, action_sequence, repeat, usb_list=None, usb_checks=None, crash_check=False): self.client_autotest = client_autotest self.host = host self.autotest_client = autotest.Autotest(self.host) self.usb_list = usb_list self.usb_checks = usb_checks self.crash_check = crash_check self.suspend_status = False self.login_status = False self.fail_reasons = list() self.action_step = None self.host.servo.switch_usbkey('dut') self.host.servo.set('usb_mux_sel3', 'dut_sees_usbkey') time.sleep(_WAIT_DELAY) # Collect USB peripherals when unplugged self.set_hub_power(False) time.sleep(_WAIT_DELAY) off_list = self.getPluggedUsbDevices() # Collect USB peripherals when plugged self.set_hub_power(True) time.sleep(_WAIT_DELAY * 2) on_list = self.getPluggedUsbDevices() self.diff_list = set(on_list).difference(set(off_list)) if len(self.diff_list) == 0: # Fail if no devices detected after raise error.TestError('No connected devices were detected. Make ' 'sure the devices are connected to USB_KEY ' 'and DUT_HUB1_USB on the servo board.') logging.debug('Connected devices list: %s', self.diff_list) board = host.get_board().split(':')[1] action_sequence = action_sequence.upper() actions = action_sequence.split(',') boot_id = 0 self.remove_crash_data() for iteration in xrange(1, repeat + 1): step = 0 for action in actions: step += 1 action = action.strip() self.action_step = 'STEP %d.%d. %s' % (iteration, step, action) logging.info(self.action_step) if action == 'RESUME': self.action_resume(boot_id) time.sleep(_WAIT_DELAY) elif action == 'UNPLUG': self.set_hub_power(False) elif action == 'PLUG': self.set_hub_power(True) elif self.suspend_status == False: if action.startswith('LOGIN'): if self.login_status: logging.debug('Skipping login. Already logged in.') continue else: self.action_login() self.login_status = True elif action == 'REBOOT': self.host.reboot() time.sleep(_WAIT_DELAY * 3) self.login_status = False elif action == 'SUSPEND': boot_id = self.action_suspend() else: logging.info('WRONG ACTION: %s .', self.action_step) self.check_status() if self.fail_reasons: raise error.TestFail('Failures reported: %s' % str(self.fail_reasons))