autoupdate_EndToEndTest.py revision f789cf3a52c720344062f0a6c782bb758f08b189
10ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold# Copyright (c) 2012 The Chromium OS Authors. All rights reserved. 20ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold# Use of this source code is governed by a BSD-style license that can be 30ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold# found in the LICENSE file. 40ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold 52f1ae9f1f2464ebbeb226a6669fa90ef211a179cChris Sosaimport collections 60ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnoldimport json 70ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnoldimport logging 86c55bdb98e967675456a71a0971b81058536cac8Chris Sosaimport os 9a0261611f9be78ee9d456fd50bea81e9eda07c17Gilad Arnoldimport socket 1015384a4ded97f171906a72106791d19e96e68fa7Gilad Arnoldimport time 110ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnoldimport urllib2 120ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnoldimport urlparse 1303901089c0db27508a6b2ff61ae8b75eba77cf37Gilad Arnold 146c55bdb98e967675456a71a0971b81058536cac8Chris Sosafrom autotest_lib.client.bin import utils as client_utils 156c55bdb98e967675456a71a0971b81058536cac8Chris Sosafrom autotest_lib.client.common_lib import error, global_config 160ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnoldfrom autotest_lib.client.common_lib.cros import autoupdater, dev_server 17f789cf3a52c720344062f0a6c782bb758f08b189Don Garrettfrom autotest_lib.server import autotest, hosts, test 182f1ae9f1f2464ebbeb226a6669fa90ef211a179cChris Sosafrom autotest_lib.server.cros.dynamic_suite import tools 190ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold 200ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold 210ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnolddef _wait(secs, desc=None): 220ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold """Emits a log message and sleeps for a given number of seconds.""" 230338ff3b754d52b41ca2d0432164a757daa1112dGilad Arnold msg = 'Waiting %s seconds' % secs 240ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold if desc: 250ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold msg += ' (%s)' % desc 260ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold logging.info(msg) 270ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold time.sleep(secs) 280ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold 290ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold 30ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosaclass ExpectedUpdateEventChainFailed(error.TestFail): 31ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa """Raised if we fail to receive an expected event in a chain.""" 32ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa 33f789cf3a52c720344062f0a6c782bb758f08b189Don Garrettclass RequiredArgumentMissing(error.TestFail): 34f789cf3a52c720344062f0a6c782bb758f08b189Don Garrett """Raised if we fail to receive an expected event in a chain.""" 35f789cf3a52c720344062f0a6c782bb758f08b189Don Garrett 36ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa 370338ff3b754d52b41ca2d0432164a757daa1112dGilad Arnold# Update event types. 380338ff3b754d52b41ca2d0432164a757daa1112dGilad ArnoldEVENT_TYPE_DOWNLOAD_COMPLETE = '1' 390338ff3b754d52b41ca2d0432164a757daa1112dGilad ArnoldEVENT_TYPE_INSTALL_COMPLETE = '2' 400338ff3b754d52b41ca2d0432164a757daa1112dGilad ArnoldEVENT_TYPE_UPDATE_COMPLETE = '3' 410338ff3b754d52b41ca2d0432164a757daa1112dGilad ArnoldEVENT_TYPE_DOWNLOAD_STARTED = '13' 420338ff3b754d52b41ca2d0432164a757daa1112dGilad ArnoldEVENT_TYPE_DOWNLOAD_FINISHED = '14' 430338ff3b754d52b41ca2d0432164a757daa1112dGilad Arnold 440338ff3b754d52b41ca2d0432164a757daa1112dGilad Arnold# Update event results. 450338ff3b754d52b41ca2d0432164a757daa1112dGilad ArnoldEVENT_RESULT_ERROR = '0' 460338ff3b754d52b41ca2d0432164a757daa1112dGilad ArnoldEVENT_RESULT_SUCCESS = '1' 470338ff3b754d52b41ca2d0432164a757daa1112dGilad ArnoldEVENT_RESULT_SUCCESS_REBOOT = '2' 480338ff3b754d52b41ca2d0432164a757daa1112dGilad ArnoldEVENT_RESULT_UPDATE_DEFERRED = '9' 490338ff3b754d52b41ca2d0432164a757daa1112dGilad Arnold 500338ff3b754d52b41ca2d0432164a757daa1112dGilad Arnold 510ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnoldclass ExpectedUpdateEvent(object): 52ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa """Defines an expected event in a host update process. 53ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa 54ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa Attrs: 55ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa _expected_attrs: Dictionary of attributes that should match events 56ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa received. If attribute is not provided, assumes match. 57ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa error_message: What we should error out with if we fail to verify this 58ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa expected event. 59ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa """ 6045f02ae47134953169805d281992c9edf0019250Chris Sosa 6145f02ae47134953169805d281992c9edf0019250Chris Sosa # Omaha event types/results, from update_engine/omaha_request_action.h 6245f02ae47134953169805d281992c9edf0019250Chris Sosa # These are stored in dict form in order to easily print out the keys. 630338ff3b754d52b41ca2d0432164a757daa1112dGilad Arnold _EVENT_TYPE_DICT = { 640338ff3b754d52b41ca2d0432164a757daa1112dGilad Arnold EVENT_TYPE_DOWNLOAD_COMPLETE: 'download_complete', 650338ff3b754d52b41ca2d0432164a757daa1112dGilad Arnold EVENT_TYPE_INSTALL_COMPLETE: 'install_complete', 660338ff3b754d52b41ca2d0432164a757daa1112dGilad Arnold EVENT_TYPE_UPDATE_COMPLETE: 'update_complete', 670338ff3b754d52b41ca2d0432164a757daa1112dGilad Arnold EVENT_TYPE_DOWNLOAD_STARTED: 'download_started', 680338ff3b754d52b41ca2d0432164a757daa1112dGilad Arnold EVENT_TYPE_DOWNLOAD_FINISHED: 'download_finished' 690338ff3b754d52b41ca2d0432164a757daa1112dGilad Arnold } 700338ff3b754d52b41ca2d0432164a757daa1112dGilad Arnold 710338ff3b754d52b41ca2d0432164a757daa1112dGilad Arnold _EVENT_RESULT_DICT = { 720338ff3b754d52b41ca2d0432164a757daa1112dGilad Arnold EVENT_RESULT_ERROR: 'error', 730338ff3b754d52b41ca2d0432164a757daa1112dGilad Arnold EVENT_RESULT_SUCCESS: 'success', 740338ff3b754d52b41ca2d0432164a757daa1112dGilad Arnold EVENT_RESULT_SUCCESS_REBOOT: 'success_reboot', 750338ff3b754d52b41ca2d0432164a757daa1112dGilad Arnold EVENT_RESULT_UPDATE_DEFERRED: 'update_deferred' 760338ff3b754d52b41ca2d0432164a757daa1112dGilad Arnold } 770338ff3b754d52b41ca2d0432164a757daa1112dGilad Arnold 780338ff3b754d52b41ca2d0432164a757daa1112dGilad Arnold _ATTR_NAME_DICT_MAP = { 790338ff3b754d52b41ca2d0432164a757daa1112dGilad Arnold 'event_type': _EVENT_TYPE_DICT, 800338ff3b754d52b41ca2d0432164a757daa1112dGilad Arnold 'event_result': _EVENT_RESULT_DICT, 810338ff3b754d52b41ca2d0432164a757daa1112dGilad Arnold } 820338ff3b754d52b41ca2d0432164a757daa1112dGilad Arnold 830338ff3b754d52b41ca2d0432164a757daa1112dGilad Arnold _VALID_TYPES = set(_EVENT_TYPE_DICT.keys()) 840338ff3b754d52b41ca2d0432164a757daa1112dGilad Arnold _VALID_RESULTS = set(_EVENT_RESULT_DICT.keys()) 8545f02ae47134953169805d281992c9edf0019250Chris Sosa 860ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold def __init__(self, event_type=None, event_result=None, version=None, 87ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa previous_version=None, error_message=None): 880338ff3b754d52b41ca2d0432164a757daa1112dGilad Arnold if event_type and event_type not in self._VALID_TYPES: 8945f02ae47134953169805d281992c9edf0019250Chris Sosa raise ValueError('event_type %s is not valid.' % event_type) 9045f02ae47134953169805d281992c9edf0019250Chris Sosa 910338ff3b754d52b41ca2d0432164a757daa1112dGilad Arnold if event_result and event_result not in self._VALID_RESULTS: 9245f02ae47134953169805d281992c9edf0019250Chris Sosa raise ValueError('event_result %s is not valid.' % event_result) 9345f02ae47134953169805d281992c9edf0019250Chris Sosa 940ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold self._expected_attrs = { 950ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold 'event_type': event_type, 960ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold 'event_result': event_result, 970ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold 'version': version, 980ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold 'previous_version': previous_version, 990ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold } 100ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa self.error_message = error_message 1010ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold 1020ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold 1030338ff3b754d52b41ca2d0432164a757daa1112dGilad Arnold @staticmethod 1040338ff3b754d52b41ca2d0432164a757daa1112dGilad Arnold def _attr_val_str(attr_val, helper_dict, default=None): 1050338ff3b754d52b41ca2d0432164a757daa1112dGilad Arnold """Returns an enriched attribute value string, or default.""" 1060338ff3b754d52b41ca2d0432164a757daa1112dGilad Arnold if not attr_val: 1070338ff3b754d52b41ca2d0432164a757daa1112dGilad Arnold return default 1080338ff3b754d52b41ca2d0432164a757daa1112dGilad Arnold 1090338ff3b754d52b41ca2d0432164a757daa1112dGilad Arnold s = str(attr_val) 1100338ff3b754d52b41ca2d0432164a757daa1112dGilad Arnold if helper_dict: 1110338ff3b754d52b41ca2d0432164a757daa1112dGilad Arnold s += ':%s' % helper_dict.get(attr_val, 'unknown') 1120338ff3b754d52b41ca2d0432164a757daa1112dGilad Arnold 1130338ff3b754d52b41ca2d0432164a757daa1112dGilad Arnold return s 1140338ff3b754d52b41ca2d0432164a757daa1112dGilad Arnold 1150338ff3b754d52b41ca2d0432164a757daa1112dGilad Arnold 1160338ff3b754d52b41ca2d0432164a757daa1112dGilad Arnold def _attr_name_and_values(self, attr_name, expected_attr_val, 1170338ff3b754d52b41ca2d0432164a757daa1112dGilad Arnold actual_attr_val=None): 1180338ff3b754d52b41ca2d0432164a757daa1112dGilad Arnold """Returns an attribute name, expected and actual value strings. 1190338ff3b754d52b41ca2d0432164a757daa1112dGilad Arnold 1200338ff3b754d52b41ca2d0432164a757daa1112dGilad Arnold This will return (name, expected, actual); the returned value for 1210338ff3b754d52b41ca2d0432164a757daa1112dGilad Arnold actual will be None if its respective input is None/empty. 1220338ff3b754d52b41ca2d0432164a757daa1112dGilad Arnold 1230338ff3b754d52b41ca2d0432164a757daa1112dGilad Arnold """ 1240338ff3b754d52b41ca2d0432164a757daa1112dGilad Arnold helper_dict = self._ATTR_NAME_DICT_MAP.get(attr_name) 1250338ff3b754d52b41ca2d0432164a757daa1112dGilad Arnold expected_attr_val_str = self._attr_val_str(expected_attr_val, 1260338ff3b754d52b41ca2d0432164a757daa1112dGilad Arnold helper_dict, 1270338ff3b754d52b41ca2d0432164a757daa1112dGilad Arnold default='any') 1280338ff3b754d52b41ca2d0432164a757daa1112dGilad Arnold actual_attr_val_str = self._attr_val_str(actual_attr_val, helper_dict) 1290338ff3b754d52b41ca2d0432164a757daa1112dGilad Arnold 1300338ff3b754d52b41ca2d0432164a757daa1112dGilad Arnold return attr_name, expected_attr_val_str, actual_attr_val_str 1310338ff3b754d52b41ca2d0432164a757daa1112dGilad Arnold 1320338ff3b754d52b41ca2d0432164a757daa1112dGilad Arnold 1330ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold def __str__(self): 1340338ff3b754d52b41ca2d0432164a757daa1112dGilad Arnold return ' '.join(['%s=%s' % 1350338ff3b754d52b41ca2d0432164a757daa1112dGilad Arnold self._attr_name_and_values(attr_name, attr_val)[0:2] 1360ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold for attr_name, attr_val 1370ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold in self._expected_attrs.iteritems()]) 1380ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold 1390ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold 1400ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold def verify(self, actual_event): 1410ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold """Verify the attributes of an actual event. 1420ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold 143ae6acd9b3e1c6f56310cdb4e05737122b7b2818cChris Sosa @param actual_event: a dictionary containing event attributes 1440ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold 1450ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold @return True if all attributes as expected, False otherwise. 1460ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold 1470ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold """ 1480ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold return all([self._verify_attr(attr_name, expected_attr_val, 1490ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold actual_event.get(attr_name)) 1500ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold for attr_name, expected_attr_val 1510ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold in self._expected_attrs.iteritems() if expected_attr_val]) 1520ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold 1530ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold 1540ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold def _verify_attr(self, attr_name, expected_attr_val, actual_attr_val): 1550ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold """Verifies that an actual log event attributes matches expected on. 1560ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold 1570ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold @param attr_name: name of the attribute to verify 1580ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold @param expected_attr_val: expected attribute value 1590ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold @param actual_attr_val: actual attribute value 1600ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold 1610ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold @return True if actual value is present and matches, False otherwise. 1620ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold 1630ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold """ 16445f02ae47134953169805d281992c9edf0019250Chris Sosa # None values are assumed to be missing and non-matching. 16545f02ae47134953169805d281992c9edf0019250Chris Sosa if not actual_attr_val: 1660338ff3b754d52b41ca2d0432164a757daa1112dGilad Arnold logging.error('No value found for %s (expected %s)', 1670338ff3b754d52b41ca2d0432164a757daa1112dGilad Arnold *self._attr_name_and_values(attr_name, 1680338ff3b754d52b41ca2d0432164a757daa1112dGilad Arnold expected_attr_val)[0:2]) 16945f02ae47134953169805d281992c9edf0019250Chris Sosa return False 17045f02ae47134953169805d281992c9edf0019250Chris Sosa 1710338ff3b754d52b41ca2d0432164a757daa1112dGilad Arnold # Convert actual value to a string. 17245f02ae47134953169805d281992c9edf0019250Chris Sosa actual_attr_val = str(actual_attr_val) 17345f02ae47134953169805d281992c9edf0019250Chris Sosa 17445f02ae47134953169805d281992c9edf0019250Chris Sosa if not actual_attr_val == expected_attr_val: 1750338ff3b754d52b41ca2d0432164a757daa1112dGilad Arnold # We allow expected version numbers (e.g. 2940.0.0) to be contained 1760338ff3b754d52b41ca2d0432164a757daa1112dGilad Arnold # in actual values (2940.0.0-a1); this is necessary for the test to 1770338ff3b754d52b41ca2d0432164a757daa1112dGilad Arnold # pass with developer / non-release images. 1780338ff3b754d52b41ca2d0432164a757daa1112dGilad Arnold if 'version' in attr_name and expected_attr_val in actual_attr_val: 1790338ff3b754d52b41ca2d0432164a757daa1112dGilad Arnold logging.info('Expected %s (%s) contained in actual value (%s) ' 1800338ff3b754d52b41ca2d0432164a757daa1112dGilad Arnold 'but does not match exactly', 1810338ff3b754d52b41ca2d0432164a757daa1112dGilad Arnold *self._attr_name_and_values( 1820338ff3b754d52b41ca2d0432164a757daa1112dGilad Arnold attr_name, expected_attr_val, 1830338ff3b754d52b41ca2d0432164a757daa1112dGilad Arnold actual_attr_val=actual_attr_val)) 184fec1349a3e70eb41dce8bc07a8c63563d23d64b2Chris Sosa return True 185fec1349a3e70eb41dce8bc07a8c63563d23d64b2Chris Sosa 1860338ff3b754d52b41ca2d0432164a757daa1112dGilad Arnold logging.error('Expected %s (%s) different from actual value (%s)', 1870338ff3b754d52b41ca2d0432164a757daa1112dGilad Arnold *self._attr_name_and_values( 1880338ff3b754d52b41ca2d0432164a757daa1112dGilad Arnold attr_name, expected_attr_val, 1890338ff3b754d52b41ca2d0432164a757daa1112dGilad Arnold actual_attr_val=actual_attr_val)) 1900ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold return False 1910ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold 1920ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold return True 1930ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold 1940ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold 1950ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnoldclass ExpectedUpdateEventChain(object): 1960ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold """Defines a chain of expected update events.""" 1970ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold def __init__(self, *expected_event_chain_args): 1980ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold """Initialize the chain object. 1990ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold 2000ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold @param expected_event_chain_args: list of tuples arguments, each 2010ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold containing a timeout (in seconds) and an ExpectedUpdateEvent 2020ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold object. 2030ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold 2040ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold """ 2050ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold self._expected_event_chain = expected_event_chain_args 2060ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold 2070ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold 2080ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold def _format_event_with_timeout(self, timeout, expected_event): 2090ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold return ('%s %s' % 2100ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold (expected_event, 2110ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold ('within %s seconds' % timeout) if timeout 2120ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold else 'indefinitely')) 2130ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold 2140ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold 2150ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold def __str__(self): 2160ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold return ('[%s]' % 2170ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold ', '.join( 2180ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold [self._format_event_with_timeout(timeout, expected_event) 2190ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold for timeout, expected_event 2200ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold in self._expected_event_chain])) 2210ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold 2220ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold 2230ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold def __repr__(self): 2240ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold return str(self._expected_event_chain) 2250ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold 2260ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold 2270ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold def verify(self, get_next_event): 2280ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold """Verifies that an actual stream of events complies. 2290ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold 2300ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold @param get_next_event: a function returning the next event 2310ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold 232ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa @raises ExpectedUpdateEventChainFailed if we failed to verify an event. 2330ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold 2340ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold """ 2350ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold for timeout, expected_event in self._expected_event_chain: 2360338ff3b754d52b41ca2d0432164a757daa1112dGilad Arnold logging.info('Expecting %s', 237ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa self._format_event_with_timeout(timeout, 238ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa expected_event)) 2390ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold if not self._verify_event_with_timeout( 2400ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold timeout, expected_event, get_next_event): 241ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa logging.error('Failed expected event: %s', 242ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa expected_event.error_message) 243ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa raise ExpectedUpdateEventChainFailed( 244ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa expected_event.error_message) 2450ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold 2460ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold 2470ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold def _verify_event_with_timeout(self, timeout, expected_event, 2480ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold get_next_event): 2490ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold """Verify an expected event occurs within a given timeout. 2500ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold 2510ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold @param timeout: specified in seconds 2520ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold @param expected_event: an expected event specification 2530ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold @param get_next_event: function returning the next event in a stream 2540ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold 2550ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold @return True if event complies, False otherwise. 2560ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold 2570ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold """ 2580ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold base_timestamp = curr_timestamp = time.time() 2590ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold expired_timestamp = base_timestamp + timeout 2600ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold while curr_timestamp <= expired_timestamp: 2610ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold new_event = get_next_event() 2620ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold if new_event: 2630338ff3b754d52b41ca2d0432164a757daa1112dGilad Arnold logging.info('Event received after %s seconds', 2640338ff3b754d52b41ca2d0432164a757daa1112dGilad Arnold round(curr_timestamp - base_timestamp, 1)) 2650ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold return expected_event.verify(new_event) 2660ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold 2670ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold # No new events, sleep for one second only (so we don't miss 2680ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold # events at the end of the allotted timeout). 2690ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold time.sleep(1) 2700ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold curr_timestamp = time.time() 2710ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold 2720338ff3b754d52b41ca2d0432164a757daa1112dGilad Arnold logging.error('Timeout expired') 2730ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold return False 2740ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold 2750ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold 2760ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnoldclass UpdateEventLogVerifier(object): 2770ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold """Verifies update event chains on a devserver update log.""" 27803901089c0db27508a6b2ff61ae8b75eba77cf37Gilad Arnold def __init__(self, event_log_url, url_request_timeout=None): 2790ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold self._event_log_url = event_log_url 28003901089c0db27508a6b2ff61ae8b75eba77cf37Gilad Arnold self._url_request_timeout = url_request_timeout 2810ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold self._event_log = [] 2820ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold self._num_consumed_events = 0 2830ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold 2840ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold 2850ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold def verify_expected_event_chain(self, expected_event_chain): 286ae6acd9b3e1c6f56310cdb4e05737122b7b2818cChris Sosa """Verify a given event chain. 287ae6acd9b3e1c6f56310cdb4e05737122b7b2818cChris Sosa 288ae6acd9b3e1c6f56310cdb4e05737122b7b2818cChris Sosa @param expected_event_chain: instance of expected event chain. 289ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa 290ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa @raises ExpectedUpdateEventChainFailed if we failed to verify the an 291ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa event. 292ae6acd9b3e1c6f56310cdb4e05737122b7b2818cChris Sosa """ 293ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa expected_event_chain.verify(self._get_next_log_event) 2940ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold 2950ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold 2960ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold def _get_next_log_event(self): 2970ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold """Returns the next event in an event log. 2980ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold 2990ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold Uses the URL handed to it during initialization to obtain the host log 3000ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold from a devserver. If new events are encountered, the first of them is 3010ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold consumed and returned. 3020ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold 3030ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold @return The next new event in the host log, as reported by devserver; 30403901089c0db27508a6b2ff61ae8b75eba77cf37Gilad Arnold None if no such event was found or an error occurred. 3050ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold 3060ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold """ 3070ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold # (Re)read event log from devserver, if necessary. 3080ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold if len(self._event_log) <= self._num_consumed_events: 30903901089c0db27508a6b2ff61ae8b75eba77cf37Gilad Arnold try: 31003901089c0db27508a6b2ff61ae8b75eba77cf37Gilad Arnold if self._url_request_timeout: 31103901089c0db27508a6b2ff61ae8b75eba77cf37Gilad Arnold conn = urllib2.urlopen(self._event_log_url, 31203901089c0db27508a6b2ff61ae8b75eba77cf37Gilad Arnold timeout=self._url_request_timeout) 31303901089c0db27508a6b2ff61ae8b75eba77cf37Gilad Arnold else: 31403901089c0db27508a6b2ff61ae8b75eba77cf37Gilad Arnold conn = urllib2.urlopen(self._event_log_url) 31503901089c0db27508a6b2ff61ae8b75eba77cf37Gilad Arnold except urllib2.URLError, e: 3160338ff3b754d52b41ca2d0432164a757daa1112dGilad Arnold logging.warning('Failed to read event log url: %s', e) 31703901089c0db27508a6b2ff61ae8b75eba77cf37Gilad Arnold return None 318a0261611f9be78ee9d456fd50bea81e9eda07c17Gilad Arnold except socket.timeout, e: 319a0261611f9be78ee9d456fd50bea81e9eda07c17Gilad Arnold logging.warning('Timed out reading event log url: %s', e) 320a0261611f9be78ee9d456fd50bea81e9eda07c17Gilad Arnold return None 32103901089c0db27508a6b2ff61ae8b75eba77cf37Gilad Arnold 3220ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold event_log_resp = conn.read() 3230ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold conn.close() 3240ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold self._event_log = json.loads(event_log_resp) 3250ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold 32603901089c0db27508a6b2ff61ae8b75eba77cf37Gilad Arnold # Return next new event, if one is found. 3270ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold if len(self._event_log) > self._num_consumed_events: 3280ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold new_event = self._event_log[self._num_consumed_events] 3290ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold self._num_consumed_events += 1 3300338ff3b754d52b41ca2d0432164a757daa1112dGilad Arnold logging.info('Consumed new event: %s', new_event) 3310ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold return new_event 3320ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold 3330ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold 334793344b359304c31d78197788b55cfbbe2636025Chris Sosaclass OmahaDevserverFailedToStart(error.TestError): 335793344b359304c31d78197788b55cfbbe2636025Chris Sosa """Raised when a omaha devserver fails to start.""" 336793344b359304c31d78197788b55cfbbe2636025Chris Sosa 337793344b359304c31d78197788b55cfbbe2636025Chris Sosa 3380ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnoldclass OmahaDevserver(object): 3390ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold """Spawns a test-private devserver instance.""" 340793344b359304c31d78197788b55cfbbe2636025Chris Sosa # How long to wait for a devserver to start. 3410ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold _WAIT_FOR_DEVSERVER_STARTED_SECONDS = 15 342793344b359304c31d78197788b55cfbbe2636025Chris Sosa 343793344b359304c31d78197788b55cfbbe2636025Chris Sosa # How long to sleep between checks to see if a devserver is up. 34403901089c0db27508a6b2ff61ae8b75eba77cf37Gilad Arnold _WAIT_SLEEP_INTERVAL = 1 3450ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold 346793344b359304c31d78197788b55cfbbe2636025Chris Sosa # If a previous devserver exists, how long to wait in seconds before 347793344b359304c31d78197788b55cfbbe2636025Chris Sosa # attempting to reconnect. 348793344b359304c31d78197788b55cfbbe2636025Chris Sosa _TIME_TO_LET_PORT_FREE = 15 349793344b359304c31d78197788b55cfbbe2636025Chris Sosa 350793344b359304c31d78197788b55cfbbe2636025Chris Sosa # How many times to attempt to start a devserver. 351793344b359304c31d78197788b55cfbbe2636025Chris Sosa _NUM_DEVSERVER_ATTEMPTS = 5 352793344b359304c31d78197788b55cfbbe2636025Chris Sosa 3530ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold 35403901089c0db27508a6b2ff61ae8b75eba77cf37Gilad Arnold def __init__(self, omaha_host, devserver_dir, dut_ip_addr, 355ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa update_payload_staged_url): 3560ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold """Starts a private devserver instance, operating at Omaha capacity. 3570ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold 3580ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold @param omaha_host: host address where the devserver is spawned. 35903901089c0db27508a6b2ff61ae8b75eba77cf37Gilad Arnold @param devserver_dir: path to the devserver source directory 3600ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold @param dut_ip_addr: the IP address of the client DUT, used for deriving 3610ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold a unique port number. 362ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa @param update_payload_staged_url: URL to provision for update requests. 3630ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold 3640ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold """ 365ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa if not update_payload_staged_url: 3660338ff3b754d52b41ca2d0432164a757daa1112dGilad Arnold raise error.TestError('Missing update payload url') 3670ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold 3686c55bdb98e967675456a71a0971b81058536cac8Chris Sosa self._omaha_host = omaha_host 3690ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold self._omaha_port = self._get_unique_port(dut_ip_addr) 3706c55bdb98e967675456a71a0971b81058536cac8Chris Sosa self._devserver_dir = devserver_dir 371ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa self._update_payload_staged_url = update_payload_staged_url 3726c55bdb98e967675456a71a0971b81058536cac8Chris Sosa 3736c55bdb98e967675456a71a0971b81058536cac8Chris Sosa self._devserver_ssh = hosts.SSHHost(self._omaha_host, 3746c55bdb98e967675456a71a0971b81058536cac8Chris Sosa user=os.environ['USER']) 3756c55bdb98e967675456a71a0971b81058536cac8Chris Sosa self._devserver_output = '/tmp/devserver.%s' % self._omaha_port 3766c55bdb98e967675456a71a0971b81058536cac8Chris Sosa self._devserver_pid = None 3770ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold 3786c55bdb98e967675456a71a0971b81058536cac8Chris Sosa 379793344b359304c31d78197788b55cfbbe2636025Chris Sosa def _wait_for_devserver_to_start(self): 380793344b359304c31d78197788b55cfbbe2636025Chris Sosa """Waits until the devserver starts within the time limit. 381793344b359304c31d78197788b55cfbbe2636025Chris Sosa 382793344b359304c31d78197788b55cfbbe2636025Chris Sosa Raises: 383793344b359304c31d78197788b55cfbbe2636025Chris Sosa OmahaDevserverFailedToStart: If the time limit is reached and we 384793344b359304c31d78197788b55cfbbe2636025Chris Sosa cannot connect to the devserver. 385793344b359304c31d78197788b55cfbbe2636025Chris Sosa """ 386793344b359304c31d78197788b55cfbbe2636025Chris Sosa logging.warning('Waiting for devserver to start up.') 387793344b359304c31d78197788b55cfbbe2636025Chris Sosa timeout = self._WAIT_FOR_DEVSERVER_STARTED_SECONDS 388793344b359304c31d78197788b55cfbbe2636025Chris Sosa netloc = self.get_netloc() 389793344b359304c31d78197788b55cfbbe2636025Chris Sosa current_time = time.time() 390793344b359304c31d78197788b55cfbbe2636025Chris Sosa deadline = current_time + timeout 391ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa remote_cmd = 'test -e /proc/%s' % self._devserver_pid 392ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa result = self._devserver_ssh.run(remote_cmd, ignore_status=True) 393ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa if result.exit_status != 0: 394ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa self._devserver_pid = None 395ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa raise OmahaDevserverFailedToStart( 396ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa 'Failed to start -- check devserver_dir path is correct.') 397793344b359304c31d78197788b55cfbbe2636025Chris Sosa while(current_time < deadline): 398793344b359304c31d78197788b55cfbbe2636025Chris Sosa if dev_server.DevServer.devserver_healthy('http://%s' % netloc, 399793344b359304c31d78197788b55cfbbe2636025Chris Sosa timeout_min=0.1): 400793344b359304c31d78197788b55cfbbe2636025Chris Sosa return 401793344b359304c31d78197788b55cfbbe2636025Chris Sosa 402793344b359304c31d78197788b55cfbbe2636025Chris Sosa # TODO(milleral): Refactor once crbug.com/221626 is resolved. 403793344b359304c31d78197788b55cfbbe2636025Chris Sosa time.sleep(self._WAIT_SLEEP_INTERVAL) 404793344b359304c31d78197788b55cfbbe2636025Chris Sosa current_time = time.time() 405793344b359304c31d78197788b55cfbbe2636025Chris Sosa else: 406793344b359304c31d78197788b55cfbbe2636025Chris Sosa raise OmahaDevserverFailedToStart( 407793344b359304c31d78197788b55cfbbe2636025Chris Sosa 'The test failed to establish a connection to the omaha ' 408793344b359304c31d78197788b55cfbbe2636025Chris Sosa 'devserver it set up on port %d. Check the dumped ' 409793344b359304c31d78197788b55cfbbe2636025Chris Sosa 'devserver logs for more information.' % self._omaha_port) 410793344b359304c31d78197788b55cfbbe2636025Chris Sosa 411793344b359304c31d78197788b55cfbbe2636025Chris Sosa 4126c55bdb98e967675456a71a0971b81058536cac8Chris Sosa def start_devserver(self): 413793344b359304c31d78197788b55cfbbe2636025Chris Sosa """Starts the devserver and confirms it is up. 414793344b359304c31d78197788b55cfbbe2636025Chris Sosa 415793344b359304c31d78197788b55cfbbe2636025Chris Sosa Stores the remote pid in self._devserver_pid and raises an exception 416793344b359304c31d78197788b55cfbbe2636025Chris Sosa if the devserver failed to start. 417793344b359304c31d78197788b55cfbbe2636025Chris Sosa 418793344b359304c31d78197788b55cfbbe2636025Chris Sosa Raises: 419793344b359304c31d78197788b55cfbbe2636025Chris Sosa OmahaDevserverFailedToStart: If the time limit is reached and we 420793344b359304c31d78197788b55cfbbe2636025Chris Sosa cannot connect to the devserver. 4216c55bdb98e967675456a71a0971b81058536cac8Chris Sosa """ 4222f1ae9f1f2464ebbeb226a6669fa90ef211a179cChris Sosa update_payload_url_base, update_payload_path = self._split_url( 423ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa self._update_payload_staged_url) 4246c55bdb98e967675456a71a0971b81058536cac8Chris Sosa # Invoke the Omaha/devserver on the remote server. 4250ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold cmdlist = [ 4266c55bdb98e967675456a71a0971b81058536cac8Chris Sosa '%s/devserver.py' % self._devserver_dir, 4270ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold '--payload=%s' % update_payload_path, 4280ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold '--port=%d' % self._omaha_port, 4290ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold '--remote_payload', 4300ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold '--urlbase=%s' % update_payload_url_base, 4310ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold '--max_updates=1', 4320ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold '--host_log', 4330ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold ] 4346c55bdb98e967675456a71a0971b81058536cac8Chris Sosa remote_cmd = '( %s ) </dev/null >%s 2>&1 & echo $!' % ( 435793344b359304c31d78197788b55cfbbe2636025Chris Sosa ' '.join(cmdlist), self._devserver_output) 436793344b359304c31d78197788b55cfbbe2636025Chris Sosa 437793344b359304c31d78197788b55cfbbe2636025Chris Sosa # Devserver may have some trouble re-using the port if previously 438793344b359304c31d78197788b55cfbbe2636025Chris Sosa # created so create in a loop with a max number of attempts. 439793344b359304c31d78197788b55cfbbe2636025Chris Sosa for i in range(self._NUM_DEVSERVER_ATTEMPTS): 440793344b359304c31d78197788b55cfbbe2636025Chris Sosa # In the remote case that a previous devserver is still running, 441793344b359304c31d78197788b55cfbbe2636025Chris Sosa # kill it. 442793344b359304c31d78197788b55cfbbe2636025Chris Sosa devserver_pid = self._remote_devserver_pid() 443793344b359304c31d78197788b55cfbbe2636025Chris Sosa if devserver_pid: 444793344b359304c31d78197788b55cfbbe2636025Chris Sosa logging.warning('Previous devserver still running. Killing.') 445793344b359304c31d78197788b55cfbbe2636025Chris Sosa self._kill_devserver_pid(devserver_pid) 446793344b359304c31d78197788b55cfbbe2636025Chris Sosa self._devserver_ssh.run('rm -f %s' % self._devserver_output, 447793344b359304c31d78197788b55cfbbe2636025Chris Sosa ignore_status=True) 448793344b359304c31d78197788b55cfbbe2636025Chris Sosa time.sleep(self._TIME_TO_LET_PORT_FREE) 449793344b359304c31d78197788b55cfbbe2636025Chris Sosa 450793344b359304c31d78197788b55cfbbe2636025Chris Sosa logging.info('Starting devserver with %r', remote_cmd) 451ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa try: 452ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa self._devserver_pid = self._devserver_ssh.run_output(remote_cmd) 453ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa except error.AutoservRunError as e: 454ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa logging.debug('Failed to ssh into the devserver: %s', e) 455ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa logging.error('If you are running this locally it means you ' 456ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa 'did not configure ssh correctly.') 457ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa raise error.TestError('Failed to ssh into the devserver.') 458ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa 459793344b359304c31d78197788b55cfbbe2636025Chris Sosa try: 460793344b359304c31d78197788b55cfbbe2636025Chris Sosa self._wait_for_devserver_to_start() 461793344b359304c31d78197788b55cfbbe2636025Chris Sosa return 462793344b359304c31d78197788b55cfbbe2636025Chris Sosa except OmahaDevserverFailedToStart: 463793344b359304c31d78197788b55cfbbe2636025Chris Sosa if i + 1 < self._NUM_DEVSERVER_ATTEMPTS: 464793344b359304c31d78197788b55cfbbe2636025Chris Sosa logging.error('Devserver failed to start, re-attempting.') 465793344b359304c31d78197788b55cfbbe2636025Chris Sosa else: 466793344b359304c31d78197788b55cfbbe2636025Chris Sosa self.dump_devserver_log() 467793344b359304c31d78197788b55cfbbe2636025Chris Sosa raise 4686c55bdb98e967675456a71a0971b81058536cac8Chris Sosa 4696c55bdb98e967675456a71a0971b81058536cac8Chris Sosa 4706c55bdb98e967675456a71a0971b81058536cac8Chris Sosa def _kill_devserver_pid(self, pid): 4716c55bdb98e967675456a71a0971b81058536cac8Chris Sosa """Kills devserver with given pid and verifies devserver is down. 4726c55bdb98e967675456a71a0971b81058536cac8Chris Sosa 4736c55bdb98e967675456a71a0971b81058536cac8Chris Sosa @param pid: The pid of the devserver to kill. 4746c55bdb98e967675456a71a0971b81058536cac8Chris Sosa 4756c55bdb98e967675456a71a0971b81058536cac8Chris Sosa @raise client_utils.TimeoutError if we are unable to kill the devserver 4766c55bdb98e967675456a71a0971b81058536cac8Chris Sosa within the default timeouts (11 seconds). 4776c55bdb98e967675456a71a0971b81058536cac8Chris Sosa """ 4786c55bdb98e967675456a71a0971b81058536cac8Chris Sosa def _devserver_down(): 4796c55bdb98e967675456a71a0971b81058536cac8Chris Sosa return self._remote_devserver_pid() == None 4806c55bdb98e967675456a71a0971b81058536cac8Chris Sosa 4816c55bdb98e967675456a71a0971b81058536cac8Chris Sosa self._devserver_ssh.run('kill %s' % pid) 4826c55bdb98e967675456a71a0971b81058536cac8Chris Sosa try: 483469cbb2e9436a4efcc90dbfe3e6ecfe14a3ea3c9Gilad Arnold client_utils.poll_for_condition( 484469cbb2e9436a4efcc90dbfe3e6ecfe14a3ea3c9Gilad Arnold _devserver_down, sleep_interval=1, desc='devserver down') 4856c55bdb98e967675456a71a0971b81058536cac8Chris Sosa return 4866c55bdb98e967675456a71a0971b81058536cac8Chris Sosa except client_utils.TimeoutError: 487469cbb2e9436a4efcc90dbfe3e6ecfe14a3ea3c9Gilad Arnold logging.warning('Could not gracefully shut down devserver, ' 488469cbb2e9436a4efcc90dbfe3e6ecfe14a3ea3c9Gilad Arnold 'retrying with SIGKILL') 4896c55bdb98e967675456a71a0971b81058536cac8Chris Sosa 4906c55bdb98e967675456a71a0971b81058536cac8Chris Sosa self._devserver_ssh.run('kill -9 %s' % pid) 491469cbb2e9436a4efcc90dbfe3e6ecfe14a3ea3c9Gilad Arnold client_utils.poll_for_condition( 492469cbb2e9436a4efcc90dbfe3e6ecfe14a3ea3c9Gilad Arnold _devserver_down, timeout=5, sleep_interval=1, 493469cbb2e9436a4efcc90dbfe3e6ecfe14a3ea3c9Gilad Arnold desc='devserver down') 4946c55bdb98e967675456a71a0971b81058536cac8Chris Sosa 4956c55bdb98e967675456a71a0971b81058536cac8Chris Sosa 4966c55bdb98e967675456a71a0971b81058536cac8Chris Sosa def _remote_devserver_pid(self): 4976c55bdb98e967675456a71a0971b81058536cac8Chris Sosa """If a devserver is running on our port, return its pid.""" 4986c55bdb98e967675456a71a0971b81058536cac8Chris Sosa # fuser returns pid in its stdout if found. 4996c55bdb98e967675456a71a0971b81058536cac8Chris Sosa result = self._devserver_ssh.run('fuser -n tcp %d' % self._omaha_port, 5006c55bdb98e967675456a71a0971b81058536cac8Chris Sosa ignore_status=True) 5016c55bdb98e967675456a71a0971b81058536cac8Chris Sosa if result.exit_status == 0: 5026c55bdb98e967675456a71a0971b81058536cac8Chris Sosa return result.stdout.strip() 5036c55bdb98e967675456a71a0971b81058536cac8Chris Sosa 5046c55bdb98e967675456a71a0971b81058536cac8Chris Sosa 5056c55bdb98e967675456a71a0971b81058536cac8Chris Sosa def get_netloc(self): 5066c55bdb98e967675456a71a0971b81058536cac8Chris Sosa """Returns the netloc (host:port) of the devserver.""" 5076c55bdb98e967675456a71a0971b81058536cac8Chris Sosa if not self._devserver_pid: 5080338ff3b754d52b41ca2d0432164a757daa1112dGilad Arnold raise error.TestError('No running omaha/devserver') 5096c55bdb98e967675456a71a0971b81058536cac8Chris Sosa 5106c55bdb98e967675456a71a0971b81058536cac8Chris Sosa 5116c55bdb98e967675456a71a0971b81058536cac8Chris Sosa return '%s:%s' % (self._omaha_host, self._omaha_port) 5120ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold 5136c55bdb98e967675456a71a0971b81058536cac8Chris Sosa 5142f1ae9f1f2464ebbeb226a6669fa90ef211a179cChris Sosa def get_update_url(self): 5152f1ae9f1f2464ebbeb226a6669fa90ef211a179cChris Sosa """Returns the update_url you can use to update via this server.""" 5162f1ae9f1f2464ebbeb226a6669fa90ef211a179cChris Sosa return urlparse.urlunsplit(('http', self.get_netloc(), '/update', 5172f1ae9f1f2464ebbeb226a6669fa90ef211a179cChris Sosa '', '')) 5182f1ae9f1f2464ebbeb226a6669fa90ef211a179cChris Sosa 5192f1ae9f1f2464ebbeb226a6669fa90ef211a179cChris Sosa 52019426059c61c1b318b5d4bd04d70dacb63ec827dChris Sosa def dump_devserver_log(self, logging_level=logging.ERROR): 52119426059c61c1b318b5d4bd04d70dacb63ec827dChris Sosa """Dump the devserver log to the autotest log. 52219426059c61c1b318b5d4bd04d70dacb63ec827dChris Sosa 52319426059c61c1b318b5d4bd04d70dacb63ec827dChris Sosa @param logging_level: logging level (from logging) to log the output. 52419426059c61c1b318b5d4bd04d70dacb63ec827dChris Sosa """ 5256c55bdb98e967675456a71a0971b81058536cac8Chris Sosa if self._devserver_pid: 52619426059c61c1b318b5d4bd04d70dacb63ec827dChris Sosa logging.log(logging_level, self._devserver_ssh.run_output( 5276c55bdb98e967675456a71a0971b81058536cac8Chris Sosa 'cat %s' % self._devserver_output)) 5280ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold 5290ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold 5300ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold @staticmethod 5310ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold def _split_url(url): 5322f1ae9f1f2464ebbeb226a6669fa90ef211a179cChris Sosa """Splits a URL into the URL base and path.""" 5330ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold split_url = urlparse.urlsplit(url) 5340ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold url_base = urlparse.urlunsplit( 5352f1ae9f1f2464ebbeb226a6669fa90ef211a179cChris Sosa (split_url.scheme, split_url.netloc, '', '', '')) 5362f1ae9f1f2464ebbeb226a6669fa90ef211a179cChris Sosa url_path = split_url.path 5372f1ae9f1f2464ebbeb226a6669fa90ef211a179cChris Sosa return url_base, url_path.lstrip('/') 5380ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold 5390ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold 5400ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold @staticmethod 5410ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold def _get_unique_port(dut_ip_addr): 5420ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold """Compute a unique IP port based on the DUT's IP address. 5430ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold 5440ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold We need a mapping that can be mirrored by a DUT running an official 5450ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold image, based only on the DUT's own state. Here, we simply take the two 5460ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold least significant bytes in the DUT's IPv4 address and bitwise-OR them 5470ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold with 0xc0000, resulting in a 16-bit IP port within the 5480ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold private/unallocated range. Using the least significant bytes of the IP 5490ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold address guarantees (sort of) that we'll have a unique mapping in a 5500ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold small lab setting. 5510ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold 5520ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold """ 5530ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold ip_addr_bytes = [int(byte_str) for byte_str in dut_ip_addr.split('.')] 5540ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold return (((ip_addr_bytes[2] << 8) | ip_addr_bytes[3] | 0x8000) & ~0x4000) 5550ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold 5560ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold 5570ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold def kill(self): 5580ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold """Kill private devserver, wait for it to die.""" 5596c55bdb98e967675456a71a0971b81058536cac8Chris Sosa if not self._devserver_pid: 560ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa logging.error('No running omaha/devserver.') 561ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa return 5626c55bdb98e967675456a71a0971b81058536cac8Chris Sosa 5630338ff3b754d52b41ca2d0432164a757daa1112dGilad Arnold logging.info('Killing omaha/devserver') 56419426059c61c1b318b5d4bd04d70dacb63ec827dChris Sosa logging.debug('Final devserver log before killing') 5656c55bdb98e967675456a71a0971b81058536cac8Chris Sosa self._kill_devserver_pid(self._devserver_pid) 56619426059c61c1b318b5d4bd04d70dacb63ec827dChris Sosa self.dump_devserver_log(logging.DEBUG) 5676c55bdb98e967675456a71a0971b81058536cac8Chris Sosa self._devserver_ssh.run('rm -f %s' % self._devserver_output) 5680ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold 5690ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold 5700ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnoldclass autoupdate_EndToEndTest(test.test): 5710ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold """Complete update test between two Chrome OS releases. 5720ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold 5730ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold Performs an end-to-end test of updating a ChromeOS device from one version 5740ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold to another. This script requires a running (possibly remote) servod 5750ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold instance connected to an actual servo board, which controls the DUT. It 5760ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold also assumes that a corresponding target (update) image was staged for 577ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa download on a central staging devserver. 5780ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold 5790ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold The test performs the following steps: 5800ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold 5810ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold 0. Stages the source image and target update payload on the central 5820ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold Lorry/devserver. 5830ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold 1. Spawns a private Omaha/devserver instance, configured to return the 5840ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold target (update) image URL in response for an update check. 5850ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold 2. Connects to servod. 5860ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold a. Resets the DUT to a known initial state. 5870ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold b. Installs a source image on the DUT via recovery. 5880ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold 3. Reboots the DUT with the new image. 5890ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold 4. Triggers an update check at the DUT. 5900ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold 5. Watches as the DUT obtains an update and applies it. 5910ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold 6. Repeats 3-5, ensuring that the next update check shows the new image 5920ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold version. 5930ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold 594ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa Some notes on naming: 595ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa devserver: Refers to a machine running the Chrome OS Update Devserver. 596ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa autotest_devserver: An autotest wrapper to interact with a devserver. 597ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa Can be used to stage artifacts to a devserver. While 598ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa this can also be used to update a machine, we do not 599ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa use it for that purpose in this test as we manage 600ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa updates with out own devserver instances (see below). 601ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa omaha_devserver: This test's wrapper of a devserver running for the 602ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa purposes of emulating omaha. This test controls the 603ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa lifetime of this devserver instance and is separate 604ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa from the autotest lab's devserver's instances which are 605ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa only used for staging and hosting artifacts (because they 606ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa scale). These are run on the same machines as the actual 607ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa autotest devservers which are used for staging but on 608ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa different ports. 609ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa *staged_url's: In this case staged refers to the fact that these items 610ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa are available to be downloaded statically from these urls 611ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa e.g. 'localhost:8080/static/my_file.gz'. These are usually 612ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa given after staging an artifact using a autotest_devserver 613ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa though they can be re-created given enough assumptions. 614ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa *update_url's: Urls refering to the update RPC on a given omaha devserver. 615ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa Since we always use an instantiated omaha devserver to run 616ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa updates, these will always reference an existing instance 617ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa of an omaha devserver that we just created for the purposes 618ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa of updating. 619ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa devserver_hostname: At the start of each test, we choose a devserver 620ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa machine in the lab for the test. We use the devserver 621ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa instance there (access by autotest_devserver) to stage 622ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa artifacts. However, we also use the same host to start 623ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa omaha devserver instances for updating machines with 624ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa (that reference the staged paylaods on the autotest 625ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa devserver instance). This hostname refers to that 626ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa machine we are using (since it's always the same for 627ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa both staging/omaha'ing). 628ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa 6290ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold """ 6300ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold version = 1 6310ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold 6320ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold # Timeout periods, given in seconds. 633f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa _WAIT_AFTER_SHUTDOWN_SECONDS = 10 634f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa _WAIT_AFTER_UPDATE_SECONDS = 20 635f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa _WAIT_FOR_USB_INSTALL_SECONDS = 4 * 60 636f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa _WAIT_FOR_MP_RECOVERY_SECONDS = 8 * 60 6370ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold _WAIT_FOR_INITIAL_UPDATE_CHECK_SECONDS = 12 * 60 6384f4426a2184828ad195fbb0b66b34f809a619236Chris Sosa # TODO(sosa): Investigate why this needs to be so long (this used to be 6394f4426a2184828ad195fbb0b66b34f809a619236Chris Sosa # 120 and regressed). 6404f4426a2184828ad195fbb0b66b34f809a619236Chris Sosa _WAIT_FOR_DOWNLOAD_STARTED_SECONDS = 4 * 60 6416c55bdb98e967675456a71a0971b81058536cac8Chris Sosa _WAIT_FOR_DOWNLOAD_COMPLETED_SECONDS = 10 * 60 642f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa _WAIT_FOR_UPDATE_COMPLETED_SECONDS = 4 * 60 6430ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold _WAIT_FOR_UPDATE_CHECK_AFTER_REBOOT_SECONDS = 15 * 60 64403901089c0db27508a6b2ff61ae8b75eba77cf37Gilad Arnold _DEVSERVER_HOSTLOG_REQUEST_TIMEOUT_SECONDS = 30 6450ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold 64615384a4ded97f171906a72106791d19e96e68fa7Gilad Arnold _STATEFUL_UPDATE_FILENAME = 'stateful.tgz' 64715384a4ded97f171906a72106791d19e96e68fa7Gilad Arnold 6482f1ae9f1f2464ebbeb226a6669fa90ef211a179cChris Sosa # Named tuple containing urls for staged urls needed for test. 6492f1ae9f1f2464ebbeb226a6669fa90ef211a179cChris Sosa # source_url: url to find the update payload for the source image. 6502f1ae9f1f2464ebbeb226a6669fa90ef211a179cChris Sosa # source_stateful_url: url to find the stateful payload for the source 6512f1ae9f1f2464ebbeb226a6669fa90ef211a179cChris Sosa # image. 6522f1ae9f1f2464ebbeb226a6669fa90ef211a179cChris Sosa # target_url: url to find the update payload for the target image. 6532f1ae9f1f2464ebbeb226a6669fa90ef211a179cChris Sosa # target_stateful_url: url to find the stateful payload for the target 6542f1ae9f1f2464ebbeb226a6669fa90ef211a179cChris Sosa # image. 6552f1ae9f1f2464ebbeb226a6669fa90ef211a179cChris Sosa _STAGED_URLS = collections.namedtuple( 6562f1ae9f1f2464ebbeb226a6669fa90ef211a179cChris Sosa 'StagedUrls', 6572f1ae9f1f2464ebbeb226a6669fa90ef211a179cChris Sosa ['source_url', 'source_stateful_url', 'target_url', 6582f1ae9f1f2464ebbeb226a6669fa90ef211a179cChris Sosa 'target_stateful_url']) 6592f1ae9f1f2464ebbeb226a6669fa90ef211a179cChris Sosa 6600ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold 661f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa def _servo_dut_power_up(self): 6620ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold """Powers up the DUT, optionally simulating a Ctrl-D key press.""" 663f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa self._host.servo.power_short_press() 664f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa if self._dev_mode: 665f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa self._host.servo.pass_devmode() 6660ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold 6670ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold 668f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa def _servo_dut_reboot(self, disconnect_usbkey=False): 6690ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold """Reboots a DUT. 6700ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold 671e7bd9369b76e2bbdc230c3c1aeb9afc6e6ec60baVadim Bendebury @param disconnect_usbkey: detach USB flash device from the DUT before 672e7bd9369b76e2bbdc230c3c1aeb9afc6e6ec60baVadim Bendebury powering it back up; this is useful when (for example) a USB 673e7bd9369b76e2bbdc230c3c1aeb9afc6e6ec60baVadim Bendebury booted device need not see the attached USB key after the 674e7bd9369b76e2bbdc230c3c1aeb9afc6e6ec60baVadim Bendebury reboot. 6750ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold 6760ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold @raise error.TestFail if DUT fails to reboot. 6770ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold 6780ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold """ 6790338ff3b754d52b41ca2d0432164a757daa1112dGilad Arnold logging.info('Rebooting dut') 680f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa self._host.servo.power_long_press() 6810ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold _wait(self._WAIT_AFTER_SHUTDOWN_SECONDS, 'after shutdown') 682e7bd9369b76e2bbdc230c3c1aeb9afc6e6ec60baVadim Bendebury if disconnect_usbkey: 683f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa self._host.servo.switch_usbkey('host') 684f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa 685f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa self._servo_dut_power_up() 686f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa if self._use_test_image: 687f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa if not self._host.wait_up(timeout=self._host.BOOT_TIMEOUT): 6880ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold raise error.TestFail( 6890338ff3b754d52b41ca2d0432164a757daa1112dGilad Arnold 'DUT %s failed to boot after %d secs' % 690f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa (self._host.ip, self._host.BOOT_TIMEOUT)) 6910ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold else: 6920ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold # TODO(garnold) chromium-os:33766: implement waiting for MP-signed 6930ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold # images; ideas include waiting for a ping reply, or using a GPIO 6940ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold # signal. 6950ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold pass 6960ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold 6970ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold 698ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa def _install_mp_image(self, staged_image_url): 6990ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold """Installs an MP-signed recovery image on a DUT. 7000ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold 701ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa @param staged_image_url: URL of the image on a Lorry/devserver 7020ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold """ 7030ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold # Flash DUT with source image version, using recovery. 7040338ff3b754d52b41ca2d0432164a757daa1112dGilad Arnold logging.info('Installing source mp-signed image via recovery: %s', 705ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa staged_image_url) 706f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa self._host.servo.install_recovery_image( 707ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa staged_image_url, 7080ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold wait_timeout=self._WAIT_FOR_MP_RECOVERY_SECONDS) 7090ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold 7100ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold # Reboot the DUT after installation. 711f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa self._servo_dut_reboot(disconnect_usbkey=True) 7120ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold 7130ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold 714ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa def _install_test_image_with_servo(self, staged_image_url): 7150ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold """Installs a test image on a DUT, booted via recovery. 7160ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold 717ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa @param staged_image_url: URL of the image on the devserver 7180ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold @param is_dev_nmode: whether or not the DUT is in dev mode 7190ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold 7200ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold @raise error.TestFail if DUT cannot boot the test image from USB; 7210ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold AutotestHostRunError if failed to run the install command on the 7220ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold DUT. 7230ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold 7240ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold """ 7250338ff3b754d52b41ca2d0432164a757daa1112dGilad Arnold logging.info('Installing source test image via recovery: %s', 726ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa staged_image_url) 727ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa self._host.servo.install_recovery_image(staged_image_url) 7280338ff3b754d52b41ca2d0432164a757daa1112dGilad Arnold logging.info('Waiting for image to boot') 729f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa if not self._host.wait_up(timeout=self._host.USB_BOOT_TIMEOUT): 7300ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold raise error.TestFail( 7310338ff3b754d52b41ca2d0432164a757daa1112dGilad Arnold 'DUT %s boot from usb timed out after %d secs' % 732f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa (self._host, self._host.USB_BOOT_TIMEOUT)) 7330338ff3b754d52b41ca2d0432164a757daa1112dGilad Arnold logging.info('Installing new image onto ssd') 7340ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold try: 735f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa cmd_result = self._host.run( 7360ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold 'chromeos-install --yes', 7370ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold timeout=self._WAIT_FOR_USB_INSTALL_SECONDS, 7380ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold stdout_tee=None, stderr_tee=None) 739f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa except error.AutotestHostRunError: 7400ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold # Dump stdout (with stderr) to the error log. 7410338ff3b754d52b41ca2d0432164a757daa1112dGilad Arnold logging.error('Command failed, stderr:\n' + cmd_result.stderr) 7420ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold raise 7430ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold 7440ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold # Reboot the DUT after installation. 745f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa self._servo_dut_reboot(disconnect_usbkey=True) 7460ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold 7470ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold 7482f1ae9f1f2464ebbeb226a6669fa90ef211a179cChris Sosa def _trigger_test_update(self, omaha_devserver): 7490ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold """Trigger an update check on a test image. 7500ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold 7512f1ae9f1f2464ebbeb226a6669fa90ef211a179cChris Sosa @param omaha_devserver: Instance of OmahaDevserver that will serve the 7522f1ae9f1f2464ebbeb226a6669fa90ef211a179cChris Sosa update. 7530ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold @raise RootFSUpdateError if anything went wrong. 7540ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold 7550ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold """ 7562f1ae9f1f2464ebbeb226a6669fa90ef211a179cChris Sosa updater = autoupdater.ChromiumOSUpdater( 7572f1ae9f1f2464ebbeb226a6669fa90ef211a179cChris Sosa omaha_devserver.get_update_url(), host=self._host) 7580ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold updater.trigger_update() 7590ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold 7600ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold 761f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa def _get_rootdev(self): 76209706f18ea3878f1b173a63fdc92f2fc6450bb4dGilad Arnold """Returns the partition device containing the rootfs on a host. 76309706f18ea3878f1b173a63fdc92f2fc6450bb4dGilad Arnold 76409706f18ea3878f1b173a63fdc92f2fc6450bb4dGilad Arnold @return The rootfs partition device (string). 76509706f18ea3878f1b173a63fdc92f2fc6450bb4dGilad Arnold 76609706f18ea3878f1b173a63fdc92f2fc6450bb4dGilad Arnold @raise AutotestHostRunError if command failed to run on host. 76709706f18ea3878f1b173a63fdc92f2fc6450bb4dGilad Arnold 76809706f18ea3878f1b173a63fdc92f2fc6450bb4dGilad Arnold """ 76966d74078caa789f80d4a43c82b61d6016a0cf430Chris Sosa return self._host.run('rootdev -s').stdout.strip() 77009706f18ea3878f1b173a63fdc92f2fc6450bb4dGilad Arnold 77109706f18ea3878f1b173a63fdc92f2fc6450bb4dGilad Arnold 772ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa def _stage_image(self, autotest_devserver, image_uri): 773ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa """Stage a Chrome OS image onto a staging devserver. 7740ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold 775ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa @param autotest_devserver: instance of client.common_lib.dev_server to 776ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa use to stage the image. 777ae6acd9b3e1c6f56310cdb4e05737122b7b2818cChris Sosa @param image_uri: The uri of the image. 778ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa @return URL of the staged image on the staging devserver. 7790ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold 7800ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold @raise error.TestError if there's a problem with staging. 7810ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold 7820ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold """ 783f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa if self._use_test_image: 7840ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold # For this call, we just need the URL path up to the image.zip file 7850ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold # (exclusive). 7860ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold image_uri_path = urlparse.urlsplit(image_uri).path.partition( 7870ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold 'image.zip')[0].strip('/') 7880ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold try: 789ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa autotest_devserver.stage_artifacts(image_uri_path, 790ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa ['test_image']) 791ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa return autotest_devserver.get_test_image_url(image_uri_path) 7920ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold except dev_server.DevServerException, e: 7930ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold raise error.TestError( 7940338ff3b754d52b41ca2d0432164a757daa1112dGilad Arnold 'Failed to stage source test image: %s' % e) 7950ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold else: 7960ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold # TODO(garnold) chromium-os:33766: implement staging of MP-signed 7970ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold # images. 7982f1ae9f1f2464ebbeb226a6669fa90ef211a179cChris Sosa raise NotImplementedError() 7990ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold 8000ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold 80115384a4ded97f171906a72106791d19e96e68fa7Gilad Arnold def _stage_payload(self, autotest_devserver, devserver_label, filename, 802f789cf3a52c720344062f0a6c782bb758f08b189Don Garrett archive_url=None, artifacts=None): 8032f1ae9f1f2464ebbeb226a6669fa90ef211a179cChris Sosa """Stage the given payload onto the devserver. 8040ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold 8052f1ae9f1f2464ebbeb226a6669fa90ef211a179cChris Sosa Works for either a stateful or full/delta test payload. Expects the 8062f1ae9f1f2464ebbeb226a6669fa90ef211a179cChris Sosa gs_path or a combo of devserver_label + filename. 8070ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold 808ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa @param autotest_devserver: instance of client.common_lib.dev_server to 809ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa use to reach the devserver instance for this 810ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa build. 8112f1ae9f1f2464ebbeb226a6669fa90ef211a179cChris Sosa @param devserver_label: The build name e.g. x86-mario-release/<version>. 8122f1ae9f1f2464ebbeb226a6669fa90ef211a179cChris Sosa If set, assumes default gs archive bucket and 8132f1ae9f1f2464ebbeb226a6669fa90ef211a179cChris Sosa requires filename to be specified. 8142f1ae9f1f2464ebbeb226a6669fa90ef211a179cChris Sosa @param filename: In conjunction with devserver_label, if just specifying 8152f1ae9f1f2464ebbeb226a6669fa90ef211a179cChris Sosa the devserver label name, this is which file are you 8162f1ae9f1f2464ebbeb226a6669fa90ef211a179cChris Sosa downloading. 81715384a4ded97f171906a72106791d19e96e68fa7Gilad Arnold @param archive_url: An optional GS archive location, if not using the 81815384a4ded97f171906a72106791d19e96e68fa7Gilad Arnold devserver's default. 819ae6acd9b3e1c6f56310cdb4e05737122b7b2818cChris Sosa 8200ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold @return URL of the staged payload on the server. 8210ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold 8220ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold @raise error.TestError if there's a problem with staging. 8230ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold 8240ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold """ 8252f1ae9f1f2464ebbeb226a6669fa90ef211a179cChris Sosa try: 826ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa autotest_devserver.stage_artifacts( 827f789cf3a52c720344062f0a6c782bb758f08b189Don Garrett image=devserver_label, artifacts=artifacts, 828f789cf3a52c720344062f0a6c782bb758f08b189Don Garrett files=[filename], archive_url=archive_url) 829ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa return autotest_devserver.get_staged_file_url(filename, 830ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa devserver_label) 8312f1ae9f1f2464ebbeb226a6669fa90ef211a179cChris Sosa except dev_server.DevServerException, e: 8320338ff3b754d52b41ca2d0432164a757daa1112dGilad Arnold raise error.TestError('Failed to stage payload: %s' % e) 8330ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold 8340ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold 835f789cf3a52c720344062f0a6c782bb758f08b189Don Garrett def _stage_payload_by_uri(self, autotest_devserver, payload_uri, 836f789cf3a52c720344062f0a6c782bb758f08b189Don Garrett artifacts=None): 83715384a4ded97f171906a72106791d19e96e68fa7Gilad Arnold """Stage a payload based on its GS URI. 83815384a4ded97f171906a72106791d19e96e68fa7Gilad Arnold 83915384a4ded97f171906a72106791d19e96e68fa7Gilad Arnold This infers the build's label, filename and GS archive from the 84015384a4ded97f171906a72106791d19e96e68fa7Gilad Arnold provided GS URI. 84115384a4ded97f171906a72106791d19e96e68fa7Gilad Arnold 84215384a4ded97f171906a72106791d19e96e68fa7Gilad Arnold @param autotest_devserver: instance of client.common_lib.dev_server to 84315384a4ded97f171906a72106791d19e96e68fa7Gilad Arnold use to reach the devserver instance for this 84415384a4ded97f171906a72106791d19e96e68fa7Gilad Arnold build. 84515384a4ded97f171906a72106791d19e96e68fa7Gilad Arnold @param payload_uri: The full GS URI of the payload. 84615384a4ded97f171906a72106791d19e96e68fa7Gilad Arnold 847f789cf3a52c720344062f0a6c782bb758f08b189Don Garrett @param artifacts: A list of artifacts to stage along from the build. 848f789cf3a52c720344062f0a6c782bb758f08b189Don Garrett 84915384a4ded97f171906a72106791d19e96e68fa7Gilad Arnold @return URL of the staged payload on the server. 85015384a4ded97f171906a72106791d19e96e68fa7Gilad Arnold 85115384a4ded97f171906a72106791d19e96e68fa7Gilad Arnold @raise error.TestError if there's a problem with staging. 85215384a4ded97f171906a72106791d19e96e68fa7Gilad Arnold 85315384a4ded97f171906a72106791d19e96e68fa7Gilad Arnold """ 85415384a4ded97f171906a72106791d19e96e68fa7Gilad Arnold archive_url, _, filename = payload_uri.rpartition('/') 85515384a4ded97f171906a72106791d19e96e68fa7Gilad Arnold devserver_label = urlparse.urlsplit(archive_url).path.strip('/') 85615384a4ded97f171906a72106791d19e96e68fa7Gilad Arnold return self._stage_payload(autotest_devserver, devserver_label, 85715384a4ded97f171906a72106791d19e96e68fa7Gilad Arnold filename, archive_url=archive_url) 85815384a4ded97f171906a72106791d19e96e68fa7Gilad Arnold 85915384a4ded97f171906a72106791d19e96e68fa7Gilad Arnold 8607231260da421abdf5ceceff6ab60155936dca21fChris Sosa def _payload_to_update_url(self, payload_url): 8612f1ae9f1f2464ebbeb226a6669fa90ef211a179cChris Sosa """Given a update or stateful payload url, returns the update url.""" 8627231260da421abdf5ceceff6ab60155936dca21fChris Sosa # We want to transform it to the correct omaha url which is 8637231260da421abdf5ceceff6ab60155936dca21fChris Sosa # <hostname>/update/...LABEL. 8642f1ae9f1f2464ebbeb226a6669fa90ef211a179cChris Sosa base_url = payload_url.rpartition('/')[0] 8652f1ae9f1f2464ebbeb226a6669fa90ef211a179cChris Sosa return base_url.replace('/static/', '/update/') 8662f1ae9f1f2464ebbeb226a6669fa90ef211a179cChris Sosa 8672f1ae9f1f2464ebbeb226a6669fa90ef211a179cChris Sosa 86815384a4ded97f171906a72106791d19e96e68fa7Gilad Arnold def _get_stateful_uri(self, build_uri): 86915384a4ded97f171906a72106791d19e96e68fa7Gilad Arnold """Returns a complete GS URI of a stateful update given a build path.""" 87015384a4ded97f171906a72106791d19e96e68fa7Gilad Arnold return '/'.join([build_uri.rstrip('/'), self._STATEFUL_UPDATE_FILENAME]) 87115384a4ded97f171906a72106791d19e96e68fa7Gilad Arnold 87215384a4ded97f171906a72106791d19e96e68fa7Gilad Arnold 87315384a4ded97f171906a72106791d19e96e68fa7Gilad Arnold def _payload_to_stateful_uri(self, payload_uri): 87415384a4ded97f171906a72106791d19e96e68fa7Gilad Arnold """Given a payload GS URI, returns the corresponding stateful URI.""" 87515384a4ded97f171906a72106791d19e96e68fa7Gilad Arnold build_uri = payload_uri.rpartition('/')[0] 87615384a4ded97f171906a72106791d19e96e68fa7Gilad Arnold return self._get_stateful_uri(build_uri) 8772f1ae9f1f2464ebbeb226a6669fa90ef211a179cChris Sosa 8782f1ae9f1f2464ebbeb226a6669fa90ef211a179cChris Sosa 8792f1ae9f1f2464ebbeb226a6669fa90ef211a179cChris Sosa def update_via_test_payloads(self, omaha_host, payload_url, stateful_url, 8802f1ae9f1f2464ebbeb226a6669fa90ef211a179cChris Sosa clobber): 8812f1ae9f1f2464ebbeb226a6669fa90ef211a179cChris Sosa """Given the following update and stateful urls, update the DUT. 8822f1ae9f1f2464ebbeb226a6669fa90ef211a179cChris Sosa 8832f1ae9f1f2464ebbeb226a6669fa90ef211a179cChris Sosa Only updates the rootfs/stateful if the respective url is provided. 8842f1ae9f1f2464ebbeb226a6669fa90ef211a179cChris Sosa 8852f1ae9f1f2464ebbeb226a6669fa90ef211a179cChris Sosa @param omaha_host: If updating rootfs, redirect updates through this 8862f1ae9f1f2464ebbeb226a6669fa90ef211a179cChris Sosa host. Should be None iff payload_url is None. 8872f1ae9f1f2464ebbeb226a6669fa90ef211a179cChris Sosa @param payload_url: If set, the specified url to find the update payload. 8882f1ae9f1f2464ebbeb226a6669fa90ef211a179cChris Sosa @param stateful_url: If set, the specified url to find the stateful 8892f1ae9f1f2464ebbeb226a6669fa90ef211a179cChris Sosa payload. 8902f1ae9f1f2464ebbeb226a6669fa90ef211a179cChris Sosa @param clobber: If True, do a clean install of stateful. 8912f1ae9f1f2464ebbeb226a6669fa90ef211a179cChris Sosa """ 8922f1ae9f1f2464ebbeb226a6669fa90ef211a179cChris Sosa # We create a OmahaDevserver to redirect blah.bin to update/. This allows 8932f1ae9f1f2464ebbeb226a6669fa90ef211a179cChris Sosa # us to use any payload filename to serve an update. 8942f1ae9f1f2464ebbeb226a6669fa90ef211a179cChris Sosa temp_devserver = None 8952f1ae9f1f2464ebbeb226a6669fa90ef211a179cChris Sosa try: 8962f1ae9f1f2464ebbeb226a6669fa90ef211a179cChris Sosa if payload_url: 8972f1ae9f1f2464ebbeb226a6669fa90ef211a179cChris Sosa temp_devserver = OmahaDevserver( 8982f1ae9f1f2464ebbeb226a6669fa90ef211a179cChris Sosa omaha_host, self._devserver_dir, self._host.ip, 8992f1ae9f1f2464ebbeb226a6669fa90ef211a179cChris Sosa payload_url) 9002f1ae9f1f2464ebbeb226a6669fa90ef211a179cChris Sosa temp_devserver.start_devserver() 9012f1ae9f1f2464ebbeb226a6669fa90ef211a179cChris Sosa payload_url = temp_devserver.get_update_url() 9022f1ae9f1f2464ebbeb226a6669fa90ef211a179cChris Sosa 9032f1ae9f1f2464ebbeb226a6669fa90ef211a179cChris Sosa stateful_url = self._payload_to_update_url(stateful_url) 9042f1ae9f1f2464ebbeb226a6669fa90ef211a179cChris Sosa 9052f1ae9f1f2464ebbeb226a6669fa90ef211a179cChris Sosa for (url, is_stateful) in (payload_url, False), (stateful_url, True): 9062f1ae9f1f2464ebbeb226a6669fa90ef211a179cChris Sosa if not url: 9072f1ae9f1f2464ebbeb226a6669fa90ef211a179cChris Sosa continue 9082f1ae9f1f2464ebbeb226a6669fa90ef211a179cChris Sosa 9092f1ae9f1f2464ebbeb226a6669fa90ef211a179cChris Sosa updater = autoupdater.ChromiumOSUpdater(url, host=self._host) 9102f1ae9f1f2464ebbeb226a6669fa90ef211a179cChris Sosa if not is_stateful: 9112f1ae9f1f2464ebbeb226a6669fa90ef211a179cChris Sosa updater.update_rootfs() 9122f1ae9f1f2464ebbeb226a6669fa90ef211a179cChris Sosa else: 9132f1ae9f1f2464ebbeb226a6669fa90ef211a179cChris Sosa updater.update_stateful(clobber=clobber) 9142f1ae9f1f2464ebbeb226a6669fa90ef211a179cChris Sosa finally: 9152f1ae9f1f2464ebbeb226a6669fa90ef211a179cChris Sosa if temp_devserver: 9162f1ae9f1f2464ebbeb226a6669fa90ef211a179cChris Sosa temp_devserver.kill() 9172f1ae9f1f2464ebbeb226a6669fa90ef211a179cChris Sosa 9182f1ae9f1f2464ebbeb226a6669fa90ef211a179cChris Sosa 919ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa def install_source_version(self, devserver_hostname, image_url, 920ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa stateful_url): 9212f1ae9f1f2464ebbeb226a6669fa90ef211a179cChris Sosa """Prepare the specified host with the image given by the urls. 9222f1ae9f1f2464ebbeb226a6669fa90ef211a179cChris Sosa 923ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa @param devserver_hostname: If updating rootfs, redirect updates 924ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa through this host. Should be None iff 925ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa image_url is None. 9262f1ae9f1f2464ebbeb226a6669fa90ef211a179cChris Sosa @param image_url: If set, the specified url to find the source image 9272f1ae9f1f2464ebbeb226a6669fa90ef211a179cChris Sosa or full payload for the source image. 9282f1ae9f1f2464ebbeb226a6669fa90ef211a179cChris Sosa @param stateful_url: If set, the specified url to find the stateful 9292f1ae9f1f2464ebbeb226a6669fa90ef211a179cChris Sosa payload. 9302f1ae9f1f2464ebbeb226a6669fa90ef211a179cChris Sosa """ 931f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa if self._use_servo: 932f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa # Install source image (test vs MP). 933f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa if self._use_test_image: 934f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa self._install_test_image_with_servo(image_url) 935f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa else: 936f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa self._install_mp_image(image_url) 937f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa 938f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa else: 93945f02ae47134953169805d281992c9edf0019250Chris Sosa try: 9402f1ae9f1f2464ebbeb226a6669fa90ef211a179cChris Sosa # Reboot to get us into a clean state. 9412f1ae9f1f2464ebbeb226a6669fa90ef211a179cChris Sosa self._host.reboot() 9422f1ae9f1f2464ebbeb226a6669fa90ef211a179cChris Sosa # Since we are installing the source image of the test, clobber 9432f1ae9f1f2464ebbeb226a6669fa90ef211a179cChris Sosa # stateful. 944ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa self.update_via_test_payloads(devserver_hostname, image_url, 9452f1ae9f1f2464ebbeb226a6669fa90ef211a179cChris Sosa stateful_url, clobber=True) 9462f1ae9f1f2464ebbeb226a6669fa90ef211a179cChris Sosa self._host.reboot() 94745f02ae47134953169805d281992c9edf0019250Chris Sosa except error.AutoservRunError: 94845f02ae47134953169805d281992c9edf0019250Chris Sosa logging.fatal('Error re-imaging the machine with the source ' 94945f02ae47134953169805d281992c9edf0019250Chris Sosa 'image %s', image_url) 9502f1ae9f1f2464ebbeb226a6669fa90ef211a179cChris Sosa raise error.TestError( 95145f02ae47134953169805d281992c9edf0019250Chris Sosa 'Could not update to pre-conditions of test. This is ' 95245f02ae47134953169805d281992c9edf0019250Chris Sosa 'most likely a problem with the autotest lab and not ' 95345f02ae47134953169805d281992c9edf0019250Chris Sosa 'autoupdate.') 954f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa 955f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa 956ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa def stage_artifacts_onto_devserver(self, autotest_devserver, test_conf): 9572f1ae9f1f2464ebbeb226a6669fa90ef211a179cChris Sosa """Stages artifacts that will be used by the test onto the devserver. 9582f1ae9f1f2464ebbeb226a6669fa90ef211a179cChris Sosa 959ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa @param autotest_devserver: instance of client.common_lib.dev_server to 960ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa use to reach the devserver instance for this 961ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa build. 9622f1ae9f1f2464ebbeb226a6669fa90ef211a179cChris Sosa @param test_conf: a dictionary containing test configuration values 9632f1ae9f1f2464ebbeb226a6669fa90ef211a179cChris Sosa 9642f1ae9f1f2464ebbeb226a6669fa90ef211a179cChris Sosa @return a _STAGED_URLS tuple containing the staged urls. 965f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa """ 9660338ff3b754d52b41ca2d0432164a757daa1112dGilad Arnold logging.info('Staging images onto autotest devserver (%s)', 967ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa autotest_devserver.url()) 968f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa 96915384a4ded97f171906a72106791d19e96e68fa7Gilad Arnold source_image_uri = test_conf['source_image_uri'] 97015384a4ded97f171906a72106791d19e96e68fa7Gilad Arnold 97115384a4ded97f171906a72106791d19e96e68fa7Gilad Arnold staged_source_url = None 97215384a4ded97f171906a72106791d19e96e68fa7Gilad Arnold source_stateful_uri = None 97315384a4ded97f171906a72106791d19e96e68fa7Gilad Arnold staged_source_stateful_url = None 974f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa if self._use_servo: 97515384a4ded97f171906a72106791d19e96e68fa7Gilad Arnold staged_source_url = self._stage_image( 97615384a4ded97f171906a72106791d19e96e68fa7Gilad Arnold autotest_devserver, source_image_uri) 97715384a4ded97f171906a72106791d19e96e68fa7Gilad Arnold # Test image already contains a stateful update, leave 97815384a4ded97f171906a72106791d19e96e68fa7Gilad Arnold # staged_source_stateful_url untouhced. 979f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa else: 98015384a4ded97f171906a72106791d19e96e68fa7Gilad Arnold staged_source_url = self._stage_payload_by_uri( 98115384a4ded97f171906a72106791d19e96e68fa7Gilad Arnold autotest_devserver, source_image_uri) 98215384a4ded97f171906a72106791d19e96e68fa7Gilad Arnold 98315384a4ded97f171906a72106791d19e96e68fa7Gilad Arnold # In order to properly install the source image using a full 98415384a4ded97f171906a72106791d19e96e68fa7Gilad Arnold # payload we'll also need the stateful update that comes with it. 98515384a4ded97f171906a72106791d19e96e68fa7Gilad Arnold # In general, tests may have their source artifacts in a different 98615384a4ded97f171906a72106791d19e96e68fa7Gilad Arnold # location than their payloads. This is determined by whether or 98715384a4ded97f171906a72106791d19e96e68fa7Gilad Arnold # not the source_archive_uri attribute is set; if it isn't set, 98815384a4ded97f171906a72106791d19e96e68fa7Gilad Arnold # then we derive it from the dirname of the source payload. 9892f1ae9f1f2464ebbeb226a6669fa90ef211a179cChris Sosa source_archive_uri = test_conf.get('source_archive_uri') 99015384a4ded97f171906a72106791d19e96e68fa7Gilad Arnold if source_archive_uri: 99115384a4ded97f171906a72106791d19e96e68fa7Gilad Arnold source_stateful_uri = self._get_stateful_uri(source_archive_uri) 9922f1ae9f1f2464ebbeb226a6669fa90ef211a179cChris Sosa else: 99315384a4ded97f171906a72106791d19e96e68fa7Gilad Arnold source_stateful_uri = self._payload_to_stateful_uri( 99415384a4ded97f171906a72106791d19e96e68fa7Gilad Arnold source_image_uri) 99515384a4ded97f171906a72106791d19e96e68fa7Gilad Arnold 99615384a4ded97f171906a72106791d19e96e68fa7Gilad Arnold staged_source_stateful_url = self._stage_payload_by_uri( 99715384a4ded97f171906a72106791d19e96e68fa7Gilad Arnold autotest_devserver, source_stateful_uri) 99815384a4ded97f171906a72106791d19e96e68fa7Gilad Arnold 99915384a4ded97f171906a72106791d19e96e68fa7Gilad Arnold target_payload_uri = test_conf['target_payload_uri'] 100015384a4ded97f171906a72106791d19e96e68fa7Gilad Arnold staged_target_url = self._stage_payload_by_uri( 100115384a4ded97f171906a72106791d19e96e68fa7Gilad Arnold autotest_devserver, target_payload_uri) 100215384a4ded97f171906a72106791d19e96e68fa7Gilad Arnold target_stateful_uri = None 100315384a4ded97f171906a72106791d19e96e68fa7Gilad Arnold target_archive_uri = test_conf.get('target_archive_uri') 100415384a4ded97f171906a72106791d19e96e68fa7Gilad Arnold if not target_archive_uri and self._job_repo_url: 10052f1ae9f1f2464ebbeb226a6669fa90ef211a179cChris Sosa _, devserver_label = tools.get_devserver_build_from_package_url( 10062f1ae9f1f2464ebbeb226a6669fa90ef211a179cChris Sosa self._job_repo_url) 100715384a4ded97f171906a72106791d19e96e68fa7Gilad Arnold staged_target_stateful_url = self._stage_payload( 100815384a4ded97f171906a72106791d19e96e68fa7Gilad Arnold autotest_devserver, devserver_label, 100915384a4ded97f171906a72106791d19e96e68fa7Gilad Arnold self._STATEFUL_UPDATE_FILENAME) 10102f1ae9f1f2464ebbeb226a6669fa90ef211a179cChris Sosa else: 101115384a4ded97f171906a72106791d19e96e68fa7Gilad Arnold if target_archive_uri: 101215384a4ded97f171906a72106791d19e96e68fa7Gilad Arnold target_stateful_uri = self._get_stateful_uri(target_archive_uri) 101315384a4ded97f171906a72106791d19e96e68fa7Gilad Arnold else: 101415384a4ded97f171906a72106791d19e96e68fa7Gilad Arnold target_stateful_uri = self._payload_to_stateful_uri( 101515384a4ded97f171906a72106791d19e96e68fa7Gilad Arnold target_payload_uri) 1016f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa 101715384a4ded97f171906a72106791d19e96e68fa7Gilad Arnold staged_target_stateful_url = self._stage_payload_by_uri( 101815384a4ded97f171906a72106791d19e96e68fa7Gilad Arnold autotest_devserver, target_stateful_uri) 10192f1ae9f1f2464ebbeb226a6669fa90ef211a179cChris Sosa 1020f789cf3a52c720344062f0a6c782bb758f08b189Don Garrett # Stage artifacts for client login test. 1021f789cf3a52c720344062f0a6c782bb758f08b189Don Garrett self._stage_payload_by_uri(autotest_devserver, 1022f789cf3a52c720344062f0a6c782bb758f08b189Don Garrett target_payload_uri, 1023f789cf3a52c720344062f0a6c782bb758f08b189Don Garrett ['autotest']) 1024f789cf3a52c720344062f0a6c782bb758f08b189Don Garrett 102515384a4ded97f171906a72106791d19e96e68fa7Gilad Arnold # Log all the urls. 10260338ff3b754d52b41ca2d0432164a757daa1112dGilad Arnold logging.info('Source %s from %s staged at %s', 102715384a4ded97f171906a72106791d19e96e68fa7Gilad Arnold 'image' if self._use_servo else 'full payload', 102815384a4ded97f171906a72106791d19e96e68fa7Gilad Arnold source_image_uri, staged_source_url) 102915384a4ded97f171906a72106791d19e96e68fa7Gilad Arnold if staged_source_stateful_url: 10300338ff3b754d52b41ca2d0432164a757daa1112dGilad Arnold logging.info('Source stateful update from %s staged at %s', 103115384a4ded97f171906a72106791d19e96e68fa7Gilad Arnold source_stateful_uri, staged_source_stateful_url) 103215384a4ded97f171906a72106791d19e96e68fa7Gilad Arnold logging.info('%s test payload from %s staged at %s', 103315384a4ded97f171906a72106791d19e96e68fa7Gilad Arnold test_conf['update_type'], target_payload_uri, 103415384a4ded97f171906a72106791d19e96e68fa7Gilad Arnold staged_target_url) 10350338ff3b754d52b41ca2d0432164a757daa1112dGilad Arnold logging.info('Target stateful update from %s staged at %s', 103615384a4ded97f171906a72106791d19e96e68fa7Gilad Arnold target_stateful_uri or 'standard location', 103715384a4ded97f171906a72106791d19e96e68fa7Gilad Arnold staged_target_stateful_url) 103815384a4ded97f171906a72106791d19e96e68fa7Gilad Arnold 103915384a4ded97f171906a72106791d19e96e68fa7Gilad Arnold return self._STAGED_URLS(staged_source_url, staged_source_stateful_url, 104015384a4ded97f171906a72106791d19e96e68fa7Gilad Arnold staged_target_url, staged_target_stateful_url) 1041f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa 1042f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa 1043f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa def initialize(self): 1044f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa """Sets up variables that will be used by test.""" 1045f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa self._host = None 1046f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa self._use_servo = False 1047f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa self._dev_mode = False 1048f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa self._omaha_devserver = None 1049f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa 1050f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa self._use_test_image = True 10512f1ae9f1f2464ebbeb226a6669fa90ef211a179cChris Sosa self._job_repo_url = None 1052f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa self._devserver_dir = global_config.global_config.get_config_value( 1053f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa 'CROS', 'devserver_dir', default=None) 1054f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa if self._devserver_dir is None: 1055f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa raise error.TestError( 10560338ff3b754d52b41ca2d0432164a757daa1112dGilad Arnold 'Path to devserver source tree not provided; please define ' 1057f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa 'devserver_dir under [CROS] in your shadow_config.ini') 1058f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa 1059f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa 1060f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa def cleanup(self): 1061f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa """Kill the omaha devserver if it's still around.""" 1062f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa if self._omaha_devserver: 1063f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa self._omaha_devserver.kill() 1064f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa 1065f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa self._omaha_devserver = None 1066f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa 1067f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa 106845f02ae47134953169805d281992c9edf0019250Chris Sosa def _verify_preconditions(self): 1069f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa """Validate input args make sense.""" 1070f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa if self._use_servo and not self._host.servo: 1071f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa raise error.AutotestError('Servo use specified but no servo ' 1072f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa 'attached to host object.') 1073f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa 1074f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa if not self._use_test_image and not self._use_servo: 10750338ff3b754d52b41ca2d0432164a757daa1112dGilad Arnold raise error.TestError('Cannot install mp image without servo.') 1076f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa 1077f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa 1078ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa def _dump_update_engine_log(self): 1079ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa """Dumps relevant AU error log.""" 108045f02ae47134953169805d281992c9edf0019250Chris Sosa if not self._use_servo: 108145f02ae47134953169805d281992c9edf0019250Chris Sosa logging.error('Test failed -- dumping snippet of update_engine log') 1082ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa try: 1083ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa error_log = self._host.run_output( 1084ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa 'tail -n 40 /var/log/update_engine.log') 1085ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa logging.error(error_log) 1086ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa except Exception: 1087ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa # Mute any exceptions we get printing debug logs. 1088ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa pass 108945f02ae47134953169805d281992c9edf0019250Chris Sosa 10900ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold 1091ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa def run_update_test(self, staged_urls, test_conf): 1092ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa """Runs the actual update test once preconditions are met. 10930ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold 1094ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa @param staged_urls: A _STAGED_URLS tuple containing the staged urls. 1095ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa @param test_conf: A dictionary containing test configuration values 10960ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold 1097ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa @raises ExpectedUpdateEventChainFailed if we failed to verify an update 1098ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa event. 10990ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold """ 1100f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa # On test images, record the active root partition. 1101f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa source_rootfs_partition = None 1102f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa if self._use_test_image: 1103f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa source_rootfs_partition = self._get_rootdev() 11040338ff3b754d52b41ca2d0432164a757daa1112dGilad Arnold logging.info('Source image rootfs partition: %s', 1105f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa source_rootfs_partition) 11060ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold 11072f1ae9f1f2464ebbeb226a6669fa90ef211a179cChris Sosa # Trigger an update. 1108f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa if self._use_test_image: 11092f1ae9f1f2464ebbeb226a6669fa90ef211a179cChris Sosa self._trigger_test_update(self._omaha_devserver) 1110f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa else: 1111f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa # TODO(garnold) chromium-os:33766: use GPIOs to trigger an 1112f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa # update. 1113f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa pass 11140ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold 1115f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa # Track update progress. 11162f1ae9f1f2464ebbeb226a6669fa90ef211a179cChris Sosa omaha_netloc = self._omaha_devserver.get_netloc() 1117f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa omaha_hostlog_url = urlparse.urlunsplit( 1118f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa ['http', omaha_netloc, '/api/hostlog', 1119f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa 'ip=' + self._host.ip, '']) 11200338ff3b754d52b41ca2d0432164a757daa1112dGilad Arnold logging.info('Polling update progress from omaha/devserver: %s', 1121f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa omaha_hostlog_url) 1122f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa log_verifier = UpdateEventLogVerifier( 1123f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa omaha_hostlog_url, 1124f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa self._DEVSERVER_HOSTLOG_REQUEST_TIMEOUT_SECONDS) 1125f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa 1126f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa # Verify chain of events in a successful update process. 1127f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa chain = ExpectedUpdateEventChain( 1128f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa (self._WAIT_FOR_INITIAL_UPDATE_CHECK_SECONDS, 1129f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa ExpectedUpdateEvent( 1130ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa version=test_conf['source_release'], 113115384a4ded97f171906a72106791d19e96e68fa7Gilad Arnold error_message=('Failed to receive initial update check. ' 1132ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa 'Check Omaha devserver log in this ' 1133ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa 'output.'))), 1134f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa (self._WAIT_FOR_DOWNLOAD_STARTED_SECONDS, 1135f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa ExpectedUpdateEvent( 11360338ff3b754d52b41ca2d0432164a757daa1112dGilad Arnold event_type=EVENT_TYPE_DOWNLOAD_STARTED, 11370338ff3b754d52b41ca2d0432164a757daa1112dGilad Arnold event_result=EVENT_RESULT_SUCCESS, 1138ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa version=test_conf['source_release'], 11390338ff3b754d52b41ca2d0432164a757daa1112dGilad Arnold error_message=( 11400338ff3b754d52b41ca2d0432164a757daa1112dGilad Arnold 'Failed to start the download of the update ' 11410338ff3b754d52b41ca2d0432164a757daa1112dGilad Arnold 'payload from the staging server. Check both the ' 11420338ff3b754d52b41ca2d0432164a757daa1112dGilad Arnold 'omaha log and update_engine.log in sysinfo (or ' 11430338ff3b754d52b41ca2d0432164a757daa1112dGilad Arnold 'on the DUT).'))), 1144f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa (self._WAIT_FOR_DOWNLOAD_COMPLETED_SECONDS, 1145f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa ExpectedUpdateEvent( 11460338ff3b754d52b41ca2d0432164a757daa1112dGilad Arnold event_type=EVENT_TYPE_DOWNLOAD_FINISHED, 11470338ff3b754d52b41ca2d0432164a757daa1112dGilad Arnold event_result=EVENT_RESULT_SUCCESS, 1148ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa version=test_conf['source_release'], 11490338ff3b754d52b41ca2d0432164a757daa1112dGilad Arnold error_message=( 11500338ff3b754d52b41ca2d0432164a757daa1112dGilad Arnold 'Failed to finish download from devserver. Check ' 11510338ff3b754d52b41ca2d0432164a757daa1112dGilad Arnold 'the update_engine.log in sysinfo (or on the ' 11520338ff3b754d52b41ca2d0432164a757daa1112dGilad Arnold 'DUT).'))), 1153f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa (self._WAIT_FOR_UPDATE_COMPLETED_SECONDS, 1154f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa ExpectedUpdateEvent( 11550338ff3b754d52b41ca2d0432164a757daa1112dGilad Arnold event_type=EVENT_TYPE_UPDATE_COMPLETE, 11560338ff3b754d52b41ca2d0432164a757daa1112dGilad Arnold event_result=EVENT_RESULT_SUCCESS, 1157ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa version=test_conf['source_release'], 11580338ff3b754d52b41ca2d0432164a757daa1112dGilad Arnold error_message=( 11590338ff3b754d52b41ca2d0432164a757daa1112dGilad Arnold 'Failed to complete update before reboot. Check ' 11600338ff3b754d52b41ca2d0432164a757daa1112dGilad Arnold 'the update_engine.log in sysinfo (or on the ' 11610338ff3b754d52b41ca2d0432164a757daa1112dGilad Arnold 'DUT).')))) 1162f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa 1163ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa log_verifier.verify_expected_event_chain(chain) 1164f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa 1165f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa # Wait after an update completion (safety margin). 1166f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa _wait(self._WAIT_AFTER_UPDATE_SECONDS, 'after update completion') 1167f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa 1168f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa # Reboot the DUT after the update. 1169ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa if self._use_servo: 1170f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa self._servo_dut_reboot() 1171f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa else: 11722f1ae9f1f2464ebbeb226a6669fa90ef211a179cChris Sosa # Only update the stateful partition since the test has updated the 11732f1ae9f1f2464ebbeb226a6669fa90ef211a179cChris Sosa # rootfs. 11742f1ae9f1f2464ebbeb226a6669fa90ef211a179cChris Sosa self.update_via_test_payloads( 11752f1ae9f1f2464ebbeb226a6669fa90ef211a179cChris Sosa None, None, staged_urls.target_stateful_url, clobber=False) 1176f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa self._host.reboot() 11770ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold 1178f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa # Trigger a second update check (again, test vs MP). 1179f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa if self._use_test_image: 11802f1ae9f1f2464ebbeb226a6669fa90ef211a179cChris Sosa self._trigger_test_update(self._omaha_devserver) 1181f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa else: 1182f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa # TODO(garnold) chromium-os:33766: use GPIOs to trigger an 1183f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa # update. 1184f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa pass 11850ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold 1186f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa # Observe post-reboot update check, which should indicate that the 1187f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa # image version has been updated. 1188f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa chain = ExpectedUpdateEventChain( 1189f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa (self._WAIT_FOR_UPDATE_CHECK_AFTER_REBOOT_SECONDS, 1190f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa ExpectedUpdateEvent( 11910338ff3b754d52b41ca2d0432164a757daa1112dGilad Arnold event_type=EVENT_TYPE_UPDATE_COMPLETE, 11920338ff3b754d52b41ca2d0432164a757daa1112dGilad Arnold event_result=EVENT_RESULT_SUCCESS_REBOOT, 1193f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa version=test_conf['target_release'], 1194ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa previous_version=test_conf['source_release'], 11950338ff3b754d52b41ca2d0432164a757daa1112dGilad Arnold error_message=( 11960338ff3b754d52b41ca2d0432164a757daa1112dGilad Arnold 'Failed to reboot into the target version after ' 11970338ff3b754d52b41ca2d0432164a757daa1112dGilad Arnold 'an update. Check the sysinfo logs. This probably ' 11980338ff3b754d52b41ca2d0432164a757daa1112dGilad Arnold 'means that the updated image failed to verify ' 11990338ff3b754d52b41ca2d0432164a757daa1112dGilad Arnold 'after reboot and might mean that the update ' 12000338ff3b754d52b41ca2d0432164a757daa1112dGilad Arnold 'payload is bad')))) 1201ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa 1202ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa log_verifier.verify_expected_event_chain(chain) 1203f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa 1204f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa # On test images, make sure we're using a different partition after 1205f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa # the update. 1206f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa if self._use_test_image: 1207f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa target_rootfs_partition = self._get_rootdev() 1208f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa if target_rootfs_partition == source_rootfs_partition: 1209f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa raise error.TestFail( 12100338ff3b754d52b41ca2d0432164a757daa1112dGilad Arnold 'Rootfs partition did not change (%s)' % 1211f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa target_rootfs_partition) 12120ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold 12130338ff3b754d52b41ca2d0432164a757daa1112dGilad Arnold logging.info( 12140338ff3b754d52b41ca2d0432164a757daa1112dGilad Arnold 'Target image rootfs partition changed as expected: %s', 12150338ff3b754d52b41ca2d0432164a757daa1112dGilad Arnold target_rootfs_partition) 12160338ff3b754d52b41ca2d0432164a757daa1112dGilad Arnold 12170338ff3b754d52b41ca2d0432164a757daa1112dGilad Arnold logging.info('Update successful, test completed') 12180ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold 1219ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa 1220ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa def run_once(self, host, test_conf, use_servo): 1221ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa """Performs a complete auto update test. 1222ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa 1223ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa @param host: a host object representing the DUT 1224ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa @param test_conf: a dictionary containing test configuration values 1225ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa @param use_servo: True whether we should use servo. 1226ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa 1227ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa @raise error.TestError if anything went wrong with setting up the test; 1228ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa error.TestFail if any part of the test has failed. 1229ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa 1230ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa """ 1231f789cf3a52c720344062f0a6c782bb758f08b189Don Garrett 1232f789cf3a52c720344062f0a6c782bb758f08b189Don Garrett if not test_conf['target_release']: 1233f789cf3a52c720344062f0a6c782bb758f08b189Don Garrett raise RequiredArgumentMissing( 1234f789cf3a52c720344062f0a6c782bb758f08b189Don Garrett 'target_release is a required argument.') 1235f789cf3a52c720344062f0a6c782bb758f08b189Don Garrett 1236ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa # Attempt to get the job_repo_url to find the stateful payload for the 1237ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa # target image. 1238ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa try: 1239ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa self._job_repo_url = host.lookup_job_repo_url() 1240ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa except KeyError: 1241ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa logging.warning('Job Repo URL not found. Assuming stateful ' 1242ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa 'payload can be found along with the target update') 1243ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa 1244ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa self._host = host 1245ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa self._use_test_image = test_conf.get('image_type') != 'mp' 1246ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa self._use_servo = use_servo 1247ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa if self._use_servo: 1248ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa self._dev_mode = self._host.servo.get('dev_mode') == 'on' 1249ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa 1250ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa # Verify that our arguments are sane. 1251ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa self._verify_preconditions() 1252ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa 1253ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa # Find a devserver to use. We use the payload URI as argument for the 1254ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa # lab's devserver load-balancing mechanism. 1255ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa autotest_devserver = dev_server.ImageServer.resolve( 1256ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa test_conf['target_payload_uri']) 1257ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa devserver_hostname = urlparse.urlparse( 1258ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa autotest_devserver.url()).hostname 1259ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa 1260ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa # Stage source images and update payloads onto a devserver. 1261ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa staged_urls = self.stage_artifacts_onto_devserver( 1262ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa autotest_devserver, test_conf) 1263ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa 1264ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa # Install the source version onto the DUT. 1265ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa self.install_source_version(devserver_hostname, 1266ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa staged_urls.source_url, 1267ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa staged_urls.source_stateful_url) 1268ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa 1269ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa self._omaha_devserver = OmahaDevserver( 1270ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa devserver_hostname, self._devserver_dir, self._host.ip, 1271ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa staged_urls.target_url) 1272ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa self._omaha_devserver.start_devserver() 1273ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa try: 1274ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa self.run_update_test(staged_urls, test_conf) 1275ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa except ExpectedUpdateEventChainFailed: 1276ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa self._dump_update_engine_log() 1277ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa raise 1278f789cf3a52c720344062f0a6c782bb758f08b189Don Garrett 1279f789cf3a52c720344062f0a6c782bb758f08b189Don Garrett # Only do login tests with recent builds, since they depend on 1280f789cf3a52c720344062f0a6c782bb758f08b189Don Garrett # some binary compatibility with the build itself. 1281f789cf3a52c720344062f0a6c782bb758f08b189Don Garrett # '5116.0.0' -> ('5116', '0', '0') -> 5116 1282f789cf3a52c720344062f0a6c782bb758f08b189Don Garrett if int(test_conf['target_release'].split('.')[0]) > 5110: 1283f789cf3a52c720344062f0a6c782bb758f08b189Don Garrett # Login, to prove we can after the update. 1284f789cf3a52c720344062f0a6c782bb758f08b189Don Garrett # 1285f789cf3a52c720344062f0a6c782bb758f08b189Don Garrett # TODO(dgarrett): Telemetry login is not yet robust. When it is, 1286f789cf3a52c720344062f0a6c782bb758f08b189Don Garrett # switch test to 'login_LoginSuccessTelemetry' 1287f789cf3a52c720344062f0a6c782bb758f08b189Don Garrett logging.info('Attempting to login to verify image.') 1288f789cf3a52c720344062f0a6c782bb758f08b189Don Garrett 1289f789cf3a52c720344062f0a6c782bb758f08b189Don Garrett client_at = autotest.Autotest(self._host) 1290f789cf3a52c720344062f0a6c782bb758f08b189Don Garrett client_at.run_test('login_LoginSuccess') 1291f789cf3a52c720344062f0a6c782bb758f08b189Don Garrett else: 1292f789cf3a52c720344062f0a6c782bb758f08b189Don Garrett logging.info('Not attempting login test.') 1293