autoupdate_EndToEndTest.py revision 6c55bdb98e967675456a71a0971b81058536cac8
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 50ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnoldimport json 60ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnoldimport logging 70ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnoldimport time 86c55bdb98e967675456a71a0971b81058536cac8Chris Sosaimport os 90ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnoldimport urllib2 100ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnoldimport urlparse 1103901089c0db27508a6b2ff61ae8b75eba77cf37Gilad Arnold 126c55bdb98e967675456a71a0971b81058536cac8Chris Sosafrom autotest_lib.client.bin import utils as client_utils 136c55bdb98e967675456a71a0971b81058536cac8Chris Sosafrom autotest_lib.client.common_lib import error, global_config 140ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnoldfrom autotest_lib.client.common_lib.cros import autoupdater, dev_server 156c55bdb98e967675456a71a0971b81058536cac8Chris Sosafrom autotest_lib.server import hosts, test 160ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold 170ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold 180ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnolddef _wait(secs, desc=None): 190ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold """Emits a log message and sleeps for a given number of seconds.""" 200ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold msg = 'waiting %s seconds' % secs 210ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold if desc: 220ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold msg += ' (%s)' % desc 230ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold logging.info(msg) 240ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold time.sleep(secs) 250ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold 260ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold 270ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnoldclass ExpectedUpdateEvent(object): 280ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold """Defines an expected event in a host update process.""" 290ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold def __init__(self, event_type=None, event_result=None, version=None, 300ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold previous_version=None): 310ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold self._expected_attrs = { 320ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold 'event_type': event_type, 330ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold 'event_result': event_result, 340ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold 'version': version, 350ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold 'previous_version': previous_version, 360ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold } 370ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold 380ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold 390ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold def __str__(self): 400ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold return ' '.join(['%s=%s' % (attr_name, attr_val or 'any') 410ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold for attr_name, attr_val 420ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold in self._expected_attrs.iteritems()]) 430ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold 440ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold 450ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold def verify(self, actual_event): 460ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold """Verify the attributes of an actual event. 470ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold 480ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold @params actual_event: a dictionary containing event attributes 490ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold 500ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold @return True if all attributes as expected, False otherwise. 510ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold 520ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold """ 530ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold return all([self._verify_attr(attr_name, expected_attr_val, 540ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold actual_event.get(attr_name)) 550ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold for attr_name, expected_attr_val 560ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold in self._expected_attrs.iteritems() if expected_attr_val]) 570ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold 580ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold 590ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold def _verify_attr(self, attr_name, expected_attr_val, actual_attr_val): 600ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold """Verifies that an actual log event attributes matches expected on. 610ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold 620ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold @param attr_name: name of the attribute to verify 630ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold @param expected_attr_val: expected attribute value 640ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold @param actual_attr_val: actual attribute value 650ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold 660ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold @return True if actual value is present and matches, False otherwise. 670ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold 680ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold """ 690ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold if not (actual_attr_val and 700ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold str(actual_attr_val) == str(expected_attr_val)): 710ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold logging.error( 720ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold 'actual %s (%s) not as expected (%s)', 730ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold attr_name, actual_attr_val, expected_attr_val) 740ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold return False 750ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold 760ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold return True 770ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold 780ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold 790ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnoldclass ExpectedUpdateEventChain(object): 800ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold """Defines a chain of expected update events.""" 810ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold def __init__(self, *expected_event_chain_args): 820ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold """Initialize the chain object. 830ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold 840ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold @param expected_event_chain_args: list of tuples arguments, each 850ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold containing a timeout (in seconds) and an ExpectedUpdateEvent 860ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold object. 870ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold 880ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold """ 890ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold self._expected_event_chain = expected_event_chain_args 900ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold 910ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold 920ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold def _format_event_with_timeout(self, timeout, expected_event): 930ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold return ('%s %s' % 940ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold (expected_event, 950ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold ('within %s seconds' % timeout) if timeout 960ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold else 'indefinitely')) 970ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold 980ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold 990ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold def __str__(self): 1000ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold return ('[%s]' % 1010ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold ', '.join( 1020ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold [self._format_event_with_timeout(timeout, expected_event) 1030ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold for timeout, expected_event 1040ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold in self._expected_event_chain])) 1050ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold 1060ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold 1070ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold def __repr__(self): 1080ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold return str(self._expected_event_chain) 1090ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold 1100ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold 1110ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold def verify(self, get_next_event): 1120ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold """Verifies that an actual stream of events complies. 1130ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold 1140ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold @param get_next_event: a function returning the next event 1150ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold 1160ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold @return True if chain was satisfied, False otherwise. 1170ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold 1180ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold """ 1190ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold for timeout, expected_event in self._expected_event_chain: 1200ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold logging.info( 1210ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold 'expecting %s', 1220ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold self._format_event_with_timeout(timeout, expected_event)) 1230ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold if not self._verify_event_with_timeout( 1240ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold timeout, expected_event, get_next_event): 1250ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold return False 1260ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold return True 1270ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold 1280ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold 1290ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold def _verify_event_with_timeout(self, timeout, expected_event, 1300ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold get_next_event): 1310ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold """Verify an expected event occurs within a given timeout. 1320ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold 1330ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold @param timeout: specified in seconds 1340ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold @param expected_event: an expected event specification 1350ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold @param get_next_event: function returning the next event in a stream 1360ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold 1370ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold @return True if event complies, False otherwise. 1380ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold 1390ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold """ 1400ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold base_timestamp = curr_timestamp = time.time() 1410ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold expired_timestamp = base_timestamp + timeout 1420ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold while curr_timestamp <= expired_timestamp: 1430ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold new_event = get_next_event() 1440ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold if new_event: 1450ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold logging.info('event received after %s seconds', 1460ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold curr_timestamp - base_timestamp) 1470ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold return expected_event.verify(new_event) 1480ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold 1490ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold # No new events, sleep for one second only (so we don't miss 1500ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold # events at the end of the allotted timeout). 1510ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold time.sleep(1) 1520ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold curr_timestamp = time.time() 1530ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold 1540ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold logging.error('timeout expired') 1550ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold return False 1560ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold 1570ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold 1580ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnoldclass UpdateEventLogVerifier(object): 1590ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold """Verifies update event chains on a devserver update log.""" 16003901089c0db27508a6b2ff61ae8b75eba77cf37Gilad Arnold def __init__(self, event_log_url, url_request_timeout=None): 1610ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold self._event_log_url = event_log_url 16203901089c0db27508a6b2ff61ae8b75eba77cf37Gilad Arnold self._url_request_timeout = url_request_timeout 1630ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold self._event_log = [] 1640ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold self._num_consumed_events = 0 1650ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold 1660ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold 1670ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold def verify_expected_event_chain(self, expected_event_chain): 1680ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold """Verify a given event chain.""" 1690ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold return expected_event_chain.verify(self._get_next_log_event) 1700ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold 1710ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold 1720ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold def _get_next_log_event(self): 1730ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold """Returns the next event in an event log. 1740ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold 1750ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold Uses the URL handed to it during initialization to obtain the host log 1760ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold from a devserver. If new events are encountered, the first of them is 1770ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold consumed and returned. 1780ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold 1790ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold @return The next new event in the host log, as reported by devserver; 18003901089c0db27508a6b2ff61ae8b75eba77cf37Gilad Arnold None if no such event was found or an error occurred. 1810ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold 1820ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold """ 1830ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold # (Re)read event log from devserver, if necessary. 1840ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold if len(self._event_log) <= self._num_consumed_events: 18503901089c0db27508a6b2ff61ae8b75eba77cf37Gilad Arnold try: 18603901089c0db27508a6b2ff61ae8b75eba77cf37Gilad Arnold if self._url_request_timeout: 18703901089c0db27508a6b2ff61ae8b75eba77cf37Gilad Arnold conn = urllib2.urlopen(self._event_log_url, 18803901089c0db27508a6b2ff61ae8b75eba77cf37Gilad Arnold timeout=self._url_request_timeout) 18903901089c0db27508a6b2ff61ae8b75eba77cf37Gilad Arnold else: 19003901089c0db27508a6b2ff61ae8b75eba77cf37Gilad Arnold conn = urllib2.urlopen(self._event_log_url) 19103901089c0db27508a6b2ff61ae8b75eba77cf37Gilad Arnold except urllib2.URLError, e: 19203901089c0db27508a6b2ff61ae8b75eba77cf37Gilad Arnold logging.warning('urlopen failed: %s', e) 19303901089c0db27508a6b2ff61ae8b75eba77cf37Gilad Arnold return None 19403901089c0db27508a6b2ff61ae8b75eba77cf37Gilad Arnold 1950ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold event_log_resp = conn.read() 1960ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold conn.close() 1970ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold self._event_log = json.loads(event_log_resp) 1980ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold 19903901089c0db27508a6b2ff61ae8b75eba77cf37Gilad Arnold # Return next new event, if one is found. 2000ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold if len(self._event_log) > self._num_consumed_events: 2010ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold new_event = self._event_log[self._num_consumed_events] 2020ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold self._num_consumed_events += 1 2030ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold logging.info('consumed new event: %s', new_event) 2040ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold return new_event 2050ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold 2060ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold 2070ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnoldclass OmahaDevserver(object): 2080ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold """Spawns a test-private devserver instance.""" 2090ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold _WAIT_FOR_DEVSERVER_STARTED_SECONDS = 15 21003901089c0db27508a6b2ff61ae8b75eba77cf37Gilad Arnold _WAIT_SLEEP_INTERVAL = 1 2110ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold 2120ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold 21303901089c0db27508a6b2ff61ae8b75eba77cf37Gilad Arnold def __init__(self, omaha_host, devserver_dir, dut_ip_addr, 21403901089c0db27508a6b2ff61ae8b75eba77cf37Gilad Arnold update_payload_lorry_url): 2150ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold """Starts a private devserver instance, operating at Omaha capacity. 2160ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold 2170ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold @param omaha_host: host address where the devserver is spawned. 21803901089c0db27508a6b2ff61ae8b75eba77cf37Gilad Arnold @param devserver_dir: path to the devserver source directory 2190ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold @param dut_ip_addr: the IP address of the client DUT, used for deriving 2200ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold a unique port number. 2210ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold @param update_payload_lorry_url: URL to provision for update requests. 2220ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold 2230ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold """ 2240ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold if not update_payload_lorry_url: 2250ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold raise error.TestError('missing update payload url') 2260ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold 2276c55bdb98e967675456a71a0971b81058536cac8Chris Sosa self._omaha_host = omaha_host 2280ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold self._omaha_port = self._get_unique_port(dut_ip_addr) 2296c55bdb98e967675456a71a0971b81058536cac8Chris Sosa self._devserver_dir = devserver_dir 2306c55bdb98e967675456a71a0971b81058536cac8Chris Sosa self._update_payload_lorry_url = update_payload_lorry_url 2316c55bdb98e967675456a71a0971b81058536cac8Chris Sosa 2326c55bdb98e967675456a71a0971b81058536cac8Chris Sosa self._devserver_ssh = hosts.SSHHost(self._omaha_host, 2336c55bdb98e967675456a71a0971b81058536cac8Chris Sosa user=os.environ['USER']) 2346c55bdb98e967675456a71a0971b81058536cac8Chris Sosa self._devserver_output = '/tmp/devserver.%s' % self._omaha_port 2356c55bdb98e967675456a71a0971b81058536cac8Chris Sosa self._devserver_pid = None 2360ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold 2376c55bdb98e967675456a71a0971b81058536cac8Chris Sosa 2386c55bdb98e967675456a71a0971b81058536cac8Chris Sosa def start_devserver(self): 2396c55bdb98e967675456a71a0971b81058536cac8Chris Sosa """Starts the devserver and stores the remote pid in self._devserver_pid 2406c55bdb98e967675456a71a0971b81058536cac8Chris Sosa """ 2416c55bdb98e967675456a71a0971b81058536cac8Chris Sosa update_payload_url_base, update_payload_path, _ = self._split_url( 2426c55bdb98e967675456a71a0971b81058536cac8Chris Sosa self._update_payload_lorry_url) 2436c55bdb98e967675456a71a0971b81058536cac8Chris Sosa # Invoke the Omaha/devserver on the remote server. 2440ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold cmdlist = [ 2456c55bdb98e967675456a71a0971b81058536cac8Chris Sosa '%s/devserver.py' % self._devserver_dir, 2460ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold '--archive_dir=static/', 2470ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold '--payload=%s' % update_payload_path, 2480ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold '--port=%d' % self._omaha_port, 2490ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold '--remote_payload', 2500ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold '--urlbase=%s' % update_payload_url_base, 2510ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold '--max_updates=1', 2520ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold '--host_log', 2530ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold ] 2546c55bdb98e967675456a71a0971b81058536cac8Chris Sosa # In the remote case that a previous devserver is still running, 2556c55bdb98e967675456a71a0971b81058536cac8Chris Sosa # kill it. 2566c55bdb98e967675456a71a0971b81058536cac8Chris Sosa devserver_pid = self._remote_devserver_pid() 2576c55bdb98e967675456a71a0971b81058536cac8Chris Sosa if devserver_pid: 2586c55bdb98e967675456a71a0971b81058536cac8Chris Sosa logging.warning('Previous devserver still running. Killing.') 2596c55bdb98e967675456a71a0971b81058536cac8Chris Sosa self._kill_devserver_pid(devserver_pid) 2606c55bdb98e967675456a71a0971b81058536cac8Chris Sosa self._devserver_ssh.run('rm -f %s' % self._devserver_output, 2616c55bdb98e967675456a71a0971b81058536cac8Chris Sosa ignore_status=True) 2626c55bdb98e967675456a71a0971b81058536cac8Chris Sosa 2636c55bdb98e967675456a71a0971b81058536cac8Chris Sosa remote_cmd = '( %s ) </dev/null >%s 2>&1 & echo $!' % ( 2646c55bdb98e967675456a71a0971b81058536cac8Chris Sosa ' '.join(cmdlist), self._devserver_output) 2656c55bdb98e967675456a71a0971b81058536cac8Chris Sosa logging.info('Starting devserver with %r', remote_cmd) 2666c55bdb98e967675456a71a0971b81058536cac8Chris Sosa self._devserver_pid = self._devserver_ssh.run_output(remote_cmd) 2676c55bdb98e967675456a71a0971b81058536cac8Chris Sosa 2686c55bdb98e967675456a71a0971b81058536cac8Chris Sosa 2696c55bdb98e967675456a71a0971b81058536cac8Chris Sosa def _kill_devserver_pid(self, pid): 2706c55bdb98e967675456a71a0971b81058536cac8Chris Sosa """Kills devserver with given pid and verifies devserver is down. 2716c55bdb98e967675456a71a0971b81058536cac8Chris Sosa 2726c55bdb98e967675456a71a0971b81058536cac8Chris Sosa @param pid: The pid of the devserver to kill. 2736c55bdb98e967675456a71a0971b81058536cac8Chris Sosa 2746c55bdb98e967675456a71a0971b81058536cac8Chris Sosa @raise client_utils.TimeoutError if we are unable to kill the devserver 2756c55bdb98e967675456a71a0971b81058536cac8Chris Sosa within the default timeouts (11 seconds). 2766c55bdb98e967675456a71a0971b81058536cac8Chris Sosa """ 2776c55bdb98e967675456a71a0971b81058536cac8Chris Sosa def _devserver_down(): 2786c55bdb98e967675456a71a0971b81058536cac8Chris Sosa return self._remote_devserver_pid() == None 2796c55bdb98e967675456a71a0971b81058536cac8Chris Sosa 2806c55bdb98e967675456a71a0971b81058536cac8Chris Sosa self._devserver_ssh.run('kill %s' % pid) 2816c55bdb98e967675456a71a0971b81058536cac8Chris Sosa try: 2826c55bdb98e967675456a71a0971b81058536cac8Chris Sosa client_utils.poll_for_condition(_devserver_down, 2836c55bdb98e967675456a71a0971b81058536cac8Chris Sosa sleep_interval=1) 2846c55bdb98e967675456a71a0971b81058536cac8Chris Sosa return 2856c55bdb98e967675456a71a0971b81058536cac8Chris Sosa except client_utils.TimeoutError: 2866c55bdb98e967675456a71a0971b81058536cac8Chris Sosa logging.warning('Could not gracefully shut down devserver.') 2876c55bdb98e967675456a71a0971b81058536cac8Chris Sosa 2886c55bdb98e967675456a71a0971b81058536cac8Chris Sosa self._devserver_ssh.run('kill -9 %s' % pid) 2896c55bdb98e967675456a71a0971b81058536cac8Chris Sosa client_utils.poll_for_condition(_devserver_down, timeout=1) 2906c55bdb98e967675456a71a0971b81058536cac8Chris Sosa 2916c55bdb98e967675456a71a0971b81058536cac8Chris Sosa 2926c55bdb98e967675456a71a0971b81058536cac8Chris Sosa def _remote_devserver_pid(self): 2936c55bdb98e967675456a71a0971b81058536cac8Chris Sosa """If a devserver is running on our port, return its pid.""" 2946c55bdb98e967675456a71a0971b81058536cac8Chris Sosa # fuser returns pid in its stdout if found. 2956c55bdb98e967675456a71a0971b81058536cac8Chris Sosa result = self._devserver_ssh.run('fuser -n tcp %d' % self._omaha_port, 2966c55bdb98e967675456a71a0971b81058536cac8Chris Sosa ignore_status=True) 2976c55bdb98e967675456a71a0971b81058536cac8Chris Sosa if result.exit_status == 0: 2986c55bdb98e967675456a71a0971b81058536cac8Chris Sosa return result.stdout.strip() 2996c55bdb98e967675456a71a0971b81058536cac8Chris Sosa 3006c55bdb98e967675456a71a0971b81058536cac8Chris Sosa 3016c55bdb98e967675456a71a0971b81058536cac8Chris Sosa def wait_for_devserver_to_start(self): 3026c55bdb98e967675456a71a0971b81058536cac8Chris Sosa """Returns True if the devserver has started within the time limit.""" 3030ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold timeout = self._WAIT_FOR_DEVSERVER_STARTED_SECONDS 3046c55bdb98e967675456a71a0971b81058536cac8Chris Sosa netloc = self.get_netloc() 3056c55bdb98e967675456a71a0971b81058536cac8Chris Sosa while(timeout > 0): 3066c55bdb98e967675456a71a0971b81058536cac8Chris Sosa time.sleep(self._WAIT_SLEEP_INTERVAL) 3076c55bdb98e967675456a71a0971b81058536cac8Chris Sosa timeout -= self._WAIT_SLEEP_INTERVAL 3086c55bdb98e967675456a71a0971b81058536cac8Chris Sosa if dev_server.DevServer.devserver_up('http://%s' % netloc): 3096c55bdb98e967675456a71a0971b81058536cac8Chris Sosa return True 3106c55bdb98e967675456a71a0971b81058536cac8Chris Sosa else: 3116c55bdb98e967675456a71a0971b81058536cac8Chris Sosa self.log_output() 3126c55bdb98e967675456a71a0971b81058536cac8Chris Sosa return False 3136c55bdb98e967675456a71a0971b81058536cac8Chris Sosa 3146c55bdb98e967675456a71a0971b81058536cac8Chris Sosa 3156c55bdb98e967675456a71a0971b81058536cac8Chris Sosa def get_netloc(self): 3166c55bdb98e967675456a71a0971b81058536cac8Chris Sosa """Returns the netloc (host:port) of the devserver.""" 3176c55bdb98e967675456a71a0971b81058536cac8Chris Sosa if not self._devserver_pid: 3186c55bdb98e967675456a71a0971b81058536cac8Chris Sosa raise error.TestError('no running omaha/devserver') 3196c55bdb98e967675456a71a0971b81058536cac8Chris Sosa 3206c55bdb98e967675456a71a0971b81058536cac8Chris Sosa 3216c55bdb98e967675456a71a0971b81058536cac8Chris Sosa return '%s:%s' % (self._omaha_host, self._omaha_port) 3220ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold 3236c55bdb98e967675456a71a0971b81058536cac8Chris Sosa 3246c55bdb98e967675456a71a0971b81058536cac8Chris Sosa def log_output(self): 3256c55bdb98e967675456a71a0971b81058536cac8Chris Sosa """Logs the output of the devserver log to logging.error.""" 3266c55bdb98e967675456a71a0971b81058536cac8Chris Sosa if self._devserver_pid: 3276c55bdb98e967675456a71a0971b81058536cac8Chris Sosa logging.error(self._devserver_ssh.run_output( 3286c55bdb98e967675456a71a0971b81058536cac8Chris Sosa 'cat %s' % self._devserver_output)) 3290ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold 3300ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold 3310ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold @staticmethod 3320ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold def _split_url(url): 3330ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold """Splits a URL into the URL base, path and file name.""" 3340ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold split_url = urlparse.urlsplit(url) 3350ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold url_base = urlparse.urlunsplit( 3360ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold [split_url.scheme, split_url.netloc, '', '', '']) 3370ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold url_path = url_file = '' 3380ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold if split_url.path: 3390ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold url_path, url_file = split_url.path.rsplit('/', 1) 3400ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold return url_base, url_path.lstrip('/'), url_file 3410ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold 3420ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold 3430ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold @staticmethod 3440ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold def _get_unique_port(dut_ip_addr): 3450ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold """Compute a unique IP port based on the DUT's IP address. 3460ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold 3470ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold We need a mapping that can be mirrored by a DUT running an official 3480ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold image, based only on the DUT's own state. Here, we simply take the two 3490ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold least significant bytes in the DUT's IPv4 address and bitwise-OR them 3500ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold with 0xc0000, resulting in a 16-bit IP port within the 3510ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold private/unallocated range. Using the least significant bytes of the IP 3520ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold address guarantees (sort of) that we'll have a unique mapping in a 3530ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold small lab setting. 3540ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold 3550ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold """ 3560ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold ip_addr_bytes = [int(byte_str) for byte_str in dut_ip_addr.split('.')] 3570ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold return (((ip_addr_bytes[2] << 8) | ip_addr_bytes[3] | 0x8000) & ~0x4000) 3580ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold 3590ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold 3600ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold def kill(self): 3610ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold """Kill private devserver, wait for it to die.""" 3626c55bdb98e967675456a71a0971b81058536cac8Chris Sosa if not self._devserver_pid: 3630ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold raise error.TestError('no running omaha/devserver') 3646c55bdb98e967675456a71a0971b81058536cac8Chris Sosa 3650ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold logging.info('killing omaha/devserver') 3666c55bdb98e967675456a71a0971b81058536cac8Chris Sosa self._kill_devserver_pid(self._devserver_pid) 3676c55bdb98e967675456a71a0971b81058536cac8Chris Sosa self._devserver_ssh.run('rm -f %s' % self._devserver_output) 3680ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold 3690ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold 3700ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnoldclass autoupdate_EndToEndTest(test.test): 3710ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold """Complete update test between two Chrome OS releases. 3720ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold 3730ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold Performs an end-to-end test of updating a ChromeOS device from one version 3740ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold to another. This script requires a running (possibly remote) servod 3750ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold instance connected to an actual servo board, which controls the DUT. It 3760ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold also assumes that a corresponding target (update) image was staged for 3770ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold download on the central Lorry/devserver. 3780ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold 3790ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold The test performs the following steps: 3800ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold 3810ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold 0. Stages the source image and target update payload on the central 3820ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold Lorry/devserver. 3830ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold 1. Spawns a private Omaha/devserver instance, configured to return the 3840ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold target (update) image URL in response for an update check. 3850ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold 2. Connects to servod. 3860ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold a. Resets the DUT to a known initial state. 3870ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold b. Installs a source image on the DUT via recovery. 3880ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold 3. Reboots the DUT with the new image. 3890ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold 4. Triggers an update check at the DUT. 3900ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold 5. Watches as the DUT obtains an update and applies it. 3910ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold 6. Repeats 3-5, ensuring that the next update check shows the new image 3920ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold version. 3930ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold 3940ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold """ 3950ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold version = 1 3960ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold 3970ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold # Timeout periods, given in seconds. 398f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa _WAIT_AFTER_SHUTDOWN_SECONDS = 10 399f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa _WAIT_AFTER_UPDATE_SECONDS = 20 400f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa _WAIT_FOR_USB_INSTALL_SECONDS = 4 * 60 401f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa _WAIT_FOR_MP_RECOVERY_SECONDS = 8 * 60 4020ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold _WAIT_FOR_INITIAL_UPDATE_CHECK_SECONDS = 12 * 60 403f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa _WAIT_FOR_DOWNLOAD_STARTED_SECONDS = 2 * 60 4046c55bdb98e967675456a71a0971b81058536cac8Chris Sosa _WAIT_FOR_DOWNLOAD_COMPLETED_SECONDS = 10 * 60 405f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa _WAIT_FOR_UPDATE_COMPLETED_SECONDS = 4 * 60 4060ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold _WAIT_FOR_UPDATE_CHECK_AFTER_REBOOT_SECONDS = 15 * 60 40703901089c0db27508a6b2ff61ae8b75eba77cf37Gilad Arnold _DEVSERVER_HOSTLOG_REQUEST_TIMEOUT_SECONDS = 30 4080ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold 4090ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold # Omaha event types/results, from update_engine/omaha_request_action.h 410f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa EVENT_TYPE_UNKNOWN = 0 4110ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold EVENT_TYPE_DOWNLOAD_COMPLETE = 1 412f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa EVENT_TYPE_INSTALL_COMPLETE = 2 413f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa EVENT_TYPE_UPDATE_COMPLETE = 3 414f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa EVENT_TYPE_DOWNLOAD_STARTED = 13 4150ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold EVENT_TYPE_DOWNLOAD_FINISHED = 14 416f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa EVENT_RESULT_ERROR = 0 417f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa EVENT_RESULT_SUCCESS = 1 418f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa EVENT_RESULT_SUCCESS_REBOOT = 2 4190ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold EVENT_RESULT_UPDATE_DEFERRED = 9 4200ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold 4210ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold 422f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa def _servo_dut_power_up(self): 4230ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold """Powers up the DUT, optionally simulating a Ctrl-D key press.""" 424f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa self._host.servo.power_short_press() 425f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa if self._dev_mode: 426f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa self._host.servo.pass_devmode() 4270ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold 4280ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold 429f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa def _servo_dut_reboot(self, disconnect_usbkey=False): 4300ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold """Reboots a DUT. 4310ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold 432e7bd9369b76e2bbdc230c3c1aeb9afc6e6ec60baVadim Bendebury @param disconnect_usbkey: detach USB flash device from the DUT before 433e7bd9369b76e2bbdc230c3c1aeb9afc6e6ec60baVadim Bendebury powering it back up; this is useful when (for example) a USB 434e7bd9369b76e2bbdc230c3c1aeb9afc6e6ec60baVadim Bendebury booted device need not see the attached USB key after the 435e7bd9369b76e2bbdc230c3c1aeb9afc6e6ec60baVadim Bendebury reboot. 4360ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold 4370ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold @raise error.TestFail if DUT fails to reboot. 4380ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold 4390ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold """ 4400ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold logging.info('rebooting dut') 441f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa self._host.servo.power_long_press() 4420ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold _wait(self._WAIT_AFTER_SHUTDOWN_SECONDS, 'after shutdown') 443e7bd9369b76e2bbdc230c3c1aeb9afc6e6ec60baVadim Bendebury if disconnect_usbkey: 444f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa self._host.servo.switch_usbkey('host') 445f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa 446f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa self._servo_dut_power_up() 447f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa if self._use_test_image: 448f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa if not self._host.wait_up(timeout=self._host.BOOT_TIMEOUT): 4490ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold raise error.TestFail( 4500ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold 'dut %s failed to boot after %d secs' % 451f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa (self._host.ip, self._host.BOOT_TIMEOUT)) 4520ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold else: 4530ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold # TODO(garnold) chromium-os:33766: implement waiting for MP-signed 4540ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold # images; ideas include waiting for a ping reply, or using a GPIO 4550ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold # signal. 4560ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold pass 4570ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold 4580ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold 459f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa def _install_mp_image(self, lorry_image_url): 4600ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold """Installs an MP-signed recovery image on a DUT. 4610ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold 4620ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold @param lorry_image_url: URL of the image on a Lorry/devserver 4630ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold """ 4640ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold # Flash DUT with source image version, using recovery. 4650ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold logging.info('installing source mp-signed image via recovery: %s', 4660ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold lorry_image_url) 467f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa self._host.servo.install_recovery_image( 4680ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold lorry_image_url, 4690ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold wait_timeout=self._WAIT_FOR_MP_RECOVERY_SECONDS) 4700ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold 4710ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold # Reboot the DUT after installation. 472f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa self._servo_dut_reboot(disconnect_usbkey=True) 4730ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold 4740ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold 475f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa def _install_test_image_with_servo(self, lorry_image_url): 4760ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold """Installs a test image on a DUT, booted via recovery. 4770ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold 4780ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold @param lorry_image_url: URL of the image on a Lorry/devserver 4790ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold @param is_dev_nmode: whether or not the DUT is in dev mode 4800ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold 4810ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold @raise error.TestFail if DUT cannot boot the test image from USB; 4820ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold AutotestHostRunError if failed to run the install command on the 4830ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold DUT. 4840ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold 4850ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold """ 4860ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold logging.info('installing source test image via recovery: %s', 4870ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold lorry_image_url) 488f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa self._host.servo.install_recovery_image(lorry_image_url) 4890ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold logging.info('waiting for image to boot') 490f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa if not self._host.wait_up(timeout=self._host.USB_BOOT_TIMEOUT): 4910ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold raise error.TestFail( 4920ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold 'dut %s boot from usb timed out after %d secs' % 493f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa (self._host, self._host.USB_BOOT_TIMEOUT)) 4940ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold logging.info('installing new image onto ssd') 4950ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold try: 496f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa cmd_result = self._host.run( 4970ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold 'chromeos-install --yes', 4980ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold timeout=self._WAIT_FOR_USB_INSTALL_SECONDS, 4990ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold stdout_tee=None, stderr_tee=None) 500f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa except error.AutotestHostRunError: 5010ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold # Dump stdout (with stderr) to the error log. 5020ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold logging.error('command failed, stderr:\n' + cmd_result.stderr) 5030ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold raise 5040ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold 5050ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold # Reboot the DUT after installation. 506f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa self._servo_dut_reboot(disconnect_usbkey=True) 5070ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold 5080ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold 509f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa def _trigger_test_update(self, omaha_netloc): 5100ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold """Trigger an update check on a test image. 5110ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold 5120ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold Uses update_engine_client via SSH. This is an async call, hence a very 5130ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold short timeout. 5140ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold 5150ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold @param omaha_netloc: the network location of the Omaha/devserver 5160ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold (http://host:port) 5170ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold 5180ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold @raise RootFSUpdateError if anything went wrong. 5190ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold 5200ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold """ 5210ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold omaha_update_url = urlparse.urlunsplit( 5220ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold ['http', omaha_netloc, '/update', '', '']) 523f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa updater = autoupdater.ChromiumOSUpdater(omaha_update_url, 524f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa host=self._host) 5250ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold updater.trigger_update() 5260ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold 5270ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold 528f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa def _get_rootdev(self): 52909706f18ea3878f1b173a63fdc92f2fc6450bb4dGilad Arnold """Returns the partition device containing the rootfs on a host. 53009706f18ea3878f1b173a63fdc92f2fc6450bb4dGilad Arnold 53109706f18ea3878f1b173a63fdc92f2fc6450bb4dGilad Arnold @return The rootfs partition device (string). 53209706f18ea3878f1b173a63fdc92f2fc6450bb4dGilad Arnold 53309706f18ea3878f1b173a63fdc92f2fc6450bb4dGilad Arnold @raise AutotestHostRunError if command failed to run on host. 53409706f18ea3878f1b173a63fdc92f2fc6450bb4dGilad Arnold 53509706f18ea3878f1b173a63fdc92f2fc6450bb4dGilad Arnold """ 53609706f18ea3878f1b173a63fdc92f2fc6450bb4dGilad Arnold # This command should return immediately, hence the short timeout. 537f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa return self._host.run('rootdev -s', timeout=10).stdout.strip() 53809706f18ea3878f1b173a63fdc92f2fc6450bb4dGilad Arnold 53909706f18ea3878f1b173a63fdc92f2fc6450bb4dGilad Arnold 540f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa def stage_image(self, lorry_devserver, image_uri, board, release, branch): 5410ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold """Stage a Chrome OS image on Lorry/devserver. 5420ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold 5430ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold @return URL of the staged image on the server. 5440ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold 5450ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold @raise error.TestError if there's a problem with staging. 5460ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold 5470ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold """ 5480ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold staged_url = None 549f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa if self._use_test_image: 5500ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold # For this call, we just need the URL path up to the image.zip file 5510ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold # (exclusive). 5520ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold image_uri_path = urlparse.urlsplit(image_uri).path.partition( 5530ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold 'image.zip')[0].strip('/') 5540ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold try: 5550ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold lorry_devserver.trigger_test_image_download(image_uri_path) 5560ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold staged_url = lorry_devserver.get_test_image_url( 5570ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold board, release, branch) 5580ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold except dev_server.DevServerException, e: 5590ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold raise error.TestError( 56003901089c0db27508a6b2ff61ae8b75eba77cf37Gilad Arnold 'failed to stage source test image: %s' % e) 5610ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold else: 5620ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold # TODO(garnold) chromium-os:33766: implement staging of MP-signed 5630ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold # images. 5640ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold pass 5650ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold 5660ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold if not staged_url: 5670ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold raise error.TestError('staged source test image url missing') 5680ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold return staged_url 5690ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold 5700ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold 5710ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold def stage_payload(self, lorry_devserver, payload_uri, board, release, 572f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa branch, is_delta, is_nton): 5730ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold """Stage an update target payload on Lorry/devserver. 5740ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold 5750ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold @return URL of the staged payload on the server. 5760ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold 5770ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold @raise error.TestError if there's a problem with staging. 5780ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold 5790ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold """ 5800ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold staged_url = None 581f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa if self._use_test_image: 5820ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold # For this call, we'll need the URL path without the payload file 5830ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold # name. 5840ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold payload_uri_path = urlparse.urlsplit(payload_uri).path.rsplit( 5850ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold '/', 1)[0].strip('/') 5860ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold try: 5870ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold lorry_devserver.trigger_download(payload_uri_path) 5880ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold if is_delta: 5890ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold staged_url = lorry_devserver.get_delta_payload_url( 5900ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold 'nton' if is_nton else 'mton', 5910ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold board, release, branch) 5920ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold else: 5930ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold staged_url = lorry_devserver.get_full_payload_url( 5940ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold board, release, branch) 5950ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold except dev_server.DevServerException, e: 596f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa raise error.TestError('failed to stage test payload: %s' % e) 5970ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold else: 5980ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold # TODO(garnold) chromium-os:33766: implement staging of MP-signed 5990ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold # images. 6000ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold pass 6010ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold 6020ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold if not staged_url: 603f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa raise error.TestError('staged test payload url missing') 604f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa 6050ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold return staged_url 6060ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold 6070ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold 608f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa def _install_source_image(self, image_url): 609f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa """Prepare the specified host with the image.""" 610f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa if self._use_servo: 611f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa # Install source image (test vs MP). 612f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa if self._use_test_image: 613f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa self._install_test_image_with_servo(image_url) 614f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa else: 615f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa self._install_mp_image(image_url) 616f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa 617f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa else: 618f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa # image_url is of the format that is in the devserver i.e. 619f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa # <hostname>/static/BRANCH/VERSION/update.gz. 6206c55bdb98e967675456a71a0971b81058536cac8Chris Sosa # We want to transform it to the correct omaha url which is 621f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa # <hostname>/update/BRANCH/VERSION. 622f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa image_url_dir = image_url.rpartition('/update.gz')[0] 623f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa image_url_dir = image_url_dir.replace('/static/', '/update/') 624f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa self._host.machine_install(image_url_dir, force_update=True) 625f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa 626f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa 627f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa def _stage_images_onto_devserver(self, lorry_devserver, test_conf): 628f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa """Stages images that will be used by the test onto the devserver. 629f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa 630f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa @return a tuple containing the urls of the source and target payloads. 631f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa """ 632f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa logging.info('staging images onto lorry/devserver (%s)', 633f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa lorry_devserver.url()) 634f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa 635f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa source_url = None 636f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa if self._use_servo: 637f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa source_url = self.stage_image( 638f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa lorry_devserver, test_conf['source_image_uri'], 639f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa test_conf['board'], test_conf['source_release'], 640f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa test_conf['source_branch']) 641f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa else: 642f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa source_url = self.stage_payload( 643f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa lorry_devserver, test_conf['source_image_uri'], 644f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa test_conf['board'], test_conf['source_release'], 645f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa test_conf['source_branch'], False, False) 646f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa 647f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa return source_url, self.stage_payload( 648f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa lorry_devserver, test_conf['target_payload_uri'], 649f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa test_conf['board'], test_conf['target_release'], 650f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa test_conf['target_branch'], 651f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa test_conf['update_type'] == 'delta', 652f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa test_conf['target_release'] == test_conf['source_release']) 653f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa 654f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa 655f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa def initialize(self): 656f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa """Sets up variables that will be used by test.""" 657f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa self._host = None 658f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa self._use_servo = False 659f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa self._dev_mode = False 660f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa self._omaha_devserver = None 661f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa 662f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa self._use_test_image = True 663f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa self._devserver_dir = global_config.global_config.get_config_value( 664f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa 'CROS', 'devserver_dir', default=None) 665f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa if self._devserver_dir is None: 666f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa raise error.TestError( 667f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa 'path to devserver source tree not provided; please define ' 668f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa 'devserver_dir under [CROS] in your shadow_config.ini') 669f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa 670f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa 671f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa def cleanup(self): 672f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa """Kill the omaha devserver if it's still around.""" 673f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa if self._omaha_devserver: 674f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa self._omaha_devserver.kill() 675f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa 676f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa self._omaha_devserver = None 677f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa 678f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa 679f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa def _verify_preconditions(self, test_conf): 680f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa """Validate input args make sense.""" 681f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa if self._use_servo and not self._host.servo: 682f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa raise error.AutotestError('Servo use specified but no servo ' 683f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa 'attached to host object.') 684f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa 685f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa if not self._use_test_image and not self._use_servo: 686f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa raise error.TestError("Can't install mp image without servo.") 687f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa 688f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa 689f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa def run_once(self, host, test_conf, use_servo): 6900ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold """Performs a complete auto update test. 6910ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold 6920ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold @param host: a host object representing the DUT 6930ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold @param test_conf: a dictionary containing test configuration values 694f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa @param use_servo: True whether we should use servo. 6950ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold 6960ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold @raise error.TestError if anything went wrong with setting up the test; 6970ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold error.TestFail if any part of the test has failed. 6980ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold 6990ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold """ 700f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa self._host = host 701f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa self._use_test_image = test_conf.get('image_type') != 'mp' 702f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa self._use_servo = use_servo 703f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa if self._use_servo: 704f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa self._dev_mode = self._host.servo.get('dev_mode') == 'on' 7050ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold 706f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa # Verify that our arguments are sane. 707f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa self._verify_preconditions(test_conf) 708d70e0c8bd47ea824c5a84c5565a46690d64e4aa6Gilad Arnold 7090ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold # Stage source images and update payloads on lorry/devserver. We use 7100ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold # the payload URI as argument for the lab's devserver load-balancing 7110ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold # mechanism. 7120ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold lorry_devserver = dev_server.ImageServer.resolve( 7130ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold test_conf['target_payload_uri']) 714f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa source_url, target_payload_url = self._stage_images_onto_devserver( 715f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa lorry_devserver, test_conf) 7160ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold 717f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa # Install the source image onto the DUT. 718f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa self._install_source_image(source_url) 71903901089c0db27508a6b2ff61ae8b75eba77cf37Gilad Arnold 720f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa # On test images, record the active root partition. 721f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa source_rootfs_partition = None 722f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa if self._use_test_image: 723f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa source_rootfs_partition = self._get_rootdev() 724f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa logging.info('source image rootfs partition: %s', 725f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa source_rootfs_partition) 7260ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold 7276c55bdb98e967675456a71a0971b81058536cac8Chris Sosa omaha_host = urlparse.urlparse(lorry_devserver.url()).hostname 728f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa self._omaha_devserver = OmahaDevserver( 729f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa omaha_host, self._devserver_dir, self._host.ip, 730f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa target_payload_url) 73109706f18ea3878f1b173a63fdc92f2fc6450bb4dGilad Arnold 7326c55bdb98e967675456a71a0971b81058536cac8Chris Sosa self._omaha_devserver.start_devserver() 7336c55bdb98e967675456a71a0971b81058536cac8Chris Sosa self._omaha_devserver.wait_for_devserver_to_start() 7340ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold 735f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa # Trigger an update (test vs MP). 7366c55bdb98e967675456a71a0971b81058536cac8Chris Sosa omaha_netloc = self._omaha_devserver.get_netloc() 737f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa if self._use_test_image: 738f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa self._trigger_test_update(omaha_netloc) 739f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa else: 740f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa # TODO(garnold) chromium-os:33766: use GPIOs to trigger an 741f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa # update. 742f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa pass 7430ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold 744f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa # Track update progress. 745f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa omaha_hostlog_url = urlparse.urlunsplit( 746f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa ['http', omaha_netloc, '/api/hostlog', 747f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa 'ip=' + self._host.ip, '']) 748f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa logging.info('polling update progress from omaha/devserver: %s', 749f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa omaha_hostlog_url) 750f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa log_verifier = UpdateEventLogVerifier( 751f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa omaha_hostlog_url, 752f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa self._DEVSERVER_HOSTLOG_REQUEST_TIMEOUT_SECONDS) 753f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa 754f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa # Verify chain of events in a successful update process. 755f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa chain = ExpectedUpdateEventChain( 756f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa (self._WAIT_FOR_INITIAL_UPDATE_CHECK_SECONDS, 757f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa ExpectedUpdateEvent( 758f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa version=test_conf['source_release'])), 759f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa (self._WAIT_FOR_DOWNLOAD_STARTED_SECONDS, 760f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa ExpectedUpdateEvent( 761f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa event_type=self.EVENT_TYPE_DOWNLOAD_STARTED, 762f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa event_result=self.EVENT_RESULT_SUCCESS, 763f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa version=test_conf['source_release'])), 764f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa (self._WAIT_FOR_DOWNLOAD_COMPLETED_SECONDS, 765f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa ExpectedUpdateEvent( 766f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa event_type=self.EVENT_TYPE_DOWNLOAD_FINISHED, 767f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa event_result=self.EVENT_RESULT_SUCCESS, 768f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa version=test_conf['source_release'])), 769f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa (self._WAIT_FOR_UPDATE_COMPLETED_SECONDS, 770f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa ExpectedUpdateEvent( 771f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa event_type=self.EVENT_TYPE_UPDATE_COMPLETE, 772f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa event_result=self.EVENT_RESULT_SUCCESS, 773f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa version=test_conf['source_release']))) 774f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa 775f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa if not log_verifier.verify_expected_event_chain(chain): 776f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa raise error.TestFail( 777f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa 'could not verify that update was successful') 778f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa 779f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa # Wait after an update completion (safety margin). 780f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa _wait(self._WAIT_AFTER_UPDATE_SECONDS, 'after update completion') 781f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa 782f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa # Reboot the DUT after the update. 783f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa if use_servo: 784f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa self._servo_dut_reboot() 785f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa else: 786f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa self._host.reboot() 7870ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold 788f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa # Trigger a second update check (again, test vs MP). 789f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa if self._use_test_image: 790f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa self._trigger_test_update(omaha_netloc) 791f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa else: 792f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa # TODO(garnold) chromium-os:33766: use GPIOs to trigger an 793f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa # update. 794f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa pass 7950ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold 796f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa # Observe post-reboot update check, which should indicate that the 797f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa # image version has been updated. 798f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa chain = ExpectedUpdateEventChain( 799f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa (self._WAIT_FOR_UPDATE_CHECK_AFTER_REBOOT_SECONDS, 800f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa ExpectedUpdateEvent( 801f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa event_type=self.EVENT_TYPE_UPDATE_COMPLETE, 802f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa event_result=self.EVENT_RESULT_SUCCESS_REBOOT, 803f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa version=test_conf['target_release'], 804f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa previous_version=test_conf['source_release']))) 805f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa if not log_verifier.verify_expected_event_chain(chain): 806f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa raise error.TestFail('could not verify that machine rebooted ' 807f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa 'after update') 808f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa 809f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa # On test images, make sure we're using a different partition after 810f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa # the update. 811f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa if self._use_test_image: 812f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa target_rootfs_partition = self._get_rootdev() 813f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa if target_rootfs_partition == source_rootfs_partition: 814f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa raise error.TestFail( 815f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa 'rootfs partition did not change (%s)' % 816f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa target_rootfs_partition) 8170ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold 818f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa logging.info('target image rootfs partition: %s', 819f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa target_rootfs_partition) 8200ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold 821