autoupdate_EndToEndTest.py revision 3563042207623fbb9db02018156e8b5fa0e4c2c3
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
178ddeb9c8d6df7eb2e033651e39f434ebdf8af3ccSimran Basifrom 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
208cfa14a695654bc9cbfe9b9ce5868d669d0c57e0fGilad Arnold    @staticmethod
209cfa14a695654bc9cbfe9b9ce5868d669d0c57e0fGilad Arnold    def _format_event_with_timeout(timeout, expected_event):
210cfa14a695654bc9cbfe9b9ce5868d669d0c57e0fGilad Arnold        """Returns a string representation of the event, with timeout."""
2110ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        return ('%s %s' %
2120ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold                (expected_event,
2130ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold                 ('within %s seconds' % timeout) if timeout
2140ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold                 else 'indefinitely'))
2150ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
2160ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
2170ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold    def __str__(self):
2180ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        return ('[%s]' %
2190ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold                ', '.join(
2200ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold                    [self._format_event_with_timeout(timeout, expected_event)
2210ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold                     for timeout, expected_event
2220ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold                     in self._expected_event_chain]))
2230ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
2240ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
2250ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold    def __repr__(self):
2260ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        return str(self._expected_event_chain)
2270ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
2280ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
2290ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold    def verify(self, get_next_event):
2300ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        """Verifies that an actual stream of events complies.
2310ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
2320ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        @param get_next_event: a function returning the next event
2330ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
234ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa        @raises ExpectedUpdateEventChainFailed if we failed to verify an event.
2350ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
2360ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        """
2370ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        for timeout, expected_event in self._expected_event_chain:
2380338ff3b754d52b41ca2d0432164a757daa1112dGilad Arnold            logging.info('Expecting %s',
239ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa                         self._format_event_with_timeout(timeout,
240ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa                                                         expected_event))
2410ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold            if not self._verify_event_with_timeout(
2420ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold                    timeout, expected_event, get_next_event):
243ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa                logging.error('Failed expected event: %s',
244ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa                              expected_event.error_message)
245ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa                raise ExpectedUpdateEventChainFailed(
246ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa                        expected_event.error_message)
2470ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
2480ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
249cfa14a695654bc9cbfe9b9ce5868d669d0c57e0fGilad Arnold    @staticmethod
250cfa14a695654bc9cbfe9b9ce5868d669d0c57e0fGilad Arnold    def _verify_event_with_timeout(timeout, expected_event, get_next_event):
2510ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        """Verify an expected event occurs within a given timeout.
2520ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
2530ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        @param timeout: specified in seconds
2540ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        @param expected_event: an expected event specification
2550ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        @param get_next_event: function returning the next event in a stream
2560ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
2570ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        @return True if event complies, False otherwise.
2580ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
2590ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        """
2600ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        base_timestamp = curr_timestamp = time.time()
2610ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        expired_timestamp = base_timestamp + timeout
2620ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        while curr_timestamp <= expired_timestamp:
2630ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold            new_event = get_next_event()
2640ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold            if new_event:
2650338ff3b754d52b41ca2d0432164a757daa1112dGilad Arnold                logging.info('Event received after %s seconds',
2660338ff3b754d52b41ca2d0432164a757daa1112dGilad Arnold                             round(curr_timestamp - base_timestamp, 1))
2670ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold                return expected_event.verify(new_event)
2680ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
2690ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold            # No new events, sleep for one second only (so we don't miss
2700ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold            # events at the end of the allotted timeout).
2710ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold            time.sleep(1)
2720ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold            curr_timestamp = time.time()
2730ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
2740338ff3b754d52b41ca2d0432164a757daa1112dGilad Arnold        logging.error('Timeout expired')
2750ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        return False
2760ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
2770ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
2780ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnoldclass UpdateEventLogVerifier(object):
2790ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold    """Verifies update event chains on a devserver update log."""
28003901089c0db27508a6b2ff61ae8b75eba77cf37Gilad Arnold    def __init__(self, event_log_url, url_request_timeout=None):
2810ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        self._event_log_url = event_log_url
28203901089c0db27508a6b2ff61ae8b75eba77cf37Gilad Arnold        self._url_request_timeout = url_request_timeout
2830ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        self._event_log = []
2840ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        self._num_consumed_events = 0
2850ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
2860ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
2870ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold    def verify_expected_event_chain(self, expected_event_chain):
288ae6acd9b3e1c6f56310cdb4e05737122b7b2818cChris Sosa        """Verify a given event chain.
289ae6acd9b3e1c6f56310cdb4e05737122b7b2818cChris Sosa
290ae6acd9b3e1c6f56310cdb4e05737122b7b2818cChris Sosa        @param expected_event_chain: instance of expected event chain.
291ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa
292ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa        @raises ExpectedUpdateEventChainFailed if we failed to verify the an
293ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa                event.
294ae6acd9b3e1c6f56310cdb4e05737122b7b2818cChris Sosa        """
295ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa        expected_event_chain.verify(self._get_next_log_event)
2960ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
2970ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
2980ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold    def _get_next_log_event(self):
2990ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        """Returns the next event in an event log.
3000ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
3010ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        Uses the URL handed to it during initialization to obtain the host log
3020ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        from a devserver. If new events are encountered, the first of them is
3030ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        consumed and returned.
3040ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
3050ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        @return The next new event in the host log, as reported by devserver;
30603901089c0db27508a6b2ff61ae8b75eba77cf37Gilad Arnold                None if no such event was found or an error occurred.
3070ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
3080ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        """
3090ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        # (Re)read event log from devserver, if necessary.
3100ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        if len(self._event_log) <= self._num_consumed_events:
31103901089c0db27508a6b2ff61ae8b75eba77cf37Gilad Arnold            try:
31203901089c0db27508a6b2ff61ae8b75eba77cf37Gilad Arnold                if self._url_request_timeout:
31303901089c0db27508a6b2ff61ae8b75eba77cf37Gilad Arnold                    conn = urllib2.urlopen(self._event_log_url,
31403901089c0db27508a6b2ff61ae8b75eba77cf37Gilad Arnold                                           timeout=self._url_request_timeout)
31503901089c0db27508a6b2ff61ae8b75eba77cf37Gilad Arnold                else:
31603901089c0db27508a6b2ff61ae8b75eba77cf37Gilad Arnold                    conn = urllib2.urlopen(self._event_log_url)
31703901089c0db27508a6b2ff61ae8b75eba77cf37Gilad Arnold            except urllib2.URLError, e:
3180338ff3b754d52b41ca2d0432164a757daa1112dGilad Arnold                logging.warning('Failed to read event log url: %s', e)
31903901089c0db27508a6b2ff61ae8b75eba77cf37Gilad Arnold                return None
320a0261611f9be78ee9d456fd50bea81e9eda07c17Gilad Arnold            except socket.timeout, e:
321a0261611f9be78ee9d456fd50bea81e9eda07c17Gilad Arnold                logging.warning('Timed out reading event log url: %s', e)
322a0261611f9be78ee9d456fd50bea81e9eda07c17Gilad Arnold                return None
32303901089c0db27508a6b2ff61ae8b75eba77cf37Gilad Arnold
3240ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold            event_log_resp = conn.read()
3250ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold            conn.close()
3260ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold            self._event_log = json.loads(event_log_resp)
3270ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
32803901089c0db27508a6b2ff61ae8b75eba77cf37Gilad Arnold        # Return next new event, if one is found.
3290ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        if len(self._event_log) > self._num_consumed_events:
3300ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold            new_event = self._event_log[self._num_consumed_events]
3310ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold            self._num_consumed_events += 1
3320338ff3b754d52b41ca2d0432164a757daa1112dGilad Arnold            logging.info('Consumed new event: %s', new_event)
3330ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold            return new_event
3340ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
3350ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
336793344b359304c31d78197788b55cfbbe2636025Chris Sosaclass OmahaDevserverFailedToStart(error.TestError):
337793344b359304c31d78197788b55cfbbe2636025Chris Sosa    """Raised when a omaha devserver fails to start."""
338793344b359304c31d78197788b55cfbbe2636025Chris Sosa
339793344b359304c31d78197788b55cfbbe2636025Chris Sosa
3400ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnoldclass OmahaDevserver(object):
3410ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold    """Spawns a test-private devserver instance."""
342793344b359304c31d78197788b55cfbbe2636025Chris Sosa    # How long to wait for a devserver to start.
3430ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold    _WAIT_FOR_DEVSERVER_STARTED_SECONDS = 15
344793344b359304c31d78197788b55cfbbe2636025Chris Sosa
345260957d6079ee23ec6c9857a95fa183a78741b0bGilad Arnold    # How long to sleep (seconds) between checks to see if a devserver is up.
34603901089c0db27508a6b2ff61ae8b75eba77cf37Gilad Arnold    _WAIT_SLEEP_INTERVAL = 1
3470ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
3486f36e954e1d73e961b8d47ebf4ec7d267c4307f4Gilad Arnold    # Max devserver execution time (seconds); used with timeout(1) to ensure we
3496f36e954e1d73e961b8d47ebf4ec7d267c4307f4Gilad Arnold    # don't have defunct instances hogging the system.
3506f36e954e1d73e961b8d47ebf4ec7d267c4307f4Gilad Arnold    _DEVSERVER_TIMELIMIT_SECONDS = 12 * 60 * 60
351793344b359304c31d78197788b55cfbbe2636025Chris Sosa
352793344b359304c31d78197788b55cfbbe2636025Chris Sosa
353260957d6079ee23ec6c9857a95fa183a78741b0bGilad Arnold    def __init__(self, omaha_host, devserver_dir, update_payload_staged_url):
3540ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        """Starts a private devserver instance, operating at Omaha capacity.
3550ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
3560ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        @param omaha_host: host address where the devserver is spawned.
35703901089c0db27508a6b2ff61ae8b75eba77cf37Gilad Arnold        @param devserver_dir: path to the devserver source directory
358ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa        @param update_payload_staged_url: URL to provision for update requests.
3590ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
3600ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        """
361ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa        if not update_payload_staged_url:
3620338ff3b754d52b41ca2d0432164a757daa1112dGilad Arnold            raise error.TestError('Missing update payload url')
3630ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
3646c55bdb98e967675456a71a0971b81058536cac8Chris Sosa        self._omaha_host = omaha_host
365260957d6079ee23ec6c9857a95fa183a78741b0bGilad Arnold        self._devserver_pid = 0
366260957d6079ee23ec6c9857a95fa183a78741b0bGilad Arnold        self._devserver_port = 0  # Determined later from devserver portfile.
3676c55bdb98e967675456a71a0971b81058536cac8Chris Sosa        self._devserver_dir = devserver_dir
368ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa        self._update_payload_staged_url = update_payload_staged_url
3696c55bdb98e967675456a71a0971b81058536cac8Chris Sosa
3706c55bdb98e967675456a71a0971b81058536cac8Chris Sosa        self._devserver_ssh = hosts.SSHHost(self._omaha_host,
3716c55bdb98e967675456a71a0971b81058536cac8Chris Sosa                                            user=os.environ['USER'])
372260957d6079ee23ec6c9857a95fa183a78741b0bGilad Arnold
3733563042207623fbb9db02018156e8b5fa0e4c2c3Chris Sosa        # Temporary files for various devserver outputs.
3743563042207623fbb9db02018156e8b5fa0e4c2c3Chris Sosa        self._devserver_logfile = None
3753563042207623fbb9db02018156e8b5fa0e4c2c3Chris Sosa        self._devserver_portfile = None
3763563042207623fbb9db02018156e8b5fa0e4c2c3Chris Sosa        self._devserver_pidfile = None
3773563042207623fbb9db02018156e8b5fa0e4c2c3Chris Sosa        self._devserver_static_dir = None
3783563042207623fbb9db02018156e8b5fa0e4c2c3Chris Sosa
3793563042207623fbb9db02018156e8b5fa0e4c2c3Chris Sosa
3803563042207623fbb9db02018156e8b5fa0e4c2c3Chris Sosa    def _cleanup_devserver_files(self):
3813563042207623fbb9db02018156e8b5fa0e4c2c3Chris Sosa        """Cleans up the temporary devserver files."""
3823563042207623fbb9db02018156e8b5fa0e4c2c3Chris Sosa        for filename in (self._devserver_logfile, self._devserver_portfile,
3833563042207623fbb9db02018156e8b5fa0e4c2c3Chris Sosa                         self._devserver_pidfile):
3843563042207623fbb9db02018156e8b5fa0e4c2c3Chris Sosa           if filename:
3853563042207623fbb9db02018156e8b5fa0e4c2c3Chris Sosa              self._devserver_ssh.run('rm -f %s' % filename, ignore_status=True)
3863563042207623fbb9db02018156e8b5fa0e4c2c3Chris Sosa
3873563042207623fbb9db02018156e8b5fa0e4c2c3Chris Sosa        if self._devserver_static_dir:
3883563042207623fbb9db02018156e8b5fa0e4c2c3Chris Sosa            self._devserver_ssh.run('rm -rf %s' % self._devserver_static_dir,
3893563042207623fbb9db02018156e8b5fa0e4c2c3Chris Sosa                                    ignore_status=True)
3903563042207623fbb9db02018156e8b5fa0e4c2c3Chris Sosa
391260957d6079ee23ec6c9857a95fa183a78741b0bGilad Arnold
3923563042207623fbb9db02018156e8b5fa0e4c2c3Chris Sosa    def _create_tempfile_on_devserver(self, label, dir=False):
3933563042207623fbb9db02018156e8b5fa0e4c2c3Chris Sosa        """Creates a temporary file/dir on the devserver and returns its path.
394260957d6079ee23ec6c9857a95fa183a78741b0bGilad Arnold
395260957d6079ee23ec6c9857a95fa183a78741b0bGilad Arnold        @param label: Identifier for the file context (string, no whitespaces).
3963563042207623fbb9db02018156e8b5fa0e4c2c3Chris Sosa        @param dir: If True, create a directory instead of a file.
397260957d6079ee23ec6c9857a95fa183a78741b0bGilad Arnold
398260957d6079ee23ec6c9857a95fa183a78741b0bGilad Arnold        @raises test.TestError: If we failed to invoke mktemp on the server.
399260957d6079ee23ec6c9857a95fa183a78741b0bGilad Arnold        @raises OmahaDevserverFailedToStart: If tempfile creation failed.
400260957d6079ee23ec6c9857a95fa183a78741b0bGilad Arnold        """
401260957d6079ee23ec6c9857a95fa183a78741b0bGilad Arnold        remote_cmd = 'mktemp --tmpdir devserver-%s.XXXXXX' % label
4023563042207623fbb9db02018156e8b5fa0e4c2c3Chris Sosa        if dir:
4033563042207623fbb9db02018156e8b5fa0e4c2c3Chris Sosa            remote_cmd += ' --directory'
4043563042207623fbb9db02018156e8b5fa0e4c2c3Chris Sosa
405260957d6079ee23ec6c9857a95fa183a78741b0bGilad Arnold        try:
406260957d6079ee23ec6c9857a95fa183a78741b0bGilad Arnold            result = self._devserver_ssh.run(remote_cmd, ignore_status=True)
407260957d6079ee23ec6c9857a95fa183a78741b0bGilad Arnold        except error.AutoservRunError as e:
408260957d6079ee23ec6c9857a95fa183a78741b0bGilad Arnold            self._log_and_raise_remote_ssh_error(e)
409260957d6079ee23ec6c9857a95fa183a78741b0bGilad Arnold        if result.exit_status != 0:
410260957d6079ee23ec6c9857a95fa183a78741b0bGilad Arnold            raise OmahaDevserverFailedToStart(
411260957d6079ee23ec6c9857a95fa183a78741b0bGilad Arnold                    'Could not create a temporary %s file on the devserver, '
412260957d6079ee23ec6c9857a95fa183a78741b0bGilad Arnold                    'error output:\n%s' % (label, result.stderr))
413260957d6079ee23ec6c9857a95fa183a78741b0bGilad Arnold        return result.stdout.strip()
414260957d6079ee23ec6c9857a95fa183a78741b0bGilad Arnold
415260957d6079ee23ec6c9857a95fa183a78741b0bGilad Arnold    @staticmethod
416260957d6079ee23ec6c9857a95fa183a78741b0bGilad Arnold    def _log_and_raise_remote_ssh_error(e):
417260957d6079ee23ec6c9857a95fa183a78741b0bGilad Arnold        """Logs failure to ssh remote, then raises a TestError."""
418260957d6079ee23ec6c9857a95fa183a78741b0bGilad Arnold        logging.debug('Failed to ssh into the devserver: %s', e)
419260957d6079ee23ec6c9857a95fa183a78741b0bGilad Arnold        logging.error('If you are running this locally it means you did not '
420260957d6079ee23ec6c9857a95fa183a78741b0bGilad Arnold                      'configure ssh correctly.')
421260957d6079ee23ec6c9857a95fa183a78741b0bGilad Arnold        raise error.TestError('Failed to ssh into the devserver: %s' % e)
422260957d6079ee23ec6c9857a95fa183a78741b0bGilad Arnold
423260957d6079ee23ec6c9857a95fa183a78741b0bGilad Arnold
424260957d6079ee23ec6c9857a95fa183a78741b0bGilad Arnold    def _read_int_from_devserver_file(self, filename):
425260957d6079ee23ec6c9857a95fa183a78741b0bGilad Arnold        """Reads and returns an integer value from a file on the devserver."""
426260957d6079ee23ec6c9857a95fa183a78741b0bGilad Arnold        return int(self._get_devserver_file_content(filename).strip())
4270ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
4286c55bdb98e967675456a71a0971b81058536cac8Chris Sosa
429793344b359304c31d78197788b55cfbbe2636025Chris Sosa    def _wait_for_devserver_to_start(self):
430793344b359304c31d78197788b55cfbbe2636025Chris Sosa        """Waits until the devserver starts within the time limit.
431793344b359304c31d78197788b55cfbbe2636025Chris Sosa
432260957d6079ee23ec6c9857a95fa183a78741b0bGilad Arnold        Infers and sets the devserver PID and serving port.
433260957d6079ee23ec6c9857a95fa183a78741b0bGilad Arnold
434793344b359304c31d78197788b55cfbbe2636025Chris Sosa        Raises:
435793344b359304c31d78197788b55cfbbe2636025Chris Sosa            OmahaDevserverFailedToStart: If the time limit is reached and we
436793344b359304c31d78197788b55cfbbe2636025Chris Sosa                                         cannot connect to the devserver.
437793344b359304c31d78197788b55cfbbe2636025Chris Sosa        """
438260957d6079ee23ec6c9857a95fa183a78741b0bGilad Arnold        # Compute the overall timeout.
439260957d6079ee23ec6c9857a95fa183a78741b0bGilad Arnold        deadline = time.time() + self._WAIT_FOR_DEVSERVER_STARTED_SECONDS
440260957d6079ee23ec6c9857a95fa183a78741b0bGilad Arnold
441260957d6079ee23ec6c9857a95fa183a78741b0bGilad Arnold        # First, wait for port file to be filled and determine the server port.
44294b9ad4983cc735e162a3549c1628d08241b0d57Gilad Arnold        logging.warning('Waiting for devserver to start up.')
443260957d6079ee23ec6c9857a95fa183a78741b0bGilad Arnold        while time.time() < deadline:
444260957d6079ee23ec6c9857a95fa183a78741b0bGilad Arnold            try:
445260957d6079ee23ec6c9857a95fa183a78741b0bGilad Arnold                self._devserver_pid = self._read_int_from_devserver_file(
446260957d6079ee23ec6c9857a95fa183a78741b0bGilad Arnold                        self._devserver_pidfile)
447260957d6079ee23ec6c9857a95fa183a78741b0bGilad Arnold                self._devserver_port = self._read_int_from_devserver_file(
448260957d6079ee23ec6c9857a95fa183a78741b0bGilad Arnold                        self._devserver_portfile)
449260957d6079ee23ec6c9857a95fa183a78741b0bGilad Arnold                logging.info('Devserver pid is %d, serving on port %d',
450260957d6079ee23ec6c9857a95fa183a78741b0bGilad Arnold                             self._devserver_pid, self._devserver_port)
451260957d6079ee23ec6c9857a95fa183a78741b0bGilad Arnold                break
452260957d6079ee23ec6c9857a95fa183a78741b0bGilad Arnold            except Exception:  # Couldn't read file or corrupt content.
453260957d6079ee23ec6c9857a95fa183a78741b0bGilad Arnold                time.sleep(self._WAIT_SLEEP_INTERVAL)
454260957d6079ee23ec6c9857a95fa183a78741b0bGilad Arnold        else:
45594b9ad4983cc735e162a3549c1628d08241b0d57Gilad Arnold            raise OmahaDevserverFailedToStart(
456260957d6079ee23ec6c9857a95fa183a78741b0bGilad Arnold                    'The test failed to find the pid/port of the omaha '
457260957d6079ee23ec6c9857a95fa183a78741b0bGilad Arnold                    'devserver. Check the dumped devserver logs for more '
458260957d6079ee23ec6c9857a95fa183a78741b0bGilad Arnold                    'information.')
459260957d6079ee23ec6c9857a95fa183a78741b0bGilad Arnold
460260957d6079ee23ec6c9857a95fa183a78741b0bGilad Arnold        # Check that the server is reponsding to network requests.
461260957d6079ee23ec6c9857a95fa183a78741b0bGilad Arnold        logging.warning('Waiting for devserver to accept network requests.')
462260957d6079ee23ec6c9857a95fa183a78741b0bGilad Arnold        url = 'http://%s' % self.get_netloc()
463260957d6079ee23ec6c9857a95fa183a78741b0bGilad Arnold        while time.time() < deadline:
464260957d6079ee23ec6c9857a95fa183a78741b0bGilad Arnold            if dev_server.DevServer.devserver_healthy(url, timeout_min=0.1):
465260957d6079ee23ec6c9857a95fa183a78741b0bGilad Arnold                break
466793344b359304c31d78197788b55cfbbe2636025Chris Sosa
467793344b359304c31d78197788b55cfbbe2636025Chris Sosa            # TODO(milleral): Refactor once crbug.com/221626 is resolved.
468793344b359304c31d78197788b55cfbbe2636025Chris Sosa            time.sleep(self._WAIT_SLEEP_INTERVAL)
469793344b359304c31d78197788b55cfbbe2636025Chris Sosa        else:
470793344b359304c31d78197788b55cfbbe2636025Chris Sosa            raise OmahaDevserverFailedToStart(
471793344b359304c31d78197788b55cfbbe2636025Chris Sosa                    'The test failed to establish a connection to the omaha '
472793344b359304c31d78197788b55cfbbe2636025Chris Sosa                    'devserver it set up on port %d. Check the dumped '
473260957d6079ee23ec6c9857a95fa183a78741b0bGilad Arnold                    'devserver logs for more information.' %
474260957d6079ee23ec6c9857a95fa183a78741b0bGilad Arnold                    self._devserver_port)
475793344b359304c31d78197788b55cfbbe2636025Chris Sosa
476793344b359304c31d78197788b55cfbbe2636025Chris Sosa
4776c55bdb98e967675456a71a0971b81058536cac8Chris Sosa    def start_devserver(self):
478793344b359304c31d78197788b55cfbbe2636025Chris Sosa        """Starts the devserver and confirms it is up.
479793344b359304c31d78197788b55cfbbe2636025Chris Sosa
480793344b359304c31d78197788b55cfbbe2636025Chris Sosa        Raises:
481260957d6079ee23ec6c9857a95fa183a78741b0bGilad Arnold            test.TestError: If we failed to spawn the remote devserver.
482793344b359304c31d78197788b55cfbbe2636025Chris Sosa            OmahaDevserverFailedToStart: If the time limit is reached and we
483793344b359304c31d78197788b55cfbbe2636025Chris Sosa                                         cannot connect to the devserver.
4846c55bdb98e967675456a71a0971b81058536cac8Chris Sosa        """
4852f1ae9f1f2464ebbeb226a6669fa90ef211a179cChris Sosa        update_payload_url_base, update_payload_path = self._split_url(
486ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa                self._update_payload_staged_url)
4873563042207623fbb9db02018156e8b5fa0e4c2c3Chris Sosa
4883563042207623fbb9db02018156e8b5fa0e4c2c3Chris Sosa        # Allocate temporary files for various server outputs.
4893563042207623fbb9db02018156e8b5fa0e4c2c3Chris Sosa        self._devserver_logfile = self._create_tempfile_on_devserver('log')
4903563042207623fbb9db02018156e8b5fa0e4c2c3Chris Sosa        self._devserver_portfile = self._create_tempfile_on_devserver('port')
4913563042207623fbb9db02018156e8b5fa0e4c2c3Chris Sosa        self._devserver_pidfile = self._create_tempfile_on_devserver('pid')
4923563042207623fbb9db02018156e8b5fa0e4c2c3Chris Sosa        self._devserver_static_dir = self._create_tempfile_on_devserver(
4933563042207623fbb9db02018156e8b5fa0e4c2c3Chris Sosa                'static', dir=True)
4943563042207623fbb9db02018156e8b5fa0e4c2c3Chris Sosa
4956f36e954e1d73e961b8d47ebf4ec7d267c4307f4Gilad Arnold        # Invoke the Omaha/devserver on the remote server. Will attempt to kill
4966f36e954e1d73e961b8d47ebf4ec7d267c4307f4Gilad Arnold        # it with a SIGTERM after a predetermined timeout has elapsed, followed
4976f36e954e1d73e961b8d47ebf4ec7d267c4307f4Gilad Arnold        # by SIGKILL if not dead within 30 seconds from the former signal.
4980ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        cmdlist = [
4996f36e954e1d73e961b8d47ebf4ec7d267c4307f4Gilad Arnold                'timeout', '-s', 'TERM', '-k', '30',
5006f36e954e1d73e961b8d47ebf4ec7d267c4307f4Gilad Arnold                str(self._DEVSERVER_TIMELIMIT_SECONDS),
5016c55bdb98e967675456a71a0971b81058536cac8Chris Sosa                '%s/devserver.py' % self._devserver_dir,
5020ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold                '--payload=%s' % update_payload_path,
503260957d6079ee23ec6c9857a95fa183a78741b0bGilad Arnold                '--port=0',
504260957d6079ee23ec6c9857a95fa183a78741b0bGilad Arnold                '--pidfile=%s' % self._devserver_pidfile,
505260957d6079ee23ec6c9857a95fa183a78741b0bGilad Arnold                '--portfile=%s' % self._devserver_portfile,
506260957d6079ee23ec6c9857a95fa183a78741b0bGilad Arnold                '--logfile=%s' % self._devserver_logfile,
5070ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold                '--remote_payload',
5080ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold                '--urlbase=%s' % update_payload_url_base,
5090ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold                '--max_updates=1',
5100ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold                '--host_log',
5113563042207623fbb9db02018156e8b5fa0e4c2c3Chris Sosa                '--static_dir=%s' % self._devserver_static_dir,
5120ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        ]
513260957d6079ee23ec6c9857a95fa183a78741b0bGilad Arnold        remote_cmd = '( %s ) </dev/null >/dev/null 2>&1 &' % ' '.join(cmdlist)
5146c55bdb98e967675456a71a0971b81058536cac8Chris Sosa
515260957d6079ee23ec6c9857a95fa183a78741b0bGilad Arnold        logging.info('Starting devserver with %r', remote_cmd)
516260957d6079ee23ec6c9857a95fa183a78741b0bGilad Arnold        try:
517260957d6079ee23ec6c9857a95fa183a78741b0bGilad Arnold            self._devserver_ssh.run_output(remote_cmd)
518260957d6079ee23ec6c9857a95fa183a78741b0bGilad Arnold        except error.AutoservRunError as e:
519260957d6079ee23ec6c9857a95fa183a78741b0bGilad Arnold            self._log_and_raise_remote_ssh_error(e)
5206c55bdb98e967675456a71a0971b81058536cac8Chris Sosa
521260957d6079ee23ec6c9857a95fa183a78741b0bGilad Arnold        try:
522260957d6079ee23ec6c9857a95fa183a78741b0bGilad Arnold            self._wait_for_devserver_to_start()
523260957d6079ee23ec6c9857a95fa183a78741b0bGilad Arnold        except OmahaDevserverFailedToStart:
524260957d6079ee23ec6c9857a95fa183a78741b0bGilad Arnold            self._kill_remote_process()
525260957d6079ee23ec6c9857a95fa183a78741b0bGilad Arnold            self._dump_devserver_log()
5263563042207623fbb9db02018156e8b5fa0e4c2c3Chris Sosa            self._cleanup_devserver_file()
527260957d6079ee23ec6c9857a95fa183a78741b0bGilad Arnold            raise
52838ba6b79b19b5bdd9cfe71b26efd0c267768527aGilad Arnold
5296c55bdb98e967675456a71a0971b81058536cac8Chris Sosa
530260957d6079ee23ec6c9857a95fa183a78741b0bGilad Arnold    def _kill_remote_process(self):
531260957d6079ee23ec6c9857a95fa183a78741b0bGilad Arnold        """Kills the devserver and verifies it's down; clears the remote pid."""
532260957d6079ee23ec6c9857a95fa183a78741b0bGilad Arnold        def devserver_down():
533cfa14a695654bc9cbfe9b9ce5868d669d0c57e0fGilad Arnold            """Ensure that the devserver process is down."""
534260957d6079ee23ec6c9857a95fa183a78741b0bGilad Arnold            return not self._remote_process_alive()
5356c55bdb98e967675456a71a0971b81058536cac8Chris Sosa
536260957d6079ee23ec6c9857a95fa183a78741b0bGilad Arnold        if devserver_down():
5376c55bdb98e967675456a71a0971b81058536cac8Chris Sosa            return
5386c55bdb98e967675456a71a0971b81058536cac8Chris Sosa
539260957d6079ee23ec6c9857a95fa183a78741b0bGilad Arnold        for signal in 'SIGTERM', 'SIGKILL':
540260957d6079ee23ec6c9857a95fa183a78741b0bGilad Arnold            remote_cmd = 'kill -s %s %s' % (signal, self._devserver_pid)
541260957d6079ee23ec6c9857a95fa183a78741b0bGilad Arnold            self._devserver_ssh.run(remote_cmd)
542260957d6079ee23ec6c9857a95fa183a78741b0bGilad Arnold            try:
543260957d6079ee23ec6c9857a95fa183a78741b0bGilad Arnold                client_utils.poll_for_condition(
544260957d6079ee23ec6c9857a95fa183a78741b0bGilad Arnold                        devserver_down, sleep_interval=1, desc='devserver down')
545260957d6079ee23ec6c9857a95fa183a78741b0bGilad Arnold                break
546260957d6079ee23ec6c9857a95fa183a78741b0bGilad Arnold            except client_utils.TimeoutError:
547260957d6079ee23ec6c9857a95fa183a78741b0bGilad Arnold                logging.warning('Could not kill devserver with %s.', signal)
548260957d6079ee23ec6c9857a95fa183a78741b0bGilad Arnold        else:
549260957d6079ee23ec6c9857a95fa183a78741b0bGilad Arnold            logging.warning('Failed to kill devserver, giving up.')
550260957d6079ee23ec6c9857a95fa183a78741b0bGilad Arnold
551260957d6079ee23ec6c9857a95fa183a78741b0bGilad Arnold        self._devserver_pid = None
5526c55bdb98e967675456a71a0971b81058536cac8Chris Sosa
5536c55bdb98e967675456a71a0971b81058536cac8Chris Sosa
554260957d6079ee23ec6c9857a95fa183a78741b0bGilad Arnold    def _remote_process_alive(self):
555260957d6079ee23ec6c9857a95fa183a78741b0bGilad Arnold        """Tests whether the remote devserver process is running."""
556260957d6079ee23ec6c9857a95fa183a78741b0bGilad Arnold        if not self._devserver_pid:
557260957d6079ee23ec6c9857a95fa183a78741b0bGilad Arnold            return False
558260957d6079ee23ec6c9857a95fa183a78741b0bGilad Arnold        remote_cmd = 'test -e /proc/%s' % self._devserver_pid
559260957d6079ee23ec6c9857a95fa183a78741b0bGilad Arnold        result = self._devserver_ssh.run(remote_cmd, ignore_status=True)
560260957d6079ee23ec6c9857a95fa183a78741b0bGilad Arnold        return result.exit_status == 0
5616c55bdb98e967675456a71a0971b81058536cac8Chris Sosa
5626c55bdb98e967675456a71a0971b81058536cac8Chris Sosa
5636c55bdb98e967675456a71a0971b81058536cac8Chris Sosa    def get_netloc(self):
5646c55bdb98e967675456a71a0971b81058536cac8Chris Sosa        """Returns the netloc (host:port) of the devserver."""
565260957d6079ee23ec6c9857a95fa183a78741b0bGilad Arnold        if not (self._devserver_pid and self._devserver_port):
5660338ff3b754d52b41ca2d0432164a757daa1112dGilad Arnold            raise error.TestError('No running omaha/devserver')
5676c55bdb98e967675456a71a0971b81058536cac8Chris Sosa
568260957d6079ee23ec6c9857a95fa183a78741b0bGilad Arnold        return '%s:%s' % (self._omaha_host, self._devserver_port)
5690ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
5706c55bdb98e967675456a71a0971b81058536cac8Chris Sosa
5712f1ae9f1f2464ebbeb226a6669fa90ef211a179cChris Sosa    def get_update_url(self):
5722f1ae9f1f2464ebbeb226a6669fa90ef211a179cChris Sosa        """Returns the update_url you can use to update via this server."""
5732f1ae9f1f2464ebbeb226a6669fa90ef211a179cChris Sosa        return urlparse.urlunsplit(('http', self.get_netloc(), '/update',
5742f1ae9f1f2464ebbeb226a6669fa90ef211a179cChris Sosa                                    '', ''))
5752f1ae9f1f2464ebbeb226a6669fa90ef211a179cChris Sosa
5762f1ae9f1f2464ebbeb226a6669fa90ef211a179cChris Sosa
577260957d6079ee23ec6c9857a95fa183a78741b0bGilad Arnold    def _get_devserver_file_content(self, filename):
578260957d6079ee23ec6c9857a95fa183a78741b0bGilad Arnold        """Returns the content of a file on the devserver."""
579260957d6079ee23ec6c9857a95fa183a78741b0bGilad Arnold        return self._devserver_ssh.run_output('cat %s' % filename)
580260957d6079ee23ec6c9857a95fa183a78741b0bGilad Arnold
581260957d6079ee23ec6c9857a95fa183a78741b0bGilad Arnold
582260957d6079ee23ec6c9857a95fa183a78741b0bGilad Arnold    def _get_devserver_log(self):
583260957d6079ee23ec6c9857a95fa183a78741b0bGilad Arnold        """Obtain the devserver output."""
584260957d6079ee23ec6c9857a95fa183a78741b0bGilad Arnold        return self._get_devserver_file_content(self._devserver_logfile)
585260957d6079ee23ec6c9857a95fa183a78741b0bGilad Arnold
586260957d6079ee23ec6c9857a95fa183a78741b0bGilad Arnold
587260957d6079ee23ec6c9857a95fa183a78741b0bGilad Arnold    def _dump_devserver_log(self, logging_level=logging.ERROR):
5883563042207623fbb9db02018156e8b5fa0e4c2c3Chris Sosa        """Dump the devserver log to the autotest log.
58919426059c61c1b318b5d4bd04d70dacb63ec827dChris Sosa
59019426059c61c1b318b5d4bd04d70dacb63ec827dChris Sosa        @param logging_level: logging level (from logging) to log the output.
59119426059c61c1b318b5d4bd04d70dacb63ec827dChris Sosa        """
592260957d6079ee23ec6c9857a95fa183a78741b0bGilad Arnold        logging.log(logging_level, self._get_devserver_log())
5930ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
5940ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
5950ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold    @staticmethod
5960ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold    def _split_url(url):
5972f1ae9f1f2464ebbeb226a6669fa90ef211a179cChris Sosa        """Splits a URL into the URL base and path."""
5980ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        split_url = urlparse.urlsplit(url)
5990ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        url_base = urlparse.urlunsplit(
6002f1ae9f1f2464ebbeb226a6669fa90ef211a179cChris Sosa                (split_url.scheme, split_url.netloc, '', '', ''))
6012f1ae9f1f2464ebbeb226a6669fa90ef211a179cChris Sosa        url_path = split_url.path
6022f1ae9f1f2464ebbeb226a6669fa90ef211a179cChris Sosa        return url_base, url_path.lstrip('/')
6030ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
6040ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
605260957d6079ee23ec6c9857a95fa183a78741b0bGilad Arnold    def stop_devserver(self):
606260957d6079ee23ec6c9857a95fa183a78741b0bGilad Arnold        """Kill remote process and wait for it to die, dump its output."""
6076c55bdb98e967675456a71a0971b81058536cac8Chris Sosa        if not self._devserver_pid:
608ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa            logging.error('No running omaha/devserver.')
609ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa            return
6106c55bdb98e967675456a71a0971b81058536cac8Chris Sosa
6110338ff3b754d52b41ca2d0432164a757daa1112dGilad Arnold        logging.info('Killing omaha/devserver')
612260957d6079ee23ec6c9857a95fa183a78741b0bGilad Arnold        self._kill_remote_process()
61319426059c61c1b318b5d4bd04d70dacb63ec827dChris Sosa        logging.debug('Final devserver log before killing')
614260957d6079ee23ec6c9857a95fa183a78741b0bGilad Arnold        self._dump_devserver_log(logging.DEBUG)
6153563042207623fbb9db02018156e8b5fa0e4c2c3Chris Sosa        self._cleanup_devserver_files()
6160ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
6170ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
6180ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnoldclass autoupdate_EndToEndTest(test.test):
6190ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold    """Complete update test between two Chrome OS releases.
6200ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
6210ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold    Performs an end-to-end test of updating a ChromeOS device from one version
6220ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold    to another. This script requires a running (possibly remote) servod
6230ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold    instance connected to an actual servo board, which controls the DUT. It
6240ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold    also assumes that a corresponding target (update) image was staged for
625ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa    download on a central staging devserver.
6260ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
6270ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold    The test performs the following steps:
6280ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
6290ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold      0. Stages the source image and target update payload on the central
6300ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold         Lorry/devserver.
6310ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold      1. Spawns a private Omaha/devserver instance, configured to return the
6320ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold         target (update) image URL in response for an update check.
6330ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold      2. Connects to servod.
6340ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold         a. Resets the DUT to a known initial state.
6350ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold         b. Installs a source image on the DUT via recovery.
6360ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold      3. Reboots the DUT with the new image.
6370ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold      4. Triggers an update check at the DUT.
6380ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold      5. Watches as the DUT obtains an update and applies it.
6390ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold      6. Repeats 3-5, ensuring that the next update check shows the new image
6400ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold         version.
6410ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
642ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa    Some notes on naming:
643ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa      devserver: Refers to a machine running the Chrome OS Update Devserver.
644ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa      autotest_devserver: An autotest wrapper to interact with a devserver.
645ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa                          Can be used to stage artifacts to a devserver. While
646ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa                          this can also be used to update a machine, we do not
647ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa                          use it for that purpose in this test as we manage
648ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa                          updates with out own devserver instances (see below).
649ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa      omaha_devserver: This test's wrapper of a devserver running for the
650ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa                       purposes of emulating omaha. This test controls the
651ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa                       lifetime of this devserver instance and is separate
652ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa                       from the autotest lab's devserver's instances which are
653ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa                       only used for staging and hosting artifacts (because they
654ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa                       scale). These are run on the same machines as the actual
655ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa                       autotest devservers which are used for staging but on
656ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa                       different ports.
657ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa      *staged_url's: In this case staged refers to the fact that these items
658ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa                     are available to be downloaded statically from these urls
659ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa                     e.g. 'localhost:8080/static/my_file.gz'. These are usually
660ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa                     given after staging an artifact using a autotest_devserver
661ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa                     though they can be re-created given enough assumptions.
662ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa      *update_url's: Urls refering to the update RPC on a given omaha devserver.
663ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa                     Since we always use an instantiated omaha devserver to run
664ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa                     updates, these will always reference an existing instance
665ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa                     of an omaha devserver that we just created for the purposes
666ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa                     of updating.
667ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa      devserver_hostname: At the start of each test, we choose a devserver
668ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa                          machine in the lab for the test. We use the devserver
669ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa                          instance there (access by autotest_devserver) to stage
670ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa                          artifacts. However, we also use the same host to start
671ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa                          omaha devserver instances for updating machines with
672ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa                          (that reference the staged paylaods on the autotest
673ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa                          devserver instance). This hostname refers to that
674ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa                          machine we are using (since it's always the same for
675ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa                          both staging/omaha'ing).
676ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa
6770ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold    """
6780ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold    version = 1
6790ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
6800ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold    # Timeout periods, given in seconds.
681f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa    _WAIT_AFTER_SHUTDOWN_SECONDS = 10
682f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa    _WAIT_AFTER_UPDATE_SECONDS = 20
683f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa    _WAIT_FOR_USB_INSTALL_SECONDS = 4 * 60
684f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa    _WAIT_FOR_MP_RECOVERY_SECONDS = 8 * 60
6850ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold    _WAIT_FOR_INITIAL_UPDATE_CHECK_SECONDS = 12 * 60
6864f4426a2184828ad195fbb0b66b34f809a619236Chris Sosa    # TODO(sosa): Investigate why this needs to be so long (this used to be
6874f4426a2184828ad195fbb0b66b34f809a619236Chris Sosa    # 120 and regressed).
6884f4426a2184828ad195fbb0b66b34f809a619236Chris Sosa    _WAIT_FOR_DOWNLOAD_STARTED_SECONDS = 4 * 60
6896c55bdb98e967675456a71a0971b81058536cac8Chris Sosa    _WAIT_FOR_DOWNLOAD_COMPLETED_SECONDS = 10 * 60
690f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa    _WAIT_FOR_UPDATE_COMPLETED_SECONDS = 4 * 60
6910ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold    _WAIT_FOR_UPDATE_CHECK_AFTER_REBOOT_SECONDS = 15 * 60
69203901089c0db27508a6b2ff61ae8b75eba77cf37Gilad Arnold    _DEVSERVER_HOSTLOG_REQUEST_TIMEOUT_SECONDS = 30
6930ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
69415384a4ded97f171906a72106791d19e96e68fa7Gilad Arnold    _STATEFUL_UPDATE_FILENAME = 'stateful.tgz'
695c6ed2f633a1c4609a4b212da66b3e506008ee3a7Nam T. Nguyen    _LOGINABLE_MINIMUM_RELEASE = 5110
69615384a4ded97f171906a72106791d19e96e68fa7Gilad Arnold
6972f1ae9f1f2464ebbeb226a6669fa90ef211a179cChris Sosa    # Named tuple containing urls for staged urls needed for test.
6982f1ae9f1f2464ebbeb226a6669fa90ef211a179cChris Sosa    # source_url: url to find the update payload for the source image.
6992f1ae9f1f2464ebbeb226a6669fa90ef211a179cChris Sosa    # source_stateful_url: url to find the stateful payload for the source
7002f1ae9f1f2464ebbeb226a6669fa90ef211a179cChris Sosa    #                      image.
7012f1ae9f1f2464ebbeb226a6669fa90ef211a179cChris Sosa    # target_url: url to find the update payload for the target image.
7022f1ae9f1f2464ebbeb226a6669fa90ef211a179cChris Sosa    # target_stateful_url: url to find the stateful payload for the target
7032f1ae9f1f2464ebbeb226a6669fa90ef211a179cChris Sosa    #                      image.
7042f1ae9f1f2464ebbeb226a6669fa90ef211a179cChris Sosa    _STAGED_URLS = collections.namedtuple(
7052f1ae9f1f2464ebbeb226a6669fa90ef211a179cChris Sosa            'StagedUrls',
7062f1ae9f1f2464ebbeb226a6669fa90ef211a179cChris Sosa            ['source_url', 'source_stateful_url', 'target_url',
7072f1ae9f1f2464ebbeb226a6669fa90ef211a179cChris Sosa             'target_stateful_url'])
7082f1ae9f1f2464ebbeb226a6669fa90ef211a179cChris Sosa
7090ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
710f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa    def _servo_dut_power_up(self):
7110ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        """Powers up the DUT, optionally simulating a Ctrl-D key press."""
712f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa        self._host.servo.power_short_press()
713f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa        if self._dev_mode:
714f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa            self._host.servo.pass_devmode()
7150ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
7160ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
717f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa    def _servo_dut_reboot(self, disconnect_usbkey=False):
7180ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        """Reboots a DUT.
7190ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
720e7bd9369b76e2bbdc230c3c1aeb9afc6e6ec60baVadim Bendebury        @param disconnect_usbkey: detach USB flash device from the DUT before
721e7bd9369b76e2bbdc230c3c1aeb9afc6e6ec60baVadim Bendebury               powering it back up; this is useful when (for example) a USB
722e7bd9369b76e2bbdc230c3c1aeb9afc6e6ec60baVadim Bendebury               booted device need not see the attached USB key after the
723e7bd9369b76e2bbdc230c3c1aeb9afc6e6ec60baVadim Bendebury               reboot.
7240ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
7250ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        @raise error.TestFail if DUT fails to reboot.
7260ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
7270ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        """
7280338ff3b754d52b41ca2d0432164a757daa1112dGilad Arnold        logging.info('Rebooting dut')
729f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa        self._host.servo.power_long_press()
7300ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        _wait(self._WAIT_AFTER_SHUTDOWN_SECONDS, 'after shutdown')
731e7bd9369b76e2bbdc230c3c1aeb9afc6e6ec60baVadim Bendebury        if disconnect_usbkey:
732f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa            self._host.servo.switch_usbkey('host')
733f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa
734f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa        self._servo_dut_power_up()
735f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa        if self._use_test_image:
736f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa            if not self._host.wait_up(timeout=self._host.BOOT_TIMEOUT):
7370ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold                raise error.TestFail(
7380338ff3b754d52b41ca2d0432164a757daa1112dGilad Arnold                        'DUT %s failed to boot after %d secs' %
739f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa                        (self._host.ip, self._host.BOOT_TIMEOUT))
7400ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        else:
741cfa14a695654bc9cbfe9b9ce5868d669d0c57e0fGilad Arnold            # TODO(garnold) chromium-os:33766: implement waiting for MP-signed
742cfa14a695654bc9cbfe9b9ce5868d669d0c57e0fGilad Arnold            # images; ideas include waiting for a ping reply, or using a GPIO
743cfa14a695654bc9cbfe9b9ce5868d669d0c57e0fGilad Arnold            # signal.
744cfa14a695654bc9cbfe9b9ce5868d669d0c57e0fGilad Arnold            pass
7450ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
7460ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
747ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa    def _install_mp_image(self, staged_image_url):
7480ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        """Installs an MP-signed recovery image on a DUT.
7490ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
750ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa        @param staged_image_url: URL of the image on a Lorry/devserver
7510ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        """
7520ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        # Flash DUT with source image version, using recovery.
7530338ff3b754d52b41ca2d0432164a757daa1112dGilad Arnold        logging.info('Installing source mp-signed image via recovery: %s',
754ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa                     staged_image_url)
755f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa        self._host.servo.install_recovery_image(
756ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa                staged_image_url,
7570ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold                wait_timeout=self._WAIT_FOR_MP_RECOVERY_SECONDS)
7580ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
7590ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        # Reboot the DUT after installation.
760f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa        self._servo_dut_reboot(disconnect_usbkey=True)
7610ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
7620ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
763ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa    def _install_test_image_with_servo(self, staged_image_url):
7640ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        """Installs a test image on a DUT, booted via recovery.
7650ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
766ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa        @param staged_image_url: URL of the image on the devserver
7670ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        @param is_dev_nmode: whether or not the DUT is in dev mode
7680ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
7690ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        @raise error.TestFail if DUT cannot boot the test image from USB;
7700ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold               AutotestHostRunError if failed to run the install command on the
7710ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold               DUT.
7720ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
7730ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        """
7740338ff3b754d52b41ca2d0432164a757daa1112dGilad Arnold        logging.info('Installing source test image via recovery: %s',
775ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa                     staged_image_url)
776ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa        self._host.servo.install_recovery_image(staged_image_url)
7770338ff3b754d52b41ca2d0432164a757daa1112dGilad Arnold        logging.info('Waiting for image to boot')
778f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa        if not self._host.wait_up(timeout=self._host.USB_BOOT_TIMEOUT):
779cfa14a695654bc9cbfe9b9ce5868d669d0c57e0fGilad Arnold            raise error.TestFail(
780cfa14a695654bc9cbfe9b9ce5868d669d0c57e0fGilad Arnold                    'DUT %s boot from usb timed out after %d secs' %
781cfa14a695654bc9cbfe9b9ce5868d669d0c57e0fGilad Arnold                    (self._host, self._host.USB_BOOT_TIMEOUT))
7820338ff3b754d52b41ca2d0432164a757daa1112dGilad Arnold        logging.info('Installing new image onto ssd')
7830ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        try:
784f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa            cmd_result = self._host.run(
7850ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold                    'chromeos-install --yes',
7860ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold                    timeout=self._WAIT_FOR_USB_INSTALL_SECONDS,
7870ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold                    stdout_tee=None, stderr_tee=None)
788f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa        except error.AutotestHostRunError:
7890ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold            # Dump stdout (with stderr) to the error log.
7900338ff3b754d52b41ca2d0432164a757daa1112dGilad Arnold            logging.error('Command failed, stderr:\n' + cmd_result.stderr)
7910ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold            raise
7920ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
7930ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        # Reboot the DUT after installation.
794f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa        self._servo_dut_reboot(disconnect_usbkey=True)
7950ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
7960ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
7972f1ae9f1f2464ebbeb226a6669fa90ef211a179cChris Sosa    def _trigger_test_update(self, omaha_devserver):
7980ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        """Trigger an update check on a test image.
7990ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
8002f1ae9f1f2464ebbeb226a6669fa90ef211a179cChris Sosa        @param omaha_devserver: Instance of OmahaDevserver that will serve the
8012f1ae9f1f2464ebbeb226a6669fa90ef211a179cChris Sosa                                update.
8020ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        @raise RootFSUpdateError if anything went wrong.
8030ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
8040ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        """
8052f1ae9f1f2464ebbeb226a6669fa90ef211a179cChris Sosa        updater = autoupdater.ChromiumOSUpdater(
8062f1ae9f1f2464ebbeb226a6669fa90ef211a179cChris Sosa                omaha_devserver.get_update_url(), host=self._host)
8070ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        updater.trigger_update()
8080ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
8090ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
810f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa    def _get_rootdev(self):
81109706f18ea3878f1b173a63fdc92f2fc6450bb4dGilad Arnold        """Returns the partition device containing the rootfs on a host.
81209706f18ea3878f1b173a63fdc92f2fc6450bb4dGilad Arnold
81309706f18ea3878f1b173a63fdc92f2fc6450bb4dGilad Arnold        @return The rootfs partition device (string).
81409706f18ea3878f1b173a63fdc92f2fc6450bb4dGilad Arnold
81509706f18ea3878f1b173a63fdc92f2fc6450bb4dGilad Arnold        @raise AutotestHostRunError if command failed to run on host.
81609706f18ea3878f1b173a63fdc92f2fc6450bb4dGilad Arnold
81709706f18ea3878f1b173a63fdc92f2fc6450bb4dGilad Arnold        """
81866d74078caa789f80d4a43c82b61d6016a0cf430Chris Sosa        return self._host.run('rootdev -s').stdout.strip()
81909706f18ea3878f1b173a63fdc92f2fc6450bb4dGilad Arnold
82009706f18ea3878f1b173a63fdc92f2fc6450bb4dGilad Arnold
821ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa    def _stage_image(self, autotest_devserver, image_uri):
822ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa        """Stage a Chrome OS image onto a staging devserver.
8230ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
824ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa        @param autotest_devserver: instance of client.common_lib.dev_server to
825ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa                                   use to stage the image.
826ae6acd9b3e1c6f56310cdb4e05737122b7b2818cChris Sosa        @param image_uri: The uri of the image.
827ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa        @return URL of the staged image on the staging devserver.
8280ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
8290ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        @raise error.TestError if there's a problem with staging.
8300ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
8310ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        """
832f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa        if self._use_test_image:
8330ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold            # For this call, we just need the URL path up to the image.zip file
8340ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold            # (exclusive).
8350ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold            image_uri_path = urlparse.urlsplit(image_uri).path.partition(
8360ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold                    'image.zip')[0].strip('/')
8370ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold            try:
838ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa                autotest_devserver.stage_artifacts(image_uri_path,
839ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa                                                   ['test_image'])
840ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa                return autotest_devserver.get_test_image_url(image_uri_path)
8410ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold            except dev_server.DevServerException, e:
8420ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold                raise error.TestError(
8430338ff3b754d52b41ca2d0432164a757daa1112dGilad Arnold                        'Failed to stage source test image: %s' % e)
8440ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        else:
8450ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold            # TODO(garnold) chromium-os:33766: implement staging of MP-signed
8460ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold            # images.
8472f1ae9f1f2464ebbeb226a6669fa90ef211a179cChris Sosa            raise NotImplementedError()
8480ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
8490ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
850cfa14a695654bc9cbfe9b9ce5868d669d0c57e0fGilad Arnold    @staticmethod
851cfa14a695654bc9cbfe9b9ce5868d669d0c57e0fGilad Arnold    def _stage_payload(autotest_devserver, devserver_label, filename,
85272f48be6c3f7e30edc6ff9a785f9c4cb53521af4Don Garrett                       archive_url=None):
8532f1ae9f1f2464ebbeb226a6669fa90ef211a179cChris Sosa        """Stage the given payload onto the devserver.
8540ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
8552f1ae9f1f2464ebbeb226a6669fa90ef211a179cChris Sosa        Works for either a stateful or full/delta test payload. Expects the
8562f1ae9f1f2464ebbeb226a6669fa90ef211a179cChris Sosa        gs_path or a combo of devserver_label + filename.
8570ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
858ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa        @param autotest_devserver: instance of client.common_lib.dev_server to
859ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa                                   use to reach the devserver instance for this
860ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa                                   build.
8612f1ae9f1f2464ebbeb226a6669fa90ef211a179cChris Sosa        @param devserver_label: The build name e.g. x86-mario-release/<version>.
8622f1ae9f1f2464ebbeb226a6669fa90ef211a179cChris Sosa                                If set, assumes default gs archive bucket and
8632f1ae9f1f2464ebbeb226a6669fa90ef211a179cChris Sosa                                requires filename to be specified.
8642f1ae9f1f2464ebbeb226a6669fa90ef211a179cChris Sosa        @param filename: In conjunction with devserver_label, if just specifying
8652f1ae9f1f2464ebbeb226a6669fa90ef211a179cChris Sosa                         the devserver label name, this is which file are you
8662f1ae9f1f2464ebbeb226a6669fa90ef211a179cChris Sosa                         downloading.
86715384a4ded97f171906a72106791d19e96e68fa7Gilad Arnold        @param archive_url: An optional GS archive location, if not using the
86815384a4ded97f171906a72106791d19e96e68fa7Gilad Arnold                            devserver's default.
869ae6acd9b3e1c6f56310cdb4e05737122b7b2818cChris Sosa
8700ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        @return URL of the staged payload on the server.
8710ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
8720ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        @raise error.TestError if there's a problem with staging.
8730ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
8740ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        """
8752f1ae9f1f2464ebbeb226a6669fa90ef211a179cChris Sosa        try:
876ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa            autotest_devserver.stage_artifacts(
87772f48be6c3f7e30edc6ff9a785f9c4cb53521af4Don Garrett                    image=devserver_label, files=[filename],
87872f48be6c3f7e30edc6ff9a785f9c4cb53521af4Don Garrett                    archive_url=archive_url)
879ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa            return autotest_devserver.get_staged_file_url(filename,
880ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa                                                          devserver_label)
8812f1ae9f1f2464ebbeb226a6669fa90ef211a179cChris Sosa        except dev_server.DevServerException, e:
8820338ff3b754d52b41ca2d0432164a757daa1112dGilad Arnold            raise error.TestError('Failed to stage payload: %s' % e)
8830ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
8840ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
88572f48be6c3f7e30edc6ff9a785f9c4cb53521af4Don Garrett    def _stage_payload_by_uri(self, autotest_devserver, payload_uri):
88615384a4ded97f171906a72106791d19e96e68fa7Gilad Arnold        """Stage a payload based on its GS URI.
88715384a4ded97f171906a72106791d19e96e68fa7Gilad Arnold
88815384a4ded97f171906a72106791d19e96e68fa7Gilad Arnold        This infers the build's label, filename and GS archive from the
88915384a4ded97f171906a72106791d19e96e68fa7Gilad Arnold        provided GS URI.
89015384a4ded97f171906a72106791d19e96e68fa7Gilad Arnold
89115384a4ded97f171906a72106791d19e96e68fa7Gilad Arnold        @param autotest_devserver: instance of client.common_lib.dev_server to
89215384a4ded97f171906a72106791d19e96e68fa7Gilad Arnold                                   use to reach the devserver instance for this
89315384a4ded97f171906a72106791d19e96e68fa7Gilad Arnold                                   build.
89415384a4ded97f171906a72106791d19e96e68fa7Gilad Arnold        @param payload_uri: The full GS URI of the payload.
89515384a4ded97f171906a72106791d19e96e68fa7Gilad Arnold
89615384a4ded97f171906a72106791d19e96e68fa7Gilad Arnold        @return URL of the staged payload on the server.
89715384a4ded97f171906a72106791d19e96e68fa7Gilad Arnold
89815384a4ded97f171906a72106791d19e96e68fa7Gilad Arnold        @raise error.TestError if there's a problem with staging.
89915384a4ded97f171906a72106791d19e96e68fa7Gilad Arnold
90015384a4ded97f171906a72106791d19e96e68fa7Gilad Arnold        """
90115384a4ded97f171906a72106791d19e96e68fa7Gilad Arnold        archive_url, _, filename = payload_uri.rpartition('/')
90215384a4ded97f171906a72106791d19e96e68fa7Gilad Arnold        devserver_label = urlparse.urlsplit(archive_url).path.strip('/')
90315384a4ded97f171906a72106791d19e96e68fa7Gilad Arnold        return self._stage_payload(autotest_devserver, devserver_label,
90472f48be6c3f7e30edc6ff9a785f9c4cb53521af4Don Garrett                                   filename, archive_url=archive_url)
90515384a4ded97f171906a72106791d19e96e68fa7Gilad Arnold
90615384a4ded97f171906a72106791d19e96e68fa7Gilad Arnold
907cfa14a695654bc9cbfe9b9ce5868d669d0c57e0fGilad Arnold    @staticmethod
908cfa14a695654bc9cbfe9b9ce5868d669d0c57e0fGilad Arnold    def _payload_to_update_url(payload_url):
9092f1ae9f1f2464ebbeb226a6669fa90ef211a179cChris Sosa        """Given a update or stateful payload url, returns the update url."""
9107231260da421abdf5ceceff6ab60155936dca21fChris Sosa        # We want to transform it to the correct omaha url which is
9117231260da421abdf5ceceff6ab60155936dca21fChris Sosa        # <hostname>/update/...LABEL.
9122f1ae9f1f2464ebbeb226a6669fa90ef211a179cChris Sosa        base_url = payload_url.rpartition('/')[0]
9132f1ae9f1f2464ebbeb226a6669fa90ef211a179cChris Sosa        return base_url.replace('/static/', '/update/')
9142f1ae9f1f2464ebbeb226a6669fa90ef211a179cChris Sosa
9152f1ae9f1f2464ebbeb226a6669fa90ef211a179cChris Sosa
91615384a4ded97f171906a72106791d19e96e68fa7Gilad Arnold    def _get_stateful_uri(self, build_uri):
91715384a4ded97f171906a72106791d19e96e68fa7Gilad Arnold        """Returns a complete GS URI of a stateful update given a build path."""
91815384a4ded97f171906a72106791d19e96e68fa7Gilad Arnold        return '/'.join([build_uri.rstrip('/'), self._STATEFUL_UPDATE_FILENAME])
91915384a4ded97f171906a72106791d19e96e68fa7Gilad Arnold
92015384a4ded97f171906a72106791d19e96e68fa7Gilad Arnold
92115384a4ded97f171906a72106791d19e96e68fa7Gilad Arnold    def _payload_to_stateful_uri(self, payload_uri):
92215384a4ded97f171906a72106791d19e96e68fa7Gilad Arnold        """Given a payload GS URI, returns the corresponding stateful URI."""
92315384a4ded97f171906a72106791d19e96e68fa7Gilad Arnold        build_uri = payload_uri.rpartition('/')[0]
92415384a4ded97f171906a72106791d19e96e68fa7Gilad Arnold        return self._get_stateful_uri(build_uri)
9252f1ae9f1f2464ebbeb226a6669fa90ef211a179cChris Sosa
9262f1ae9f1f2464ebbeb226a6669fa90ef211a179cChris Sosa
9272f1ae9f1f2464ebbeb226a6669fa90ef211a179cChris Sosa    def update_via_test_payloads(self, omaha_host, payload_url, stateful_url,
9282f1ae9f1f2464ebbeb226a6669fa90ef211a179cChris Sosa                                 clobber):
929cfa14a695654bc9cbfe9b9ce5868d669d0c57e0fGilad Arnold        """Given the following update and stateful urls, update the DUT.
930cfa14a695654bc9cbfe9b9ce5868d669d0c57e0fGilad Arnold
931cfa14a695654bc9cbfe9b9ce5868d669d0c57e0fGilad Arnold        Only updates the rootfs/stateful if the respective url is provided.
932cfa14a695654bc9cbfe9b9ce5868d669d0c57e0fGilad Arnold
933cfa14a695654bc9cbfe9b9ce5868d669d0c57e0fGilad Arnold        @param omaha_host: If updating rootfs, redirect updates through this
934cfa14a695654bc9cbfe9b9ce5868d669d0c57e0fGilad Arnold            host. Should be None iff payload_url is None.
935cfa14a695654bc9cbfe9b9ce5868d669d0c57e0fGilad Arnold        @param payload_url: If set, the specified url to find the update
936cfa14a695654bc9cbfe9b9ce5868d669d0c57e0fGilad Arnold            payload.
937cfa14a695654bc9cbfe9b9ce5868d669d0c57e0fGilad Arnold        @param stateful_url: If set, the specified url to find the stateful
938cfa14a695654bc9cbfe9b9ce5868d669d0c57e0fGilad Arnold            payload.
939cfa14a695654bc9cbfe9b9ce5868d669d0c57e0fGilad Arnold        @param clobber: If True, do a clean install of stateful.
940cfa14a695654bc9cbfe9b9ce5868d669d0c57e0fGilad Arnold        """
941cfa14a695654bc9cbfe9b9ce5868d669d0c57e0fGilad Arnold        def perform_update(url, is_stateful):
942cfa14a695654bc9cbfe9b9ce5868d669d0c57e0fGilad Arnold            """Perform a rootfs/stateful update using given URL.
943cfa14a695654bc9cbfe9b9ce5868d669d0c57e0fGilad Arnold
944cfa14a695654bc9cbfe9b9ce5868d669d0c57e0fGilad Arnold            @param url: URL to update from.
945cfa14a695654bc9cbfe9b9ce5868d669d0c57e0fGilad Arnold            @param is_stateful: Whether this is a stateful or rootfs update.
946cfa14a695654bc9cbfe9b9ce5868d669d0c57e0fGilad Arnold            """
947cfa14a695654bc9cbfe9b9ce5868d669d0c57e0fGilad Arnold            if url:
948cfa14a695654bc9cbfe9b9ce5868d669d0c57e0fGilad Arnold                updater = autoupdater.ChromiumOSUpdater(url, host=self._host)
949cfa14a695654bc9cbfe9b9ce5868d669d0c57e0fGilad Arnold                if is_stateful:
950cfa14a695654bc9cbfe9b9ce5868d669d0c57e0fGilad Arnold                    updater.update_stateful(clobber=clobber)
951cfa14a695654bc9cbfe9b9ce5868d669d0c57e0fGilad Arnold                else:
952cfa14a695654bc9cbfe9b9ce5868d669d0c57e0fGilad Arnold                    updater.update_rootfs()
953cfa14a695654bc9cbfe9b9ce5868d669d0c57e0fGilad Arnold
954cfa14a695654bc9cbfe9b9ce5868d669d0c57e0fGilad Arnold        # We create a OmahaDevserver to redirect blah.bin to update/. This
955cfa14a695654bc9cbfe9b9ce5868d669d0c57e0fGilad Arnold        # allows us to use any payload filename to serve an update.
956cfa14a695654bc9cbfe9b9ce5868d669d0c57e0fGilad Arnold        temp_devserver = None
957cfa14a695654bc9cbfe9b9ce5868d669d0c57e0fGilad Arnold        try:
958cfa14a695654bc9cbfe9b9ce5868d669d0c57e0fGilad Arnold            if payload_url:
959cfa14a695654bc9cbfe9b9ce5868d669d0c57e0fGilad Arnold                temp_devserver = OmahaDevserver(
960260957d6079ee23ec6c9857a95fa183a78741b0bGilad Arnold                        omaha_host, self._devserver_dir, payload_url)
961cfa14a695654bc9cbfe9b9ce5868d669d0c57e0fGilad Arnold                temp_devserver.start_devserver()
962cfa14a695654bc9cbfe9b9ce5868d669d0c57e0fGilad Arnold                payload_url = temp_devserver.get_update_url()
963cfa14a695654bc9cbfe9b9ce5868d669d0c57e0fGilad Arnold
964cfa14a695654bc9cbfe9b9ce5868d669d0c57e0fGilad Arnold            stateful_url = self._payload_to_update_url(stateful_url)
965cfa14a695654bc9cbfe9b9ce5868d669d0c57e0fGilad Arnold
966cfa14a695654bc9cbfe9b9ce5868d669d0c57e0fGilad Arnold            perform_update(payload_url, False)
967cfa14a695654bc9cbfe9b9ce5868d669d0c57e0fGilad Arnold            perform_update(stateful_url, True)
968cfa14a695654bc9cbfe9b9ce5868d669d0c57e0fGilad Arnold        finally:
969cfa14a695654bc9cbfe9b9ce5868d669d0c57e0fGilad Arnold            if temp_devserver:
970260957d6079ee23ec6c9857a95fa183a78741b0bGilad Arnold                temp_devserver.stop_devserver()
9712f1ae9f1f2464ebbeb226a6669fa90ef211a179cChris Sosa
9722f1ae9f1f2464ebbeb226a6669fa90ef211a179cChris Sosa
973ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa    def install_source_version(self, devserver_hostname, image_url,
974ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa                               stateful_url):
9752f1ae9f1f2464ebbeb226a6669fa90ef211a179cChris Sosa        """Prepare the specified host with the image given by the urls.
9762f1ae9f1f2464ebbeb226a6669fa90ef211a179cChris Sosa
977ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa        @param devserver_hostname: If updating rootfs, redirect updates
978ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa                                   through this host. Should be None iff
979ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa                                   image_url is None.
9802f1ae9f1f2464ebbeb226a6669fa90ef211a179cChris Sosa        @param image_url: If set, the specified url to find the source image
9812f1ae9f1f2464ebbeb226a6669fa90ef211a179cChris Sosa                          or full payload for the source image.
9822f1ae9f1f2464ebbeb226a6669fa90ef211a179cChris Sosa        @param stateful_url: If set, the specified url to find the stateful
9832f1ae9f1f2464ebbeb226a6669fa90ef211a179cChris Sosa                             payload.
9842f1ae9f1f2464ebbeb226a6669fa90ef211a179cChris Sosa        """
985f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa        if self._use_servo:
986f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa            # Install source image (test vs MP).
987f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa            if self._use_test_image:
988f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa                self._install_test_image_with_servo(image_url)
989f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa            else:
990f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa                self._install_mp_image(image_url)
991f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa
992f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa        else:
99345f02ae47134953169805d281992c9edf0019250Chris Sosa            try:
9942f1ae9f1f2464ebbeb226a6669fa90ef211a179cChris Sosa                # Reboot to get us into a clean state.
9952f1ae9f1f2464ebbeb226a6669fa90ef211a179cChris Sosa                self._host.reboot()
9962f1ae9f1f2464ebbeb226a6669fa90ef211a179cChris Sosa                # Since we are installing the source image of the test, clobber
9972f1ae9f1f2464ebbeb226a6669fa90ef211a179cChris Sosa                # stateful.
998ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa                self.update_via_test_payloads(devserver_hostname, image_url,
9992f1ae9f1f2464ebbeb226a6669fa90ef211a179cChris Sosa                                              stateful_url, clobber=True)
10002f1ae9f1f2464ebbeb226a6669fa90ef211a179cChris Sosa                self._host.reboot()
100145f02ae47134953169805d281992c9edf0019250Chris Sosa            except error.AutoservRunError:
100245f02ae47134953169805d281992c9edf0019250Chris Sosa                logging.fatal('Error re-imaging the machine with the source '
100345f02ae47134953169805d281992c9edf0019250Chris Sosa                              'image %s', image_url)
10048577f8fb064fc8392bdc399fdd7465307d6da03bAlex Miller                raise error.TestError('Could not update to pre-conditions of '
10058577f8fb064fc8392bdc399fdd7465307d6da03bAlex Miller                                      'the test: we failed to start the '
10068577f8fb064fc8392bdc399fdd7465307d6da03bAlex Miller                                      'private devserver, connect to the host '
10078577f8fb064fc8392bdc399fdd7465307d6da03bAlex Miller                                      'and/or make it reboot.')
1008f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa
1009f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa
1010ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa    def stage_artifacts_onto_devserver(self, autotest_devserver, test_conf):
10112f1ae9f1f2464ebbeb226a6669fa90ef211a179cChris Sosa        """Stages artifacts that will be used by the test onto the devserver.
10122f1ae9f1f2464ebbeb226a6669fa90ef211a179cChris Sosa
1013ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa        @param autotest_devserver: instance of client.common_lib.dev_server to
1014ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa                                   use to reach the devserver instance for this
1015ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa                                   build.
10162f1ae9f1f2464ebbeb226a6669fa90ef211a179cChris Sosa        @param test_conf: a dictionary containing test configuration values
10172f1ae9f1f2464ebbeb226a6669fa90ef211a179cChris Sosa
10182f1ae9f1f2464ebbeb226a6669fa90ef211a179cChris Sosa        @return a _STAGED_URLS tuple containing the staged urls.
1019f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa        """
10200338ff3b754d52b41ca2d0432164a757daa1112dGilad Arnold        logging.info('Staging images onto autotest devserver (%s)',
1021ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa                     autotest_devserver.url())
1022f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa
102315384a4ded97f171906a72106791d19e96e68fa7Gilad Arnold        source_image_uri = test_conf['source_image_uri']
102415384a4ded97f171906a72106791d19e96e68fa7Gilad Arnold
102515384a4ded97f171906a72106791d19e96e68fa7Gilad Arnold        staged_source_url = None
102615384a4ded97f171906a72106791d19e96e68fa7Gilad Arnold        source_stateful_uri = None
102715384a4ded97f171906a72106791d19e96e68fa7Gilad Arnold        staged_source_stateful_url = None
1028f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa        if self._use_servo:
102915384a4ded97f171906a72106791d19e96e68fa7Gilad Arnold            staged_source_url = self._stage_image(
103015384a4ded97f171906a72106791d19e96e68fa7Gilad Arnold                    autotest_devserver, source_image_uri)
103115384a4ded97f171906a72106791d19e96e68fa7Gilad Arnold            # Test image already contains a stateful update, leave
103215384a4ded97f171906a72106791d19e96e68fa7Gilad Arnold            # staged_source_stateful_url untouhced.
1033f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa        else:
103415384a4ded97f171906a72106791d19e96e68fa7Gilad Arnold            staged_source_url = self._stage_payload_by_uri(
103515384a4ded97f171906a72106791d19e96e68fa7Gilad Arnold                    autotest_devserver, source_image_uri)
103615384a4ded97f171906a72106791d19e96e68fa7Gilad Arnold
103715384a4ded97f171906a72106791d19e96e68fa7Gilad Arnold            # In order to properly install the source image using a full
103815384a4ded97f171906a72106791d19e96e68fa7Gilad Arnold            # payload we'll also need the stateful update that comes with it.
103915384a4ded97f171906a72106791d19e96e68fa7Gilad Arnold            # In general, tests may have their source artifacts in a different
104015384a4ded97f171906a72106791d19e96e68fa7Gilad Arnold            # location than their payloads. This is determined by whether or
104115384a4ded97f171906a72106791d19e96e68fa7Gilad Arnold            # not the source_archive_uri attribute is set; if it isn't set,
104215384a4ded97f171906a72106791d19e96e68fa7Gilad Arnold            # then we derive it from the dirname of the source payload.
10432f1ae9f1f2464ebbeb226a6669fa90ef211a179cChris Sosa            source_archive_uri = test_conf.get('source_archive_uri')
104415384a4ded97f171906a72106791d19e96e68fa7Gilad Arnold            if source_archive_uri:
104515384a4ded97f171906a72106791d19e96e68fa7Gilad Arnold                source_stateful_uri = self._get_stateful_uri(source_archive_uri)
10462f1ae9f1f2464ebbeb226a6669fa90ef211a179cChris Sosa            else:
104715384a4ded97f171906a72106791d19e96e68fa7Gilad Arnold                source_stateful_uri = self._payload_to_stateful_uri(
104815384a4ded97f171906a72106791d19e96e68fa7Gilad Arnold                        source_image_uri)
104915384a4ded97f171906a72106791d19e96e68fa7Gilad Arnold
105015384a4ded97f171906a72106791d19e96e68fa7Gilad Arnold            staged_source_stateful_url = self._stage_payload_by_uri(
105115384a4ded97f171906a72106791d19e96e68fa7Gilad Arnold                    autotest_devserver, source_stateful_uri)
105215384a4ded97f171906a72106791d19e96e68fa7Gilad Arnold
105315384a4ded97f171906a72106791d19e96e68fa7Gilad Arnold        target_payload_uri = test_conf['target_payload_uri']
105415384a4ded97f171906a72106791d19e96e68fa7Gilad Arnold        staged_target_url = self._stage_payload_by_uri(
105515384a4ded97f171906a72106791d19e96e68fa7Gilad Arnold                autotest_devserver, target_payload_uri)
105615384a4ded97f171906a72106791d19e96e68fa7Gilad Arnold        target_stateful_uri = None
105715384a4ded97f171906a72106791d19e96e68fa7Gilad Arnold        target_archive_uri = test_conf.get('target_archive_uri')
105815384a4ded97f171906a72106791d19e96e68fa7Gilad Arnold        if not target_archive_uri and self._job_repo_url:
10592f1ae9f1f2464ebbeb226a6669fa90ef211a179cChris Sosa            _, devserver_label = tools.get_devserver_build_from_package_url(
10602f1ae9f1f2464ebbeb226a6669fa90ef211a179cChris Sosa                    self._job_repo_url)
106115384a4ded97f171906a72106791d19e96e68fa7Gilad Arnold            staged_target_stateful_url = self._stage_payload(
106215384a4ded97f171906a72106791d19e96e68fa7Gilad Arnold                    autotest_devserver, devserver_label,
106315384a4ded97f171906a72106791d19e96e68fa7Gilad Arnold                    self._STATEFUL_UPDATE_FILENAME)
10642f1ae9f1f2464ebbeb226a6669fa90ef211a179cChris Sosa        else:
106515384a4ded97f171906a72106791d19e96e68fa7Gilad Arnold            if target_archive_uri:
106615384a4ded97f171906a72106791d19e96e68fa7Gilad Arnold                target_stateful_uri = self._get_stateful_uri(target_archive_uri)
106715384a4ded97f171906a72106791d19e96e68fa7Gilad Arnold            else:
106815384a4ded97f171906a72106791d19e96e68fa7Gilad Arnold                target_stateful_uri = self._payload_to_stateful_uri(
106915384a4ded97f171906a72106791d19e96e68fa7Gilad Arnold                    target_payload_uri)
1070f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa
107115384a4ded97f171906a72106791d19e96e68fa7Gilad Arnold            staged_target_stateful_url = self._stage_payload_by_uri(
107215384a4ded97f171906a72106791d19e96e68fa7Gilad Arnold                    autotest_devserver, target_stateful_uri)
10732f1ae9f1f2464ebbeb226a6669fa90ef211a179cChris Sosa
107415384a4ded97f171906a72106791d19e96e68fa7Gilad Arnold        # Log all the urls.
10750338ff3b754d52b41ca2d0432164a757daa1112dGilad Arnold        logging.info('Source %s from %s staged at %s',
107615384a4ded97f171906a72106791d19e96e68fa7Gilad Arnold                     'image' if self._use_servo else 'full payload',
107715384a4ded97f171906a72106791d19e96e68fa7Gilad Arnold                     source_image_uri, staged_source_url)
107815384a4ded97f171906a72106791d19e96e68fa7Gilad Arnold        if staged_source_stateful_url:
10790338ff3b754d52b41ca2d0432164a757daa1112dGilad Arnold            logging.info('Source stateful update from %s staged at %s',
108015384a4ded97f171906a72106791d19e96e68fa7Gilad Arnold                         source_stateful_uri, staged_source_stateful_url)
108115384a4ded97f171906a72106791d19e96e68fa7Gilad Arnold        logging.info('%s test payload from %s staged at %s',
108215384a4ded97f171906a72106791d19e96e68fa7Gilad Arnold                     test_conf['update_type'], target_payload_uri,
108315384a4ded97f171906a72106791d19e96e68fa7Gilad Arnold                     staged_target_url)
10840338ff3b754d52b41ca2d0432164a757daa1112dGilad Arnold        logging.info('Target stateful update from %s staged at %s',
108515384a4ded97f171906a72106791d19e96e68fa7Gilad Arnold                     target_stateful_uri or 'standard location',
108615384a4ded97f171906a72106791d19e96e68fa7Gilad Arnold                     staged_target_stateful_url)
108715384a4ded97f171906a72106791d19e96e68fa7Gilad Arnold
108815384a4ded97f171906a72106791d19e96e68fa7Gilad Arnold        return self._STAGED_URLS(staged_source_url, staged_source_stateful_url,
108915384a4ded97f171906a72106791d19e96e68fa7Gilad Arnold                                 staged_target_url, staged_target_stateful_url)
1090f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa
1091f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa
1092f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa    def initialize(self):
1093f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa        """Sets up variables that will be used by test."""
1094f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa        self._host = None
1095f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa        self._use_servo = False
1096f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa        self._dev_mode = False
1097f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa        self._omaha_devserver = None
1098f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa
1099f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa        self._use_test_image = True
11002f1ae9f1f2464ebbeb226a6669fa90ef211a179cChris Sosa        self._job_repo_url = None
1101f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa        self._devserver_dir = global_config.global_config.get_config_value(
1102f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa                'CROS', 'devserver_dir', default=None)
1103f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa        if self._devserver_dir is None:
1104f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa            raise error.TestError(
11050338ff3b754d52b41ca2d0432164a757daa1112dGilad Arnold                    'Path to devserver source tree not provided; please define '
1106f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa                    'devserver_dir under [CROS] in your shadow_config.ini')
1107f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa
1108f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa
1109f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa    def cleanup(self):
1110f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa        """Kill the omaha devserver if it's still around."""
1111f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa        if self._omaha_devserver:
1112260957d6079ee23ec6c9857a95fa183a78741b0bGilad Arnold            self._omaha_devserver.stop_devserver()
1113f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa
1114f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa        self._omaha_devserver = None
1115f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa
1116f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa
111745f02ae47134953169805d281992c9edf0019250Chris Sosa    def _verify_preconditions(self):
1118f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa        """Validate input args make sense."""
1119f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa        if self._use_servo and not self._host.servo:
1120f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa            raise error.AutotestError('Servo use specified but no servo '
1121f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa                                      'attached to host object.')
1122f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa
1123f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa        if not self._use_test_image and not self._use_servo:
11240338ff3b754d52b41ca2d0432164a757daa1112dGilad Arnold            raise error.TestError('Cannot install mp image without servo.')
1125f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa
1126f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa
1127c6ed2f633a1c4609a4b212da66b3e506008ee3a7Nam T. Nguyen    def _run_login_test(self, release_string):
1128c6ed2f633a1c4609a4b212da66b3e506008ee3a7Nam T. Nguyen        """Runs login_LoginSuccess test if it is supported by the release."""
1129c6ed2f633a1c4609a4b212da66b3e506008ee3a7Nam T. Nguyen        # Only do login tests with recent builds, since they depend on
1130c6ed2f633a1c4609a4b212da66b3e506008ee3a7Nam T. Nguyen        # some binary compatibility with the build itself.
1131c6ed2f633a1c4609a4b212da66b3e506008ee3a7Nam T. Nguyen        # '5116.0.0' -> ('5116', '0', '0') -> 5116
1132c6ed2f633a1c4609a4b212da66b3e506008ee3a7Nam T. Nguyen        if int(release_string.split('.')[0]) > self._LOGINABLE_MINIMUM_RELEASE:
1133c6ed2f633a1c4609a4b212da66b3e506008ee3a7Nam T. Nguyen            # Login, to prove we can before/after the update.
1134c6ed2f633a1c4609a4b212da66b3e506008ee3a7Nam T. Nguyen            logging.info('Attempting to login (release %s).', release_string)
1135c6ed2f633a1c4609a4b212da66b3e506008ee3a7Nam T. Nguyen
1136c6ed2f633a1c4609a4b212da66b3e506008ee3a7Nam T. Nguyen            client_at = autotest.Autotest(self._host)
1137c6ed2f633a1c4609a4b212da66b3e506008ee3a7Nam T. Nguyen            client_at.run_test('login_LoginSuccess')
1138c6ed2f633a1c4609a4b212da66b3e506008ee3a7Nam T. Nguyen        else:
1139c6ed2f633a1c4609a4b212da66b3e506008ee3a7Nam T. Nguyen            logging.info('Not attempting login test because %s is older than '
1140c6ed2f633a1c4609a4b212da66b3e506008ee3a7Nam T. Nguyen                         '%d.', release_string, self._LOGINABLE_MINIMUM_RELEASE)
1141c6ed2f633a1c4609a4b212da66b3e506008ee3a7Nam T. Nguyen
1142c6ed2f633a1c4609a4b212da66b3e506008ee3a7Nam T. Nguyen
1143ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa    def _dump_update_engine_log(self):
1144ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa        """Dumps relevant AU error log."""
114545f02ae47134953169805d281992c9edf0019250Chris Sosa        if not self._use_servo:
114645f02ae47134953169805d281992c9edf0019250Chris Sosa            logging.error('Test failed -- dumping snippet of update_engine log')
1147ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa            try:
1148ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa                error_log = self._host.run_output(
1149ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa                        'tail -n 40 /var/log/update_engine.log')
1150ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa                logging.error(error_log)
1151ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa            except Exception:
1152ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa                # Mute any exceptions we get printing debug logs.
1153ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa                pass
115445f02ae47134953169805d281992c9edf0019250Chris Sosa
11550ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
1156ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa    def run_update_test(self, staged_urls, test_conf):
1157ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa        """Runs the actual update test once preconditions are met.
11580ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
1159ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa        @param staged_urls: A _STAGED_URLS tuple containing the staged urls.
1160ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa        @param test_conf: A dictionary containing test configuration values
11610ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
1162ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa        @raises ExpectedUpdateEventChainFailed if we failed to verify an update
1163ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa                event.
11640ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        """
1165c6ed2f633a1c4609a4b212da66b3e506008ee3a7Nam T. Nguyen
1166f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa        # On test images, record the active root partition.
1167f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa        source_rootfs_partition = None
1168f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa        if self._use_test_image:
1169f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa            source_rootfs_partition = self._get_rootdev()
11700338ff3b754d52b41ca2d0432164a757daa1112dGilad Arnold            logging.info('Source image rootfs partition: %s',
1171f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa                         source_rootfs_partition)
11720ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
11738ddeb9c8d6df7eb2e033651e39f434ebdf8af3ccSimran Basi        # Trigger an update.
11748ddeb9c8d6df7eb2e033651e39f434ebdf8af3ccSimran Basi        if self._use_test_image:
11758ddeb9c8d6df7eb2e033651e39f434ebdf8af3ccSimran Basi            self._trigger_test_update(self._omaha_devserver)
11768ddeb9c8d6df7eb2e033651e39f434ebdf8af3ccSimran Basi        else:
11778ddeb9c8d6df7eb2e033651e39f434ebdf8af3ccSimran Basi            # TODO(garnold) chromium-os:33766: use GPIOs to trigger an
11788ddeb9c8d6df7eb2e033651e39f434ebdf8af3ccSimran Basi            # update.
11798ddeb9c8d6df7eb2e033651e39f434ebdf8af3ccSimran Basi            pass
11800ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
11818ddeb9c8d6df7eb2e033651e39f434ebdf8af3ccSimran Basi        # Track update progress.
11828ddeb9c8d6df7eb2e033651e39f434ebdf8af3ccSimran Basi        omaha_netloc = self._omaha_devserver.get_netloc()
11838ddeb9c8d6df7eb2e033651e39f434ebdf8af3ccSimran Basi        omaha_hostlog_url = urlparse.urlunsplit(
11848ddeb9c8d6df7eb2e033651e39f434ebdf8af3ccSimran Basi                ['http', omaha_netloc, '/api/hostlog',
11858ddeb9c8d6df7eb2e033651e39f434ebdf8af3ccSimran Basi                 'ip=' + self._host.ip, ''])
11868ddeb9c8d6df7eb2e033651e39f434ebdf8af3ccSimran Basi        logging.info('Polling update progress from omaha/devserver: %s',
11878ddeb9c8d6df7eb2e033651e39f434ebdf8af3ccSimran Basi                     omaha_hostlog_url)
11888ddeb9c8d6df7eb2e033651e39f434ebdf8af3ccSimran Basi        log_verifier = UpdateEventLogVerifier(
11898ddeb9c8d6df7eb2e033651e39f434ebdf8af3ccSimran Basi                omaha_hostlog_url,
11908ddeb9c8d6df7eb2e033651e39f434ebdf8af3ccSimran Basi                self._DEVSERVER_HOSTLOG_REQUEST_TIMEOUT_SECONDS)
11918ddeb9c8d6df7eb2e033651e39f434ebdf8af3ccSimran Basi
11928ddeb9c8d6df7eb2e033651e39f434ebdf8af3ccSimran Basi        # Verify chain of events in a successful update process.
11938ddeb9c8d6df7eb2e033651e39f434ebdf8af3ccSimran Basi        chain = ExpectedUpdateEventChain(
11948ddeb9c8d6df7eb2e033651e39f434ebdf8af3ccSimran Basi                (self._WAIT_FOR_INITIAL_UPDATE_CHECK_SECONDS,
11958ddeb9c8d6df7eb2e033651e39f434ebdf8af3ccSimran Basi                 ExpectedUpdateEvent(
11968ddeb9c8d6df7eb2e033651e39f434ebdf8af3ccSimran Basi                     version=test_conf['source_release'],
11978ddeb9c8d6df7eb2e033651e39f434ebdf8af3ccSimran Basi                     error_message=('Failed to receive initial update check. '
11988ddeb9c8d6df7eb2e033651e39f434ebdf8af3ccSimran Basi                                    'Check Omaha devserver log in this '
11998ddeb9c8d6df7eb2e033651e39f434ebdf8af3ccSimran Basi                                    'output.'))),
12008ddeb9c8d6df7eb2e033651e39f434ebdf8af3ccSimran Basi                (self._WAIT_FOR_DOWNLOAD_STARTED_SECONDS,
12018ddeb9c8d6df7eb2e033651e39f434ebdf8af3ccSimran Basi                 ExpectedUpdateEvent(
12028ddeb9c8d6df7eb2e033651e39f434ebdf8af3ccSimran Basi                     event_type=EVENT_TYPE_DOWNLOAD_STARTED,
12038ddeb9c8d6df7eb2e033651e39f434ebdf8af3ccSimran Basi                     event_result=EVENT_RESULT_SUCCESS,
12048ddeb9c8d6df7eb2e033651e39f434ebdf8af3ccSimran Basi                     version=test_conf['source_release'],
12058ddeb9c8d6df7eb2e033651e39f434ebdf8af3ccSimran Basi                     error_message=(
12068ddeb9c8d6df7eb2e033651e39f434ebdf8af3ccSimran Basi                             'Failed to start the download of the update '
12078ddeb9c8d6df7eb2e033651e39f434ebdf8af3ccSimran Basi                             'payload from the staging server. Check both the '
12088ddeb9c8d6df7eb2e033651e39f434ebdf8af3ccSimran Basi                             'omaha log and update_engine.log in sysinfo (or '
12098ddeb9c8d6df7eb2e033651e39f434ebdf8af3ccSimran Basi                             'on the DUT).'))),
12108ddeb9c8d6df7eb2e033651e39f434ebdf8af3ccSimran Basi                (self._WAIT_FOR_DOWNLOAD_COMPLETED_SECONDS,
12118ddeb9c8d6df7eb2e033651e39f434ebdf8af3ccSimran Basi                 ExpectedUpdateEvent(
12128ddeb9c8d6df7eb2e033651e39f434ebdf8af3ccSimran Basi                     event_type=EVENT_TYPE_DOWNLOAD_FINISHED,
12138ddeb9c8d6df7eb2e033651e39f434ebdf8af3ccSimran Basi                     event_result=EVENT_RESULT_SUCCESS,
12148ddeb9c8d6df7eb2e033651e39f434ebdf8af3ccSimran Basi                     version=test_conf['source_release'],
12158ddeb9c8d6df7eb2e033651e39f434ebdf8af3ccSimran Basi                     error_message=(
12168ddeb9c8d6df7eb2e033651e39f434ebdf8af3ccSimran Basi                             'Failed to finish download from devserver. Check '
12178ddeb9c8d6df7eb2e033651e39f434ebdf8af3ccSimran Basi                             'the update_engine.log in sysinfo (or on the '
12188ddeb9c8d6df7eb2e033651e39f434ebdf8af3ccSimran Basi                             'DUT).'))),
12198ddeb9c8d6df7eb2e033651e39f434ebdf8af3ccSimran Basi                (self._WAIT_FOR_UPDATE_COMPLETED_SECONDS,
12208ddeb9c8d6df7eb2e033651e39f434ebdf8af3ccSimran Basi                 ExpectedUpdateEvent(
12218ddeb9c8d6df7eb2e033651e39f434ebdf8af3ccSimran Basi                     event_type=EVENT_TYPE_UPDATE_COMPLETE,
12228ddeb9c8d6df7eb2e033651e39f434ebdf8af3ccSimran Basi                     event_result=EVENT_RESULT_SUCCESS,
12238ddeb9c8d6df7eb2e033651e39f434ebdf8af3ccSimran Basi                     version=test_conf['source_release'],
12248ddeb9c8d6df7eb2e033651e39f434ebdf8af3ccSimran Basi                     error_message=(
12258ddeb9c8d6df7eb2e033651e39f434ebdf8af3ccSimran Basi                             'Failed to complete update before reboot. Check '
12268ddeb9c8d6df7eb2e033651e39f434ebdf8af3ccSimran Basi                             'the update_engine.log in sysinfo (or on the '
12278ddeb9c8d6df7eb2e033651e39f434ebdf8af3ccSimran Basi                             'DUT).'))))
12288ddeb9c8d6df7eb2e033651e39f434ebdf8af3ccSimran Basi
12298ddeb9c8d6df7eb2e033651e39f434ebdf8af3ccSimran Basi        log_verifier.verify_expected_event_chain(chain)
12308ddeb9c8d6df7eb2e033651e39f434ebdf8af3ccSimran Basi
12318ddeb9c8d6df7eb2e033651e39f434ebdf8af3ccSimran Basi        # Wait after an update completion (safety margin).
12328ddeb9c8d6df7eb2e033651e39f434ebdf8af3ccSimran Basi        _wait(self._WAIT_AFTER_UPDATE_SECONDS, 'after update completion')
1233f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa
1234f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa        # Reboot the DUT after the update.
1235ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa        if self._use_servo:
1236f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa            self._servo_dut_reboot()
1237f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa        else:
12382f1ae9f1f2464ebbeb226a6669fa90ef211a179cChris Sosa            # Only update the stateful partition since the test has updated the
12392f1ae9f1f2464ebbeb226a6669fa90ef211a179cChris Sosa            # rootfs.
12402f1ae9f1f2464ebbeb226a6669fa90ef211a179cChris Sosa            self.update_via_test_payloads(
12412f1ae9f1f2464ebbeb226a6669fa90ef211a179cChris Sosa                    None, None, staged_urls.target_stateful_url, clobber=False)
1242f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa            self._host.reboot()
12430ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
1244f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa        # Trigger a second update check (again, test vs MP).
1245f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa        if self._use_test_image:
12462f1ae9f1f2464ebbeb226a6669fa90ef211a179cChris Sosa            self._trigger_test_update(self._omaha_devserver)
1247f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa        else:
1248f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa            # TODO(garnold) chromium-os:33766: use GPIOs to trigger an
1249f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa            # update.
1250f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa            pass
12510ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
1252f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa        # Observe post-reboot update check, which should indicate that the
1253f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa        # image version has been updated.
1254f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa        chain = ExpectedUpdateEventChain(
1255f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa                (self._WAIT_FOR_UPDATE_CHECK_AFTER_REBOOT_SECONDS,
1256f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa                 ExpectedUpdateEvent(
12570338ff3b754d52b41ca2d0432164a757daa1112dGilad Arnold                     event_type=EVENT_TYPE_UPDATE_COMPLETE,
12580338ff3b754d52b41ca2d0432164a757daa1112dGilad Arnold                     event_result=EVENT_RESULT_SUCCESS_REBOOT,
1259f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa                     version=test_conf['target_release'],
1260ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa                     previous_version=test_conf['source_release'],
12610338ff3b754d52b41ca2d0432164a757daa1112dGilad Arnold                     error_message=(
12620338ff3b754d52b41ca2d0432164a757daa1112dGilad Arnold                             'Failed to reboot into the target version after '
12630338ff3b754d52b41ca2d0432164a757daa1112dGilad Arnold                             'an update. Check the sysinfo logs. This probably '
12640338ff3b754d52b41ca2d0432164a757daa1112dGilad Arnold                             'means that the updated image failed to verify '
12650338ff3b754d52b41ca2d0432164a757daa1112dGilad Arnold                             'after reboot and might mean that the update '
12660338ff3b754d52b41ca2d0432164a757daa1112dGilad Arnold                             'payload is bad'))))
1267ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa
1268ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa        log_verifier.verify_expected_event_chain(chain)
1269f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa
1270f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa        # On test images, make sure we're using a different partition after
1271f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa        # the update.
1272f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa        if self._use_test_image:
1273f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa            target_rootfs_partition = self._get_rootdev()
1274f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa            if target_rootfs_partition == source_rootfs_partition:
1275f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa                raise error.TestFail(
12760338ff3b754d52b41ca2d0432164a757daa1112dGilad Arnold                        'Rootfs partition did not change (%s)' %
1277f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa                        target_rootfs_partition)
12780ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
12790338ff3b754d52b41ca2d0432164a757daa1112dGilad Arnold            logging.info(
12800338ff3b754d52b41ca2d0432164a757daa1112dGilad Arnold                    'Target image rootfs partition changed as expected: %s',
12810338ff3b754d52b41ca2d0432164a757daa1112dGilad Arnold                    target_rootfs_partition)
12820338ff3b754d52b41ca2d0432164a757daa1112dGilad Arnold
12830338ff3b754d52b41ca2d0432164a757daa1112dGilad Arnold        logging.info('Update successful, test completed')
12840ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
1285ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa
1286ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa    def run_once(self, host, test_conf, use_servo):
1287ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa        """Performs a complete auto update test.
1288ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa
1289ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa        @param host: a host object representing the DUT
1290ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa        @param test_conf: a dictionary containing test configuration values
1291ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa        @param use_servo: True whether we should use servo.
1292ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa
1293ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa        @raise error.TestError if anything went wrong with setting up the test;
1294ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa               error.TestFail if any part of the test has failed.
1295ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa
1296ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa        """
1297f789cf3a52c720344062f0a6c782bb758f08b189Don Garrett
1298f789cf3a52c720344062f0a6c782bb758f08b189Don Garrett        if not test_conf['target_release']:
1299f789cf3a52c720344062f0a6c782bb758f08b189Don Garrett            raise RequiredArgumentMissing(
1300f789cf3a52c720344062f0a6c782bb758f08b189Don Garrett                    'target_release is a required argument.')
1301f789cf3a52c720344062f0a6c782bb758f08b189Don Garrett
1302ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa        # Attempt to get the job_repo_url to find the stateful payload for the
1303ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa        # target image.
1304ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa        try:
1305ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa            self._job_repo_url = host.lookup_job_repo_url()
1306ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa        except KeyError:
1307ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa            logging.warning('Job Repo URL not found. Assuming stateful '
1308ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa                            'payload can be found along with the target update')
1309ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa
1310ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa        self._host = host
1311ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa        self._use_test_image = test_conf.get('image_type') != 'mp'
1312ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa        self._use_servo = use_servo
1313ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa        if self._use_servo:
1314ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa            self._dev_mode = self._host.servo.get('dev_mode') == 'on'
1315ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa
1316ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa        # Verify that our arguments are sane.
1317ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa        self._verify_preconditions()
1318ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa
1319ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa        # Find a devserver to use. We use the payload URI as argument for the
1320ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa        # lab's devserver load-balancing mechanism.
1321ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa        autotest_devserver = dev_server.ImageServer.resolve(
1322ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa                test_conf['target_payload_uri'])
1323ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa        devserver_hostname = urlparse.urlparse(
1324ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa                autotest_devserver.url()).hostname
1325ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa
1326ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa        # Stage source images and update payloads onto a devserver.
1327ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa        staged_urls = self.stage_artifacts_onto_devserver(
1328ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa                autotest_devserver, test_conf)
1329ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa
1330ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa        # Install the source version onto the DUT.
1331ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa        self.install_source_version(devserver_hostname,
1332ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa                                    staged_urls.source_url,
1333ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa                                    staged_urls.source_stateful_url)
1334ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa
1335ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa        self._omaha_devserver = OmahaDevserver(
1336260957d6079ee23ec6c9857a95fa183a78741b0bGilad Arnold                devserver_hostname, self._devserver_dir, staged_urls.target_url)
1337ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa        self._omaha_devserver.start_devserver()
1338c6ed2f633a1c4609a4b212da66b3e506008ee3a7Nam T. Nguyen
1339c6ed2f633a1c4609a4b212da66b3e506008ee3a7Nam T. Nguyen        # Make sure we can login before the update.
1340c6ed2f633a1c4609a4b212da66b3e506008ee3a7Nam T. Nguyen        self._run_login_test(test_conf['source_release'])
1341c6ed2f633a1c4609a4b212da66b3e506008ee3a7Nam T. Nguyen
1342ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa        try:
1343ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa            self.run_update_test(staged_urls, test_conf)
1344ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa        except ExpectedUpdateEventChainFailed:
1345ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa            self._dump_update_engine_log()
1346ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa            raise
1347f789cf3a52c720344062f0a6c782bb758f08b189Don Garrett
1348c6ed2f633a1c4609a4b212da66b3e506008ee3a7Nam T. Nguyen        # And make sure we can login after update.
1349c6ed2f633a1c4609a4b212da66b3e506008ee3a7Nam T. Nguyen        self._run_login_test(test_conf['target_release'])
1350