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