autoupdate_EndToEndTest.py revision 03901089c0db27508a6b2ff61ae8b75eba77cf37
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 socket
80ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnoldimport subprocess
903901089c0db27508a6b2ff61ae8b75eba77cf37Gilad Arnoldimport tempfile
100ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnoldimport time
110ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnoldimport urllib2
120ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnoldimport urlparse
1303901089c0db27508a6b2ff61ae8b75eba77cf37Gilad Arnold
1403901089c0db27508a6b2ff61ae8b75eba77cf37Gilad Arnoldfrom autotest_lib.client.common_lib import error, global_config, site_utils
150ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnoldfrom autotest_lib.client.common_lib.cros import autoupdater, dev_server
160ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnoldfrom autotest_lib.server import test
170ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
180ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
190ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnolddef _wait(secs, desc=None):
200ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold    """Emits a log message and sleeps for a given number of seconds."""
210ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold    msg = 'waiting %s seconds' % secs
220ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold    if desc:
230ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        msg += ' (%s)' % desc
240ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold    logging.info(msg)
250ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold    time.sleep(secs)
260ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
270ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
280ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnoldclass ExpectedUpdateEvent(object):
290ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold    """Defines an expected event in a host update process."""
300ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold    def __init__(self, event_type=None, event_result=None, version=None,
310ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold                 previous_version=None):
320ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        self._expected_attrs = {
330ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold            'event_type': event_type,
340ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold            'event_result': event_result,
350ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold            'version': version,
360ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold            'previous_version': previous_version,
370ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        }
380ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
390ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
400ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold    def __str__(self):
410ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        return ' '.join(['%s=%s' % (attr_name, attr_val or 'any')
420ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold                         for attr_name, attr_val
430ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold                         in self._expected_attrs.iteritems()])
440ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
450ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
460ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold    def verify(self, actual_event):
470ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        """Verify the attributes of an actual event.
480ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
490ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        @params actual_event: a dictionary containing event attributes
500ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
510ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        @return True if all attributes as expected, False otherwise.
520ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
530ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        """
540ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        return all([self._verify_attr(attr_name, expected_attr_val,
550ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold                                      actual_event.get(attr_name))
560ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold                    for attr_name, expected_attr_val
570ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold                    in self._expected_attrs.iteritems() if expected_attr_val])
580ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
590ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
600ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold    def _verify_attr(self, attr_name, expected_attr_val, actual_attr_val):
610ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        """Verifies that an actual log event attributes matches expected on.
620ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
630ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        @param attr_name: name of the attribute to verify
640ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        @param expected_attr_val: expected attribute value
650ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        @param actual_attr_val: actual attribute value
660ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
670ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        @return True if actual value is present and matches, False otherwise.
680ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
690ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        """
700ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        if not (actual_attr_val and
710ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold                str(actual_attr_val) == str(expected_attr_val)):
720ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold            logging.error(
730ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold                    'actual %s (%s) not as expected (%s)',
740ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold                    attr_name, actual_attr_val, expected_attr_val)
750ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold            return False
760ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
770ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        return True
780ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
790ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
800ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnoldclass ExpectedUpdateEventChain(object):
810ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold    """Defines a chain of expected update events."""
820ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold    def __init__(self, *expected_event_chain_args):
830ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        """Initialize the chain object.
840ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
850ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        @param expected_event_chain_args: list of tuples arguments, each
860ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold               containing a timeout (in seconds) and an ExpectedUpdateEvent
870ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold               object.
880ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
890ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        """
900ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        self._expected_event_chain = expected_event_chain_args
910ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
920ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
930ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold    def _format_event_with_timeout(self, timeout, expected_event):
940ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        return ('%s %s' %
950ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold                (expected_event,
960ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold                 ('within %s seconds' % timeout) if timeout
970ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold                 else 'indefinitely'))
980ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
990ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
1000ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold    def __str__(self):
1010ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        return ('[%s]' %
1020ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold                ', '.join(
1030ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold                    [self._format_event_with_timeout(timeout, expected_event)
1040ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold                     for timeout, expected_event
1050ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold                     in self._expected_event_chain]))
1060ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
1070ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
1080ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold    def __repr__(self):
1090ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        return str(self._expected_event_chain)
1100ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
1110ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
1120ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold    def verify(self, get_next_event):
1130ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        """Verifies that an actual stream of events complies.
1140ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
1150ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        @param get_next_event: a function returning the next event
1160ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
1170ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        @return True if chain was satisfied, False otherwise.
1180ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
1190ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        """
1200ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        for timeout, expected_event in self._expected_event_chain:
1210ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold            logging.info(
1220ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold                    'expecting %s',
1230ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold                    self._format_event_with_timeout(timeout, expected_event))
1240ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold            if not self._verify_event_with_timeout(
1250ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold                    timeout, expected_event, get_next_event):
1260ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold                return False
1270ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        return True
1280ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
1290ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
1300ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold    def _verify_event_with_timeout(self, timeout, expected_event,
1310ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold                                   get_next_event):
1320ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        """Verify an expected event occurs within a given timeout.
1330ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
1340ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        @param timeout: specified in seconds
1350ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        @param expected_event: an expected event specification
1360ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        @param get_next_event: function returning the next event in a stream
1370ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
1380ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        @return True if event complies, False otherwise.
1390ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
1400ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        """
1410ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        base_timestamp = curr_timestamp = time.time()
1420ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        expired_timestamp = base_timestamp + timeout
1430ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        while curr_timestamp <= expired_timestamp:
1440ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold            new_event = get_next_event()
1450ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold            if new_event:
1460ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold                logging.info('event received after %s seconds',
1470ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold                             curr_timestamp - base_timestamp)
1480ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold                return expected_event.verify(new_event)
1490ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
1500ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold            # No new events, sleep for one second only (so we don't miss
1510ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold            # events at the end of the allotted timeout).
1520ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold            time.sleep(1)
1530ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold            curr_timestamp = time.time()
1540ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
1550ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        logging.error('timeout expired')
1560ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        return False
1570ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
1580ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
1590ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnoldclass UpdateEventLogVerifier(object):
1600ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold    """Verifies update event chains on a devserver update log."""
16103901089c0db27508a6b2ff61ae8b75eba77cf37Gilad Arnold    def __init__(self, event_log_url, url_request_timeout=None):
1620ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        self._event_log_url = event_log_url
16303901089c0db27508a6b2ff61ae8b75eba77cf37Gilad Arnold        self._url_request_timeout = url_request_timeout
1640ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        self._event_log = []
1650ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        self._num_consumed_events = 0
1660ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
1670ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
1680ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold    def verify_expected_event_chain(self, expected_event_chain):
1690ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        """Verify a given event chain."""
1700ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        return expected_event_chain.verify(self._get_next_log_event)
1710ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
1720ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
1730ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold    def _get_next_log_event(self):
1740ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        """Returns the next event in an event log.
1750ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
1760ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        Uses the URL handed to it during initialization to obtain the host log
1770ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        from a devserver. If new events are encountered, the first of them is
1780ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        consumed and returned.
1790ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
1800ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        @return The next new event in the host log, as reported by devserver;
18103901089c0db27508a6b2ff61ae8b75eba77cf37Gilad Arnold                None if no such event was found or an error occurred.
1820ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
1830ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        """
1840ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        # (Re)read event log from devserver, if necessary.
1850ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        if len(self._event_log) <= self._num_consumed_events:
18603901089c0db27508a6b2ff61ae8b75eba77cf37Gilad Arnold            try:
18703901089c0db27508a6b2ff61ae8b75eba77cf37Gilad Arnold                if self._url_request_timeout:
18803901089c0db27508a6b2ff61ae8b75eba77cf37Gilad Arnold                    conn = urllib2.urlopen(self._event_log_url,
18903901089c0db27508a6b2ff61ae8b75eba77cf37Gilad Arnold                                           timeout=self._url_request_timeout)
19003901089c0db27508a6b2ff61ae8b75eba77cf37Gilad Arnold                else:
19103901089c0db27508a6b2ff61ae8b75eba77cf37Gilad Arnold                    conn = urllib2.urlopen(self._event_log_url)
19203901089c0db27508a6b2ff61ae8b75eba77cf37Gilad Arnold            except urllib2.URLError, e:
19303901089c0db27508a6b2ff61ae8b75eba77cf37Gilad Arnold                logging.warning('urlopen failed: %s', e)
19403901089c0db27508a6b2ff61ae8b75eba77cf37Gilad Arnold                return None
19503901089c0db27508a6b2ff61ae8b75eba77cf37Gilad Arnold
1960ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold            event_log_resp = conn.read()
1970ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold            conn.close()
1980ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold            self._event_log = json.loads(event_log_resp)
1990ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
20003901089c0db27508a6b2ff61ae8b75eba77cf37Gilad Arnold        # Return next new event, if one is found.
2010ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        if len(self._event_log) > self._num_consumed_events:
2020ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold            new_event = self._event_log[self._num_consumed_events]
2030ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold            self._num_consumed_events += 1
2040ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold            logging.info('consumed new event: %s', new_event)
2050ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold            return new_event
2060ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
2070ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
2080ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnoldclass OmahaDevserver(object):
2090ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold    """Spawns a test-private devserver instance."""
2100ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold    _WAIT_FOR_DEVSERVER_STARTED_SECONDS = 15
21103901089c0db27508a6b2ff61ae8b75eba77cf37Gilad Arnold    _WAIT_SLEEP_INTERVAL = 1
2120ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
2130ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
21403901089c0db27508a6b2ff61ae8b75eba77cf37Gilad Arnold    def __init__(self, omaha_host, devserver_dir, dut_ip_addr,
21503901089c0db27508a6b2ff61ae8b75eba77cf37Gilad Arnold                 update_payload_lorry_url):
2160ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        """Starts a private devserver instance, operating at Omaha capacity.
2170ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
2180ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        @param omaha_host: host address where the devserver is spawned.
21903901089c0db27508a6b2ff61ae8b75eba77cf37Gilad Arnold        @param devserver_dir: path to the devserver source directory
2200ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        @param dut_ip_addr: the IP address of the client DUT, used for deriving
2210ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold               a unique port number.
2220ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        @param update_payload_lorry_url: URL to provision for update requests.
2230ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
2240ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        @raise error.TestError when things go wrong.
2250ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
2260ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        """
2270ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        # First, obtain the target URL base / image strings.
2280ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        if not update_payload_lorry_url:
2290ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold            raise error.TestError('missing update payload url')
2300ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        update_payload_url_base, update_payload_path, _ = self._split_url(
2310ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold                update_payload_lorry_url)
2320ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
2330ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        # Second, compute a unique port for the DUT update checks to use, based
2340ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        # on the DUT's IP address.
2350ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        self._omaha_port = self._get_unique_port(dut_ip_addr)
2360ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        logging.debug('dut ip addr: %s => omaha/devserver port: %d',
2370ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold                      dut_ip_addr, self._omaha_port)
2380ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
2390ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        # Invoke the Omaha/devserver.
2400ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        cmdlist = [
24103901089c0db27508a6b2ff61ae8b75eba77cf37Gilad Arnold                './devserver.py',
2420ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold                '--archive_dir=static/',
2430ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold                '--payload=%s' % update_payload_path,
2440ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold                '--port=%d' % self._omaha_port,
2450ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold                '--remote_payload',
2460ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold                '--urlbase=%s' % update_payload_url_base,
2470ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold                '--max_updates=1',
2480ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold                '--host_log',
2490ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        ]
25003901089c0db27508a6b2ff61ae8b75eba77cf37Gilad Arnold        logging.info('launching omaha/devserver on %s (%s): %s',
25103901089c0db27508a6b2ff61ae8b75eba77cf37Gilad Arnold                     omaha_host, devserver_dir, ' '.join(cmdlist))
2520ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        # TODO(garnold) invoke omaha/devserver remotely! The host needs to be
2530ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        # either globally known to all DUTs, or inferrable based on the DUT's
2540ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        # own IP address, or otherwise provisioned to it.
2550ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        is_omaha_devserver_local = (
2560ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold                omaha_host in ['localhost', socket.gethostname()])
2570ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        if not is_omaha_devserver_local:
2580ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold          raise error.TestError(
2590ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold              'remote omaha/devserver invocation unsupported yet')
2600ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        # We are using subprocess directly (as opposed to existing util
2610ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        # wrappers like utils.run() or utils.BgJob) because we need to be able
2620ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        # to terminate the subprocess once the test finishes.
26303901089c0db27508a6b2ff61ae8b75eba77cf37Gilad Arnold        devserver_output_namedtemp = tempfile.NamedTemporaryFile()
26403901089c0db27508a6b2ff61ae8b75eba77cf37Gilad Arnold        self._devserver = subprocess.Popen(
26503901089c0db27508a6b2ff61ae8b75eba77cf37Gilad Arnold                cmdlist, stdin=subprocess.PIPE,
26603901089c0db27508a6b2ff61ae8b75eba77cf37Gilad Arnold                stdout=devserver_output_namedtemp.file,
26703901089c0db27508a6b2ff61ae8b75eba77cf37Gilad Arnold                stderr=subprocess.STDOUT, cwd=devserver_dir or None)
2680ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        timeout = self._WAIT_FOR_DEVSERVER_STARTED_SECONDS
26903901089c0db27508a6b2ff61ae8b75eba77cf37Gilad Arnold        devserver_output_log = []
27003901089c0db27508a6b2ff61ae8b75eba77cf37Gilad Arnold        with open(devserver_output_namedtemp.name, 'r') as devserver_output:
27103901089c0db27508a6b2ff61ae8b75eba77cf37Gilad Arnold            while timeout > 0 and self._devserver.returncode is None:
27203901089c0db27508a6b2ff61ae8b75eba77cf37Gilad Arnold                time.sleep(self._WAIT_SLEEP_INTERVAL)
27303901089c0db27508a6b2ff61ae8b75eba77cf37Gilad Arnold                timeout -= self._WAIT_SLEEP_INTERVAL
27403901089c0db27508a6b2ff61ae8b75eba77cf37Gilad Arnold                devserver_started = False
27503901089c0db27508a6b2ff61ae8b75eba77cf37Gilad Arnold                while not devserver_started:
27603901089c0db27508a6b2ff61ae8b75eba77cf37Gilad Arnold                    line = devserver_output.readline()
27703901089c0db27508a6b2ff61ae8b75eba77cf37Gilad Arnold                    if not line:
27803901089c0db27508a6b2ff61ae8b75eba77cf37Gilad Arnold                        break
27903901089c0db27508a6b2ff61ae8b75eba77cf37Gilad Arnold                    log_line = '[devserver]' + line.rstrip('\n')
28003901089c0db27508a6b2ff61ae8b75eba77cf37Gilad Arnold                    logging.debug(log_line)
28103901089c0db27508a6b2ff61ae8b75eba77cf37Gilad Arnold                    devserver_output_log.append(log_line)
28203901089c0db27508a6b2ff61ae8b75eba77cf37Gilad Arnold                    devserver_started = 'Bus STARTED' in line
28303901089c0db27508a6b2ff61ae8b75eba77cf37Gilad Arnold                else:
2840ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold                    break
2850ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold            else:
28603901089c0db27508a6b2ff61ae8b75eba77cf37Gilad Arnold                raise error.TestError(
28703901089c0db27508a6b2ff61ae8b75eba77cf37Gilad Arnold                    'omaha/devserver not running, error log:\n%s' %
28803901089c0db27508a6b2ff61ae8b75eba77cf37Gilad Arnold                    '\n'.join(devserver_output_log))
2890ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
2900ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        self._omaha_host = site_utils.externalize_host(omaha_host)
2910ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
2920ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
2930ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold    @staticmethod
2940ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold    def _split_url(url):
2950ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        """Splits a URL into the URL base, path and file name."""
2960ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        split_url = urlparse.urlsplit(url)
2970ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        url_base = urlparse.urlunsplit(
2980ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold                [split_url.scheme, split_url.netloc, '', '', ''])
2990ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        url_path = url_file = ''
3000ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        if split_url.path:
3010ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold            url_path, url_file = split_url.path.rsplit('/', 1)
3020ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        return url_base, url_path.lstrip('/'), url_file
3030ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
3040ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
3050ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold    @staticmethod
3060ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold    def _get_unique_port(dut_ip_addr):
3070ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        """Compute a unique IP port based on the DUT's IP address.
3080ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
3090ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        We need a mapping that can be mirrored by a DUT running an official
3100ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        image, based only on the DUT's own state. Here, we simply take the two
3110ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        least significant bytes in the DUT's IPv4 address and bitwise-OR them
3120ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        with 0xc0000, resulting in a 16-bit IP port within the
3130ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        private/unallocated range. Using the least significant bytes of the IP
3140ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        address guarantees (sort of) that we'll have a unique mapping in a
3150ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        small lab setting.
3160ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
3170ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        """
3180ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        ip_addr_bytes = [int(byte_str) for byte_str in dut_ip_addr.split('.')]
3190ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        return (((ip_addr_bytes[2] << 8) | ip_addr_bytes[3] | 0x8000) & ~0x4000)
3200ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
3210ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
3220ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold    def get_netloc(self):
3230ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        if not self._devserver:
3240ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold            raise error.TestError('no running omaha/devserver')
3250ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        return '%s:%s' % (self._omaha_host, self._omaha_port)
3260ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
3270ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
3280ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold    def kill(self):
3290ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        """Kill private devserver, wait for it to die."""
3300ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        if not self._devserver:
3310ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold            raise error.TestError('no running omaha/devserver')
3320ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        logging.info('killing omaha/devserver')
3330ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        self._devserver.terminate()
3340ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        self._devserver.communicate()
3350ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        self._devserver = None
3360ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
3370ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
3380ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnoldclass autoupdate_EndToEndTest(test.test):
3390ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold    """Complete update test between two Chrome OS releases.
3400ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
3410ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold    Performs an end-to-end test of updating a ChromeOS device from one version
3420ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold    to another. This script requires a running (possibly remote) servod
3430ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold    instance connected to an actual servo board, which controls the DUT. It
3440ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold    also assumes that a corresponding target (update) image was staged for
3450ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold    download on the central Lorry/devserver.
3460ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
3470ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold    The test performs the following steps:
3480ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
3490ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold      0. Stages the source image and target update payload on the central
3500ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold         Lorry/devserver.
3510ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold      1. Spawns a private Omaha/devserver instance, configured to return the
3520ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold         target (update) image URL in response for an update check.
3530ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold      2. Connects to servod.
3540ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold         a. Resets the DUT to a known initial state.
3550ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold         b. Installs a source image on the DUT via recovery.
3560ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold      3. Reboots the DUT with the new image.
3570ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold      4. Triggers an update check at the DUT.
3580ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold      5. Watches as the DUT obtains an update and applies it.
3590ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold      6. Repeats 3-5, ensuring that the next update check shows the new image
3600ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold         version.
3610ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
3620ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold    """
3630ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold    version = 1
3640ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
3650ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold    # Timeout periods, given in seconds.
3660ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold    _WAIT_AFTER_SHUTDOWN_SECONDS        = 10
3670ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold    _WAIT_AFTER_UPDATE_SECONDS          = 20
3680ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold    _WAIT_FOR_USB_INSTALL_SECONDS       = 4 * 60
3690ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold    _WAIT_FOR_MP_RECOVERY_SECONDS       = 8 * 60
3700ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold    _WAIT_FOR_INITIAL_UPDATE_CHECK_SECONDS = 12 * 60
3710ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold    _WAIT_FOR_DOWNLOAD_STARTED_SECONDS     = 2 * 60
3720ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold    _WAIT_FOR_DOWNLOAD_COMPLETED_SECONDS   = 5 * 60
3730ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold    _WAIT_FOR_UPDATE_COMPLETED_SECONDS     = 4 * 60
3740ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold    _WAIT_FOR_UPDATE_CHECK_AFTER_REBOOT_SECONDS = 15 * 60
37503901089c0db27508a6b2ff61ae8b75eba77cf37Gilad Arnold    _DEVSERVER_HOSTLOG_REQUEST_TIMEOUT_SECONDS = 30
3760ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
3770ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold    # Omaha event types/results, from update_engine/omaha_request_action.h
3780ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold    EVENT_TYPE_UNKNOWN           = 0
3790ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold    EVENT_TYPE_DOWNLOAD_COMPLETE = 1
3800ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold    EVENT_TYPE_INSTALL_COMPLETE  = 2
3810ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold    EVENT_TYPE_UPDATE_COMPLETE   = 3
3820ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold    EVENT_TYPE_DOWNLOAD_STARTED  = 13
3830ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold    EVENT_TYPE_DOWNLOAD_FINISHED = 14
3840ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold    EVENT_RESULT_ERROR           = 0
3850ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold    EVENT_RESULT_SUCCESS         = 1
3860ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold    EVENT_RESULT_SUCCESS_REBOOT  = 2
3870ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold    EVENT_RESULT_UPDATE_DEFERRED = 9
3880ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
3890ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
3900ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold    def _servo_dut_power_up(self, host, is_dev_mode):
3910ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        """Powers up the DUT, optionally simulating a Ctrl-D key press."""
3920ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        host.servo.power_short_press()
3930ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        if is_dev_mode:
3940ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold            host.servo.pass_devmode()
3950ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
3960ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
3970ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold    def _servo_dut_reboot(self, host, is_dev_mode, is_using_test_images,
3980ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold                          is_disable_usb_hub=False):
3990ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        """Reboots a DUT.
4000ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
4010ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        @param host: a host object
4020ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        @param is_dev_mode: whether or not the DUT is in dev mode
4030ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        @param is_using_test_images: whether or not a test image should be
4040ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold               assumed
4050ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        @param is_disable_usb_hub: disabled the servo USB hub in between power
4060ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold               off/on cycles; this is useful when (for example) a USB booted
4070ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold               device need not see the attached USB key after the reboot.
4080ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
4090ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        @raise error.TestFail if DUT fails to reboot.
4100ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
4110ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        """
4120ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        logging.info('rebooting dut')
4130ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        host.servo.power_long_press()
4140ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        _wait(self._WAIT_AFTER_SHUTDOWN_SECONDS, 'after shutdown')
4150ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        if is_disable_usb_hub:
4160ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold            host.servo.disable_usb_hub()
4170ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        self._servo_dut_power_up(host, is_dev_mode)
4180ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        if is_using_test_images:
4190ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold            if not host.wait_up(timeout=host.BOOT_TIMEOUT):
4200ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold                raise error.TestFail(
4210ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold                        'dut %s failed to boot after %d secs' %
4220ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold                        (host.ip, host.BOOT_TIMEOUT))
4230ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        else:
4240ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold          # TODO(garnold) chromium-os:33766: implement waiting for MP-signed
4250ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold          # images; ideas include waiting for a ping reply, or using a GPIO
4260ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold          # signal.
4270ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold          pass
4280ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
4290ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
4300ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold    def _install_mp_image(self, host, lorry_image_url, is_dev_mode):
4310ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        """Installs an MP-signed recovery image on a DUT.
4320ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
4330ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        @param host: a host object
4340ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        @param lorry_image_url: URL of the image on a Lorry/devserver
4350ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        @param is_dev_nmode: whether or not the DUT is in dev mode
4360ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
4370ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        """
4380ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        # Flash DUT with source image version, using recovery.
4390ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        logging.info('installing source mp-signed image via recovery: %s',
4400ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold                     lorry_image_url)
4410ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        host.servo.install_recovery_image(
4420ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold                lorry_image_url,
4430ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold                wait_timeout=self._WAIT_FOR_MP_RECOVERY_SECONDS)
4440ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
4450ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        # Reboot the DUT after installation.
4460ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        self._servo_dut_reboot(host, is_dev_mode, False,
4470ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold                               is_disable_usb_hub=True)
4480ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
4490ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
4500ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold    def _install_test_image(self, host, lorry_image_url, is_dev_mode):
4510ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        """Installs a test image on a DUT, booted via recovery.
4520ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
4530ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        @param host: a host object
4540ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        @param lorry_image_url: URL of the image on a Lorry/devserver
4550ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        @param is_dev_nmode: whether or not the DUT is in dev mode
4560ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
4570ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        @raise error.TestFail if DUT cannot boot the test image from USB;
4580ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold               AutotestHostRunError if failed to run the install command on the
4590ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold               DUT.
4600ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
4610ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        """
4620ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        logging.info('installing source test image via recovery: %s',
4630ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold                     lorry_image_url)
4640ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        host.servo.install_recovery_image(lorry_image_url)
4650ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        logging.info('waiting for image to boot')
4660ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        if not host.wait_up(timeout=host.USB_BOOT_TIMEOUT):
4670ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold          raise error.TestFail(
4680ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold              'dut %s boot from usb timed out after %d secs' %
4690ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold              (host, host.USB_BOOT_TIMEOUT))
4700ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        logging.info('installing new image onto ssd')
4710ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        try:
4720ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold            cmd_result = host.run(
4730ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold                    'chromeos-install --yes',
4740ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold                    timeout=self._WAIT_FOR_USB_INSTALL_SECONDS,
4750ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold                    stdout_tee=None, stderr_tee=None)
4760ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        except AutotestHostRunError, e:
4770ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold            # Dump stdout (with stderr) to the error log.
4780ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold            logging.error('command failed, stderr:\n' + cmd_result.stderr)
4790ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold            raise
4800ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
4810ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        # Reboot the DUT after installation.
4820ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        self._servo_dut_reboot(host, is_dev_mode, True,
4830ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold                               is_disable_usb_hub=True)
4840ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
4850ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
4860ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold    def _trigger_test_update(self, host, omaha_netloc):
4870ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        """Trigger an update check on a test image.
4880ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
4890ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        Uses update_engine_client via SSH. This is an async call, hence a very
4900ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        short timeout.
4910ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
4920ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        @param host: a host object
4930ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        @param omaha_netloc: the network location of the Omaha/devserver
4940ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold               (http://host:port)
4950ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
4960ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        @raise RootFSUpdateError if anything went wrong.
4970ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
4980ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        """
4990ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        omaha_update_url = urlparse.urlunsplit(
5000ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold                ['http', omaha_netloc, '/update', '', ''])
5010ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        updater = autoupdater.ChromiumOSUpdater(omaha_update_url, host=host)
5020ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        updater.trigger_update()
5030ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
5040ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
5050ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold    def stage_image(self, lorry_devserver, image_uri, board, release, branch,
5060ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold                    is_using_test_images):
5070ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        """Stage a Chrome OS image on Lorry/devserver.
5080ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
5090ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        @return URL of the staged image on the server.
5100ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
5110ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        @raise error.TestError if there's a problem with staging.
5120ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
5130ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        """
5140ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        staged_url = None
5150ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        if is_using_test_images:
5160ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold            # For this call, we just need the URL path up to the image.zip file
5170ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold            # (exclusive).
5180ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold            image_uri_path = urlparse.urlsplit(image_uri).path.partition(
5190ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold                    'image.zip')[0].strip('/')
5200ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold            try:
5210ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold                lorry_devserver.trigger_test_image_download(image_uri_path)
5220ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold                staged_url = lorry_devserver.get_test_image_url(
5230ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold                        board, release, branch)
5240ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold            except dev_server.DevServerException, e:
5250ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold                raise error.TestError(
52603901089c0db27508a6b2ff61ae8b75eba77cf37Gilad Arnold                        'failed to stage source test image: %s' % e)
5270ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        else:
5280ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold            # TODO(garnold) chromium-os:33766: implement staging of MP-signed
5290ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold            # images.
5300ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold            pass
5310ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
5320ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        if not staged_url:
5330ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold            raise error.TestError('staged source test image url missing')
5340ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        return staged_url
5350ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
5360ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
5370ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold    def stage_payload(self, lorry_devserver, payload_uri, board, release,
5380ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold                      branch, is_using_test_images, is_delta, is_nton):
5390ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        """Stage an update target payload on Lorry/devserver.
5400ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
5410ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        @return URL of the staged payload on the server.
5420ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
5430ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        @raise error.TestError if there's a problem with staging.
5440ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
5450ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        """
5460ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        staged_url = None
5470ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        if is_using_test_images:
5480ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold            # For this call, we'll need the URL path without the payload file
5490ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold            # name.
5500ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold            payload_uri_path = urlparse.urlsplit(payload_uri).path.rsplit(
5510ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold                    '/', 1)[0].strip('/')
5520ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold            try:
5530ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold                lorry_devserver.trigger_download(payload_uri_path)
5540ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold                if is_delta:
5550ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold                    staged_url = lorry_devserver.get_delta_payload_url(
5560ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold                            'nton' if is_nton else 'mton',
5570ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold                            board, release, branch)
5580ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold                else:
5590ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold                    staged_url = lorry_devserver.get_full_payload_url(
5600ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold                            board, release, branch)
5610ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold            except dev_server.DevServerException, e:
5620ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold                raise error.TestError(
56303901089c0db27508a6b2ff61ae8b75eba77cf37Gilad Arnold                        'failed to stage target test payload: %s' % e)
5640ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        else:
5650ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold            # TODO(garnold) chromium-os:33766: implement staging of MP-signed
5660ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold            # images.
5670ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold            pass
5680ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
5690ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        if not staged_url:
5700ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold            raise error.TestError('staged target test payload url missing')
5710ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        return staged_url
5720ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
5730ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
5740ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold    def run_once(self, host, test_conf):
5750ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        """Performs a complete auto update test.
5760ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
5770ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        @param host: a host object representing the DUT
5780ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        @param test_conf: a dictionary containing test configuration values
5790ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
5800ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        @raise error.TestError if anything went wrong with setting up the test;
5810ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold               error.TestFail if any part of the test has failed.
5820ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
5830ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        """
5840ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        is_using_test_images = test_conf.get('image_type') != 'mp'
5850ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        omaha_host = test_conf.get('omaha_host')
5860ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
587d70e0c8bd47ea824c5a84c5565a46690d64e4aa6Gilad Arnold        # Check whether the DUT is in dev mode.
588d70e0c8bd47ea824c5a84c5565a46690d64e4aa6Gilad Arnold        is_dev_mode = host.servo.get('dev_mode') == 'on'
589d70e0c8bd47ea824c5a84c5565a46690d64e4aa6Gilad Arnold
5900ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        # Stage source images and update payloads on lorry/devserver. We use
5910ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        # the payload URI as argument for the lab's devserver load-balancing
5920ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        # mechanism.
5930ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        lorry_devserver = dev_server.ImageServer.resolve(
5940ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold                test_conf['target_payload_uri'])
5950ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        logging.info('staging image and payload on lorry/devserver (%s)',
5960ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold                     lorry_devserver.url())
5970ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        test_conf['source_image_lorry_url'] = self.stage_image(
5980ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold                lorry_devserver, test_conf['source_image_uri'],
5990ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold                test_conf['board'], test_conf['source_release'],
6000ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold                test_conf['source_branch'], is_using_test_images)
6010ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        test_conf['target_payload_lorry_url'] = self.stage_payload(
6020ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold                lorry_devserver, test_conf['target_payload_uri'],
6030ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold                test_conf['board'], test_conf['target_release'],
6040ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold                test_conf['target_branch'], is_using_test_images,
6050ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold                test_conf['update_type'] == 'delta',
6060ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold                test_conf['target_release'] == test_conf['source_release'])
6070ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
60803901089c0db27508a6b2ff61ae8b75eba77cf37Gilad Arnold        # Get the devserver directory from autotest config.
60903901089c0db27508a6b2ff61ae8b75eba77cf37Gilad Arnold        devserver_dir = global_config.global_config.get_config_value(
61003901089c0db27508a6b2ff61ae8b75eba77cf37Gilad Arnold                'CROS', 'devserver_dir', default=None)
61103901089c0db27508a6b2ff61ae8b75eba77cf37Gilad Arnold        if devserver_dir is None:
61203901089c0db27508a6b2ff61ae8b75eba77cf37Gilad Arnold            raise error.TestError(
61303901089c0db27508a6b2ff61ae8b75eba77cf37Gilad Arnold                    'path to devserver source tree not provided; please define '
61403901089c0db27508a6b2ff61ae8b75eba77cf37Gilad Arnold                    'devserver_dir under [CROS] in your shadow_config.ini')
61503901089c0db27508a6b2ff61ae8b75eba77cf37Gilad Arnold
6160ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        # Launch Omaha/devserver.
6170ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        try:
6180ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold            self._omaha_devserver = OmahaDevserver(
61903901089c0db27508a6b2ff61ae8b75eba77cf37Gilad Arnold                    omaha_host, devserver_dir, host.ip,
6200ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold                    test_conf.get('target_payload_lorry_url'))
6210ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        except error.TestError, e:
62203901089c0db27508a6b2ff61ae8b75eba77cf37Gilad Arnold            logging.error('failed to start omaha/devserver: %s', e)
6230ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold            raise
6240ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
6250ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        try:
6260ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold            # Install source image (test vs MP).
6270ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold            if is_using_test_images:
6280ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold                self._install_test_image(
6290ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold                        host, test_conf['source_image_lorry_url'],
6300ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold                        is_dev_mode)
6310ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold            else:
6320ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold                self._install_mp_image(test_conf['source_image_lorry_url'],
6330ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold                                       is_dev_mode)
6340ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
6350ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold            omaha_netloc = self._omaha_devserver.get_netloc()
6360ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
6370ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold            # Trigger an update (test vs MP).
6380ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold            if is_using_test_images:
6390ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold                self._trigger_test_update(host, omaha_netloc)
6400ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold            else:
6410ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold                # TODO(garnold) chromium-os:33766: use GPIOs to trigger an
6420ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold                # update.
6430ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold                pass
6440ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
6450ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold            # Track update progress.
6460ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold            omaha_hostlog_url = urlparse.urlunsplit(
6470ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold                    ['http', omaha_netloc, '/api/hostlog', 'ip=' + host.ip, ''])
6480ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold            logging.info('polling update progress from omaha/devserver: %s',
6490ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold                         omaha_hostlog_url)
65003901089c0db27508a6b2ff61ae8b75eba77cf37Gilad Arnold            log_verifier = UpdateEventLogVerifier(
65103901089c0db27508a6b2ff61ae8b75eba77cf37Gilad Arnold                    omaha_hostlog_url,
65203901089c0db27508a6b2ff61ae8b75eba77cf37Gilad Arnold                    self._DEVSERVER_HOSTLOG_REQUEST_TIMEOUT_SECONDS)
6530ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
6540ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold            # Verify chain of events in a successful update process.
6550ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold            chain = ExpectedUpdateEventChain(
6560ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold                    (self._WAIT_FOR_INITIAL_UPDATE_CHECK_SECONDS,
6570ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold                     ExpectedUpdateEvent(
6580ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold                         version=test_conf['source_release'])),
6590ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold                    (self._WAIT_FOR_DOWNLOAD_STARTED_SECONDS,
6600ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold                     ExpectedUpdateEvent(
6610ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold                         event_type=self.EVENT_TYPE_DOWNLOAD_STARTED,
6620ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold                         event_result=self.EVENT_RESULT_SUCCESS,
6630ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold                         version=test_conf['source_release'])),
6640ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold                    (self._WAIT_FOR_DOWNLOAD_COMPLETED_SECONDS,
6650ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold                     ExpectedUpdateEvent(
6660ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold                         event_type=self.EVENT_TYPE_DOWNLOAD_FINISHED,
6670ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold                         event_result=self.EVENT_RESULT_SUCCESS,
6680ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold                         version=test_conf['source_release'])),
6690ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold                    (self._WAIT_FOR_UPDATE_COMPLETED_SECONDS,
6700ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold                     ExpectedUpdateEvent(
6710ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold                         event_type=self.EVENT_TYPE_UPDATE_COMPLETE,
6720ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold                         event_result=self.EVENT_RESULT_SUCCESS,
6730ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold                         version=test_conf['source_release'])))
6740ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold            if not log_verifier.verify_expected_event_chain(chain):
6750ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold                raise error.TestFail(
6760ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold                        'could not verify that update was successful')
6770ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
6780ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold            # Wait after an update completion (safety margin).
6790ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold            _wait(self._WAIT_AFTER_UPDATE_SECONDS, 'after update completion')
6800ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
6810ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold            # Reboot the DUT after the update.
6820ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold            self._servo_dut_reboot(host, is_dev_mode, is_using_test_images)
6830ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
6840ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold            # Trigger a second update check (again, test vs MP).
6850ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold            if is_using_test_images:
6860ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold                self._trigger_test_update(host, omaha_netloc)
6870ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold            else:
6880ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold                # TODO(garnold) chromium-os:33766: use GPIOs to trigger an
6890ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold                # update.
6900ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold                pass
6910ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
6920ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold            # Observe post-reboot update check, which should indicate that the
6930ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold            # image version has been updated.  Note that the previous version
6940ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold            # is currently not reported by AU, as one may have expected; had it
6950ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold            # been reported, we should have included
6960ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold            # expect_previous_version=test_conf['source_release'] as well.
6970ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold            chain = ExpectedUpdateEventChain(
6980ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold                    (self._WAIT_FOR_UPDATE_CHECK_AFTER_REBOOT_SECONDS,
6990ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold                     ExpectedUpdateEvent(
7000ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold                         event_type=self.EVENT_TYPE_UPDATE_COMPLETE,
7010ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold                         event_result=self.EVENT_RESULT_SUCCESS_REBOOT,
7020ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold                         version=test_conf['target_release'])))
7030ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold            if not log_verifier.verify_expected_event_chain(chain):
7040ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold                raise error.TestFail('could not verify that machine rebooted '
7050ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold                                     'after update')
7060ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
7070ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        except error.TestFail:
7080ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold            raise
7090ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        except Exception, e:
7100ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold            # Convert any other exception into a test failure.
7110ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold            raise error.TestFail(str(e))
7120ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
7130ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        finally:
7140ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold            self._omaha_devserver.kill()
7150ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
716