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
53f9543109fc26571607da554e285eaf069ab2d51David Haddockfrom datetime import datetime, timedelta
62f1ae9f1f2464ebbeb226a6669fa90ef211a179cChris Sosaimport collections
70ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnoldimport json
80ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnoldimport logging
96c55bdb98e967675456a71a0971b81058536cac8Chris Sosaimport os
1015384a4ded97f171906a72106791d19e96e68fa7Gilad Arnoldimport time
110ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnoldimport urlparse
1203901089c0db27508a6b2ff61ae8b75eba77cf37Gilad Arnold
13a0dee8465654b79d97cbc1cfca82c42fc95bd8b5David Haddockfrom autotest_lib.client.common_lib import error
14a0dee8465654b79d97cbc1cfca82c42fc95bd8b5David Haddockfrom autotest_lib.client.common_lib.cros import dev_server
15a0dee8465654b79d97cbc1cfca82c42fc95bd8b5David Haddockfrom autotest_lib.server import autotest, test
162f1ae9f1f2464ebbeb226a6669fa90ef211a179cChris Sosafrom autotest_lib.server.cros.dynamic_suite import tools
170ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
180ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
19a0dee8465654b79d97cbc1cfca82c42fc95bd8b5David Haddockdef snippet(text):
20a0dee8465654b79d97cbc1cfca82c42fc95bd8b5David Haddock    """Returns the text with start/end snip markers around it.
210ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
22a0dee8465654b79d97cbc1cfca82c42fc95bd8b5David Haddock    @param text: The snippet text.
230ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
24a0dee8465654b79d97cbc1cfca82c42fc95bd8b5David Haddock    @return The text with start/end snip markers around it.
25a0dee8465654b79d97cbc1cfca82c42fc95bd8b5David Haddock    """
26a0dee8465654b79d97cbc1cfca82c42fc95bd8b5David Haddock    snip = '---8<---' * 10
27a0dee8465654b79d97cbc1cfca82c42fc95bd8b5David Haddock    start = '-- START -'
28a0dee8465654b79d97cbc1cfca82c42fc95bd8b5David Haddock    end = '-- END -'
29a0dee8465654b79d97cbc1cfca82c42fc95bd8b5David Haddock    return ('%s%s\n%s\n%s%s' %
30a0dee8465654b79d97cbc1cfca82c42fc95bd8b5David Haddock            (start, snip[len(start):], text, end, snip[len(end):]))
31ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa
32aa97f6e69d7a58bb4e0b20fbd41bcadb42575b9aDavid HaddockUPDATE_ENGINE_PERF_PATH = '/mnt/stateful_partition/unencrypted/preserve'
33aa97f6e69d7a58bb4e0b20fbd41bcadb42575b9aDavid HaddockUPDATE_ENGINE_PERF_SCRIPT = 'update_engine_performance_monitor.py'
34aa97f6e69d7a58bb4e0b20fbd41bcadb42575b9aDavid HaddockUPDATE_ENGINE_PERF_RESULTS_FILE = 'perf_data_results.json'
35ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa
360338ff3b754d52b41ca2d0432164a757daa1112dGilad Arnold# Update event types.
370338ff3b754d52b41ca2d0432164a757daa1112dGilad ArnoldEVENT_TYPE_DOWNLOAD_COMPLETE = '1'
380338ff3b754d52b41ca2d0432164a757daa1112dGilad ArnoldEVENT_TYPE_INSTALL_COMPLETE = '2'
390338ff3b754d52b41ca2d0432164a757daa1112dGilad ArnoldEVENT_TYPE_UPDATE_COMPLETE = '3'
400338ff3b754d52b41ca2d0432164a757daa1112dGilad ArnoldEVENT_TYPE_DOWNLOAD_STARTED = '13'
410338ff3b754d52b41ca2d0432164a757daa1112dGilad ArnoldEVENT_TYPE_DOWNLOAD_FINISHED = '14'
42fac9a5d238337da91939328386c2fa4fdfb6d957Alex DeymoEVENT_TYPE_REBOOTED_AFTER_UPDATE = '54'
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
50a0ca5707ed10a6575ed290f341294331455f7769Gilad Arnold# Omaha event types/results, from update_engine/omaha_request_action.h
51a0ca5707ed10a6575ed290f341294331455f7769Gilad Arnold# These are stored in dict form in order to easily print out the keys.
52a0ca5707ed10a6575ed290f341294331455f7769Gilad ArnoldEVENT_TYPE_DICT = {
53a0ca5707ed10a6575ed290f341294331455f7769Gilad Arnold        EVENT_TYPE_DOWNLOAD_COMPLETE: 'download_complete',
54a0ca5707ed10a6575ed290f341294331455f7769Gilad Arnold        EVENT_TYPE_INSTALL_COMPLETE: 'install_complete',
55a0ca5707ed10a6575ed290f341294331455f7769Gilad Arnold        EVENT_TYPE_UPDATE_COMPLETE: 'update_complete',
56a0ca5707ed10a6575ed290f341294331455f7769Gilad Arnold        EVENT_TYPE_DOWNLOAD_STARTED: 'download_started',
57fac9a5d238337da91939328386c2fa4fdfb6d957Alex Deymo        EVENT_TYPE_DOWNLOAD_FINISHED: 'download_finished',
58fac9a5d238337da91939328386c2fa4fdfb6d957Alex Deymo        EVENT_TYPE_REBOOTED_AFTER_UPDATE: 'rebooted_after_update'
59a0ca5707ed10a6575ed290f341294331455f7769Gilad Arnold}
60a0ca5707ed10a6575ed290f341294331455f7769Gilad Arnold
61a0ca5707ed10a6575ed290f341294331455f7769Gilad ArnoldEVENT_RESULT_DICT = {
62a0ca5707ed10a6575ed290f341294331455f7769Gilad Arnold        EVENT_RESULT_ERROR: 'error',
63a0ca5707ed10a6575ed290f341294331455f7769Gilad Arnold        EVENT_RESULT_SUCCESS: 'success',
64a0ca5707ed10a6575ed290f341294331455f7769Gilad Arnold        EVENT_RESULT_SUCCESS_REBOOT: 'success_reboot',
65a0ca5707ed10a6575ed290f341294331455f7769Gilad Arnold        EVENT_RESULT_UPDATE_DEFERRED: 'update_deferred'
66a0ca5707ed10a6575ed290f341294331455f7769Gilad Arnold}
67a0ca5707ed10a6575ed290f341294331455f7769Gilad Arnold
680338ff3b754d52b41ca2d0432164a757daa1112dGilad Arnold
69a0dee8465654b79d97cbc1cfca82c42fc95bd8b5David Haddockclass ExpectedUpdateEventChainFailed(error.TestFail):
70a0dee8465654b79d97cbc1cfca82c42fc95bd8b5David Haddock    """Raised if we fail to receive an expected event in a chain."""
71fffb6d7274294d9a57523fb7535dd627d0378e5fGilad Arnold
72fffb6d7274294d9a57523fb7535dd627d0378e5fGilad Arnold
730ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnoldclass ExpectedUpdateEvent(object):
74248108c1c925791d926105f42212b1033213a4dcGilad Arnold    """Defines an expected event in an update process."""
7545f02ae47134953169805d281992c9edf0019250Chris Sosa
760338ff3b754d52b41ca2d0432164a757daa1112dGilad Arnold    _ATTR_NAME_DICT_MAP = {
77a0ca5707ed10a6575ed290f341294331455f7769Gilad Arnold            'event_type': EVENT_TYPE_DICT,
78a0ca5707ed10a6575ed290f341294331455f7769Gilad Arnold            'event_result': EVENT_RESULT_DICT,
790338ff3b754d52b41ca2d0432164a757daa1112dGilad Arnold    }
800338ff3b754d52b41ca2d0432164a757daa1112dGilad Arnold
81a0ca5707ed10a6575ed290f341294331455f7769Gilad Arnold    _VALID_TYPES = set(EVENT_TYPE_DICT.keys())
82a0ca5707ed10a6575ed290f341294331455f7769Gilad Arnold    _VALID_RESULTS = set(EVENT_RESULT_DICT.keys())
8345f02ae47134953169805d281992c9edf0019250Chris Sosa
840ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold    def __init__(self, event_type=None, event_result=None, version=None,
85248108c1c925791d926105f42212b1033213a4dcGilad Arnold                 previous_version=None, on_error=None):
86248108c1c925791d926105f42212b1033213a4dcGilad Arnold        """Initializes an event expectation.
87248108c1c925791d926105f42212b1033213a4dcGilad Arnold
88248108c1c925791d926105f42212b1033213a4dcGilad Arnold        @param event_type: Expected event type.
89248108c1c925791d926105f42212b1033213a4dcGilad Arnold        @param event_result: Expected event result code.
90248108c1c925791d926105f42212b1033213a4dcGilad Arnold        @param version: Expected reported image version.
91248108c1c925791d926105f42212b1033213a4dcGilad Arnold        @param previous_version: Expected reported previous image version.
92248108c1c925791d926105f42212b1033213a4dcGilad Arnold        @param on_error: This is either an object to be returned when a received
93248108c1c925791d926105f42212b1033213a4dcGilad Arnold                         event mismatches the expectation, or a callable used
94248108c1c925791d926105f42212b1033213a4dcGilad Arnold                         for generating one. In the latter case, takes as
95248108c1c925791d926105f42212b1033213a4dcGilad Arnold                         input two attribute dictionaries (expected and actual)
96248108c1c925791d926105f42212b1033213a4dcGilad Arnold                         and an iterable of mismatched keys. If None, a generic
97248108c1c925791d926105f42212b1033213a4dcGilad Arnold                         message is returned.
98248108c1c925791d926105f42212b1033213a4dcGilad Arnold        """
990338ff3b754d52b41ca2d0432164a757daa1112dGilad Arnold        if event_type and event_type not in self._VALID_TYPES:
10045f02ae47134953169805d281992c9edf0019250Chris Sosa            raise ValueError('event_type %s is not valid.' % event_type)
10145f02ae47134953169805d281992c9edf0019250Chris Sosa
1020338ff3b754d52b41ca2d0432164a757daa1112dGilad Arnold        if event_result and event_result not in self._VALID_RESULTS:
10345f02ae47134953169805d281992c9edf0019250Chris Sosa            raise ValueError('event_result %s is not valid.' % event_result)
10445f02ae47134953169805d281992c9edf0019250Chris Sosa
1050ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        self._expected_attrs = {
1060ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold            'event_type': event_type,
1070ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold            'event_result': event_result,
1080ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold            'version': version,
1090ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold            'previous_version': previous_version,
1100ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        }
111248108c1c925791d926105f42212b1033213a4dcGilad Arnold        self._on_error = on_error
1120ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
1130ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
1140338ff3b754d52b41ca2d0432164a757daa1112dGilad Arnold    @staticmethod
1150338ff3b754d52b41ca2d0432164a757daa1112dGilad Arnold    def _attr_val_str(attr_val, helper_dict, default=None):
1160338ff3b754d52b41ca2d0432164a757daa1112dGilad Arnold        """Returns an enriched attribute value string, or default."""
1170338ff3b754d52b41ca2d0432164a757daa1112dGilad Arnold        if not attr_val:
1180338ff3b754d52b41ca2d0432164a757daa1112dGilad Arnold            return default
1190338ff3b754d52b41ca2d0432164a757daa1112dGilad Arnold
1200338ff3b754d52b41ca2d0432164a757daa1112dGilad Arnold        s = str(attr_val)
1210338ff3b754d52b41ca2d0432164a757daa1112dGilad Arnold        if helper_dict:
1220338ff3b754d52b41ca2d0432164a757daa1112dGilad Arnold            s += ':%s' % helper_dict.get(attr_val, 'unknown')
1230338ff3b754d52b41ca2d0432164a757daa1112dGilad Arnold
1240338ff3b754d52b41ca2d0432164a757daa1112dGilad Arnold        return s
1250338ff3b754d52b41ca2d0432164a757daa1112dGilad Arnold
1260338ff3b754d52b41ca2d0432164a757daa1112dGilad Arnold
1270338ff3b754d52b41ca2d0432164a757daa1112dGilad Arnold    def _attr_name_and_values(self, attr_name, expected_attr_val,
1280338ff3b754d52b41ca2d0432164a757daa1112dGilad Arnold                              actual_attr_val=None):
1290338ff3b754d52b41ca2d0432164a757daa1112dGilad Arnold        """Returns an attribute name, expected and actual value strings.
1300338ff3b754d52b41ca2d0432164a757daa1112dGilad Arnold
1310338ff3b754d52b41ca2d0432164a757daa1112dGilad Arnold        This will return (name, expected, actual); the returned value for
1320338ff3b754d52b41ca2d0432164a757daa1112dGilad Arnold        actual will be None if its respective input is None/empty.
1330338ff3b754d52b41ca2d0432164a757daa1112dGilad Arnold
1340338ff3b754d52b41ca2d0432164a757daa1112dGilad Arnold        """
1350338ff3b754d52b41ca2d0432164a757daa1112dGilad Arnold        helper_dict = self._ATTR_NAME_DICT_MAP.get(attr_name)
1360338ff3b754d52b41ca2d0432164a757daa1112dGilad Arnold        expected_attr_val_str = self._attr_val_str(expected_attr_val,
1370338ff3b754d52b41ca2d0432164a757daa1112dGilad Arnold                                                   helper_dict,
1380338ff3b754d52b41ca2d0432164a757daa1112dGilad Arnold                                                   default='any')
1390338ff3b754d52b41ca2d0432164a757daa1112dGilad Arnold        actual_attr_val_str = self._attr_val_str(actual_attr_val, helper_dict)
1400338ff3b754d52b41ca2d0432164a757daa1112dGilad Arnold
1410338ff3b754d52b41ca2d0432164a757daa1112dGilad Arnold        return attr_name, expected_attr_val_str, actual_attr_val_str
1420338ff3b754d52b41ca2d0432164a757daa1112dGilad Arnold
1430338ff3b754d52b41ca2d0432164a757daa1112dGilad Arnold
144248108c1c925791d926105f42212b1033213a4dcGilad Arnold    def _attrs_to_str(self, attrs_dict):
1450338ff3b754d52b41ca2d0432164a757daa1112dGilad Arnold        return ' '.join(['%s=%s' %
1460338ff3b754d52b41ca2d0432164a757daa1112dGilad Arnold                         self._attr_name_and_values(attr_name, attr_val)[0:2]
147248108c1c925791d926105f42212b1033213a4dcGilad Arnold                         for attr_name, attr_val in attrs_dict.iteritems()])
148248108c1c925791d926105f42212b1033213a4dcGilad Arnold
149248108c1c925791d926105f42212b1033213a4dcGilad Arnold
150248108c1c925791d926105f42212b1033213a4dcGilad Arnold    def __str__(self):
151248108c1c925791d926105f42212b1033213a4dcGilad Arnold        return self._attrs_to_str(self._expected_attrs)
1520ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
1530ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
1540ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold    def verify(self, actual_event):
1550ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        """Verify the attributes of an actual event.
1560ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
157ae6acd9b3e1c6f56310cdb4e05737122b7b2818cChris Sosa        @param actual_event: a dictionary containing event attributes
1580ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
159248108c1c925791d926105f42212b1033213a4dcGilad Arnold        @return An error message, or None if all attributes as expected.
1600ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
1610ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        """
162248108c1c925791d926105f42212b1033213a4dcGilad Arnold        mismatched_attrs = [
163a0dee8465654b79d97cbc1cfca82c42fc95bd8b5David Haddock            attr_name for attr_name, expected_attr_val
164a0dee8465654b79d97cbc1cfca82c42fc95bd8b5David Haddock            in self._expected_attrs.iteritems()
165a0dee8465654b79d97cbc1cfca82c42fc95bd8b5David Haddock            if (expected_attr_val and
166a0dee8465654b79d97cbc1cfca82c42fc95bd8b5David Haddock                not self._verify_attr(attr_name, expected_attr_val,
167a0dee8465654b79d97cbc1cfca82c42fc95bd8b5David Haddock                                      actual_event.get(attr_name)))]
168248108c1c925791d926105f42212b1033213a4dcGilad Arnold        if not mismatched_attrs:
169248108c1c925791d926105f42212b1033213a4dcGilad Arnold            return None
170248108c1c925791d926105f42212b1033213a4dcGilad Arnold        if callable(self._on_error):
171248108c1c925791d926105f42212b1033213a4dcGilad Arnold            return self._on_error(self._expected_attrs, actual_event,
172248108c1c925791d926105f42212b1033213a4dcGilad Arnold                                  mismatched_attrs)
173248108c1c925791d926105f42212b1033213a4dcGilad Arnold        if self._on_error is None:
174248108c1c925791d926105f42212b1033213a4dcGilad Arnold            return ('Received event (%s) does not match expectation (%s)' %
175248108c1c925791d926105f42212b1033213a4dcGilad Arnold                    (self._attrs_to_str(actual_event), self))
176248108c1c925791d926105f42212b1033213a4dcGilad Arnold        return self._on_error
1770ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
1780ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
1790ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold    def _verify_attr(self, attr_name, expected_attr_val, actual_attr_val):
1800ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        """Verifies that an actual log event attributes matches expected on.
1810ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
1820ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        @param attr_name: name of the attribute to verify
1830ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        @param expected_attr_val: expected attribute value
1840ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        @param actual_attr_val: actual attribute value
1850ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
1860ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        @return True if actual value is present and matches, False otherwise.
1870ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
1880ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        """
18945f02ae47134953169805d281992c9edf0019250Chris Sosa        # None values are assumed to be missing and non-matching.
190f014ab424450fd595c347d905ac06ccf3d6faaddGilad Arnold        if actual_attr_val is None:
1910338ff3b754d52b41ca2d0432164a757daa1112dGilad Arnold            logging.error('No value found for %s (expected %s)',
1920338ff3b754d52b41ca2d0432164a757daa1112dGilad Arnold                          *self._attr_name_and_values(attr_name,
1930338ff3b754d52b41ca2d0432164a757daa1112dGilad Arnold                                                      expected_attr_val)[0:2])
19445f02ae47134953169805d281992c9edf0019250Chris Sosa            return False
19545f02ae47134953169805d281992c9edf0019250Chris Sosa
19616e76893c34739649a751f42b3881b57620d665cGilad Arnold        # We allow expected version numbers (e.g. 2940.0.0) to be contained in
19716e76893c34739649a751f42b3881b57620d665cGilad Arnold        # actual values (2940.0.0-a1); this is necessary for the test to pass
19816e76893c34739649a751f42b3881b57620d665cGilad Arnold        # with developer / non-release images.
19916e76893c34739649a751f42b3881b57620d665cGilad Arnold        if (actual_attr_val == expected_attr_val or
20016e76893c34739649a751f42b3881b57620d665cGilad Arnold            ('version' in attr_name and expected_attr_val in actual_attr_val)):
20116e76893c34739649a751f42b3881b57620d665cGilad Arnold            return True
2020ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
20316e76893c34739649a751f42b3881b57620d665cGilad Arnold        return False
2040ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
2050ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
206248108c1c925791d926105f42212b1033213a4dcGilad Arnold    def get_attrs(self):
207248108c1c925791d926105f42212b1033213a4dcGilad Arnold        """Returns a dictionary of expected attributes."""
208248108c1c925791d926105f42212b1033213a4dcGilad Arnold        return dict(self._expected_attrs)
209248108c1c925791d926105f42212b1033213a4dcGilad Arnold
2103f9543109fc26571607da554e285eaf069ab2d51David Haddock
2110ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnoldclass ExpectedUpdateEventChain(object):
2120ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold    """Defines a chain of expected update events."""
213248108c1c925791d926105f42212b1033213a4dcGilad Arnold    def __init__(self):
214fac9a5d238337da91939328386c2fa4fdfb6d957Alex Deymo        self._expected_events_chain = []
2153f9543109fc26571607da554e285eaf069ab2d51David Haddock        self._current_timestamp = None
2160ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
2170ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
218fac9a5d238337da91939328386c2fa4fdfb6d957Alex Deymo    def add_event(self, expected_events, timeout, on_timeout=None):
219248108c1c925791d926105f42212b1033213a4dcGilad Arnold        """Adds an expected event to the chain.
220248108c1c925791d926105f42212b1033213a4dcGilad Arnold
221fac9a5d238337da91939328386c2fa4fdfb6d957Alex Deymo        @param expected_events: The ExpectedEvent, or a list thereof, to wait
222fac9a5d238337da91939328386c2fa4fdfb6d957Alex Deymo                                for. If a list is passed, it will wait for *any*
223fac9a5d238337da91939328386c2fa4fdfb6d957Alex Deymo                                of the provided events, but only one of those.
224248108c1c925791d926105f42212b1033213a4dcGilad Arnold        @param timeout: A timeout (in seconds) to wait for the event.
225248108c1c925791d926105f42212b1033213a4dcGilad Arnold        @param on_timeout: An error string to use if the event times out. If
226248108c1c925791d926105f42212b1033213a4dcGilad Arnold                           None, a generic message is used.
2270ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        """
228fac9a5d238337da91939328386c2fa4fdfb6d957Alex Deymo        if isinstance(expected_events, ExpectedUpdateEvent):
22916e76893c34739649a751f42b3881b57620d665cGilad Arnold            expected_events = [expected_events]
230fac9a5d238337da91939328386c2fa4fdfb6d957Alex Deymo        self._expected_events_chain.append(
231fac9a5d238337da91939328386c2fa4fdfb6d957Alex Deymo                (expected_events, timeout, on_timeout))
2320ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
2330ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
234cfa14a695654bc9cbfe9b9ce5868d669d0c57e0fGilad Arnold    @staticmethod
235fac9a5d238337da91939328386c2fa4fdfb6d957Alex Deymo    def _format_event_with_timeout(expected_events, timeout):
236cfa14a695654bc9cbfe9b9ce5868d669d0c57e0fGilad Arnold        """Returns a string representation of the event, with timeout."""
237248108c1c925791d926105f42212b1033213a4dcGilad Arnold        until = 'within %s seconds' % timeout if timeout else 'indefinitely'
238fac9a5d238337da91939328386c2fa4fdfb6d957Alex Deymo        return '%s, %s' % (' OR '.join(map(str, expected_events)), until)
2390ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
2400ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
2410ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold    def __str__(self):
2420ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        return ('[%s]' %
2430ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold                ', '.join(
244fac9a5d238337da91939328386c2fa4fdfb6d957Alex Deymo                    [self._format_event_with_timeout(expected_events, timeout)
245fac9a5d238337da91939328386c2fa4fdfb6d957Alex Deymo                     for expected_events, timeout, _
246fac9a5d238337da91939328386c2fa4fdfb6d957Alex Deymo                     in self._expected_events_chain]))
2470ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
2480ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
2490ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold    def __repr__(self):
250fac9a5d238337da91939328386c2fa4fdfb6d957Alex Deymo        return str(self._expected_events_chain)
2510ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
2520ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
2530ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold    def verify(self, get_next_event):
2540ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        """Verifies that an actual stream of events complies.
2550ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
2560ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        @param get_next_event: a function returning the next event
2570ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
258ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa        @raises ExpectedUpdateEventChainFailed if we failed to verify an event.
2590ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
2600ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        """
261fac9a5d238337da91939328386c2fa4fdfb6d957Alex Deymo        for expected_events, timeout, on_timeout in self._expected_events_chain:
2620338ff3b754d52b41ca2d0432164a757daa1112dGilad Arnold            logging.info('Expecting %s',
263fac9a5d238337da91939328386c2fa4fdfb6d957Alex Deymo                         self._format_event_with_timeout(expected_events,
264248108c1c925791d926105f42212b1033213a4dcGilad Arnold                                                         timeout))
265248108c1c925791d926105f42212b1033213a4dcGilad Arnold            err_msg = self._verify_event_with_timeout(
266fac9a5d238337da91939328386c2fa4fdfb6d957Alex Deymo                    expected_events, timeout, on_timeout, get_next_event)
267248108c1c925791d926105f42212b1033213a4dcGilad Arnold            if err_msg is not None:
268248108c1c925791d926105f42212b1033213a4dcGilad Arnold                logging.error('Failed expected event: %s', err_msg)
269248108c1c925791d926105f42212b1033213a4dcGilad Arnold                raise ExpectedUpdateEventChainFailed(err_msg)
2700ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
2710ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
2723f9543109fc26571607da554e285eaf069ab2d51David Haddock    def _verify_event_with_timeout(self, expected_events, timeout, on_timeout,
273248108c1c925791d926105f42212b1033213a4dcGilad Arnold                                   get_next_event):
2740ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        """Verify an expected event occurs within a given timeout.
2750ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
276fac9a5d238337da91939328386c2fa4fdfb6d957Alex Deymo        @param expected_events: the list of possible events expected next
277248108c1c925791d926105f42212b1033213a4dcGilad Arnold        @param timeout: specified in seconds
278248108c1c925791d926105f42212b1033213a4dcGilad Arnold        @param on_timeout: A string to return if timeout occurs, or None.
2790ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        @param get_next_event: function returning the next event in a stream
2800ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
281248108c1c925791d926105f42212b1033213a4dcGilad Arnold        @return None if event complies, an error string otherwise.
2820ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
2830ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        """
2843f9543109fc26571607da554e285eaf069ab2d51David Haddock        new_event = get_next_event()
2853f9543109fc26571607da554e285eaf069ab2d51David Haddock        if new_event:
2863f9543109fc26571607da554e285eaf069ab2d51David Haddock            # If this is the first event, set it as the current time
2873f9543109fc26571607da554e285eaf069ab2d51David Haddock            if self._current_timestamp is None:
2883f9543109fc26571607da554e285eaf069ab2d51David Haddock                self._current_timestamp = datetime.strptime(new_event[
2893f9543109fc26571607da554e285eaf069ab2d51David Haddock                                                                'timestamp'],
2903f9543109fc26571607da554e285eaf069ab2d51David Haddock                                                            '%Y-%m-%d %H:%M:%S')
2913f9543109fc26571607da554e285eaf069ab2d51David Haddock
2923f9543109fc26571607da554e285eaf069ab2d51David Haddock            # Get the time stamp for the current event and convert to datetime
2933f9543109fc26571607da554e285eaf069ab2d51David Haddock            timestamp = new_event['timestamp']
2943f9543109fc26571607da554e285eaf069ab2d51David Haddock            event_timestamp = datetime.strptime(timestamp, '%Y-%m-%d %H:%M:%S')
2953f9543109fc26571607da554e285eaf069ab2d51David Haddock
2963f9543109fc26571607da554e285eaf069ab2d51David Haddock            # Add the timeout onto the timestamp to get its expiry
2973f9543109fc26571607da554e285eaf069ab2d51David Haddock            event_timeout = self._current_timestamp + timedelta(seconds=timeout)
2983f9543109fc26571607da554e285eaf069ab2d51David Haddock
2993f9543109fc26571607da554e285eaf069ab2d51David Haddock            # If the event happened before the timeout
3003f9543109fc26571607da554e285eaf069ab2d51David Haddock            if event_timestamp < event_timeout:
3013f9543109fc26571607da554e285eaf069ab2d51David Haddock                difference = event_timestamp - self._current_timestamp
3023f9543109fc26571607da554e285eaf069ab2d51David Haddock                logging.info('Event took %s seconds to fire during the '
3033f9543109fc26571607da554e285eaf069ab2d51David Haddock                             'update', difference.seconds)
30416e76893c34739649a751f42b3881b57620d665cGilad Arnold                results = [event.verify(new_event) for event in expected_events]
3053f9543109fc26571607da554e285eaf069ab2d51David Haddock                self._current_timestamp = event_timestamp
30616e76893c34739649a751f42b3881b57620d665cGilad Arnold                return None if None in results else ' AND '.join(results)
3070ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
3080338ff3b754d52b41ca2d0432164a757daa1112dGilad Arnold        logging.error('Timeout expired')
309248108c1c925791d926105f42212b1033213a4dcGilad Arnold        if on_timeout is None:
310248108c1c925791d926105f42212b1033213a4dcGilad Arnold            return ('Waiting for event %s timed out after %d seconds' %
311fac9a5d238337da91939328386c2fa4fdfb6d957Alex Deymo                    (' OR '.join(map(str, expected_events)), timeout))
312248108c1c925791d926105f42212b1033213a4dcGilad Arnold        return on_timeout
3130ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
3140ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
3150ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnoldclass UpdateEventLogVerifier(object):
3160ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold    """Verifies update event chains on a devserver update log."""
317a0dee8465654b79d97cbc1cfca82c42fc95bd8b5David Haddock    def __init__(self, event_log_filename):
318a0dee8465654b79d97cbc1cfca82c42fc95bd8b5David Haddock        self._event_log_filename = event_log_filename
3190ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        self._event_log = []
3200ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        self._num_consumed_events = 0
3210ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
3220ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
323fac9a5d238337da91939328386c2fa4fdfb6d957Alex Deymo    def verify_expected_events_chain(self, expected_event_chain):
324ae6acd9b3e1c6f56310cdb4e05737122b7b2818cChris Sosa        """Verify a given event chain.
325ae6acd9b3e1c6f56310cdb4e05737122b7b2818cChris Sosa
326ae6acd9b3e1c6f56310cdb4e05737122b7b2818cChris Sosa        @param expected_event_chain: instance of expected event chain.
327ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa
328ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa        @raises ExpectedUpdateEventChainFailed if we failed to verify the an
329ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa                event.
330ae6acd9b3e1c6f56310cdb4e05737122b7b2818cChris Sosa        """
331ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa        expected_event_chain.verify(self._get_next_log_event)
3320ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
3330ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
3340ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold    def _get_next_log_event(self):
3350ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        """Returns the next event in an event log.
3360ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
337a0dee8465654b79d97cbc1cfca82c42fc95bd8b5David Haddock        Uses the filename handed to it during initialization to read the
338a0dee8465654b79d97cbc1cfca82c42fc95bd8b5David Haddock        host log from devserver used during the update.
3390ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
3400ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        @return The next new event in the host log, as reported by devserver;
34103901089c0db27508a6b2ff61ae8b75eba77cf37Gilad Arnold                None if no such event was found or an error occurred.
3420ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
3430ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        """
344a0dee8465654b79d97cbc1cfca82c42fc95bd8b5David Haddock        # (Re)read event log from hostlog file, if necessary.
3450ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        if len(self._event_log) <= self._num_consumed_events:
34603901089c0db27508a6b2ff61ae8b75eba77cf37Gilad Arnold            try:
347a0dee8465654b79d97cbc1cfca82c42fc95bd8b5David Haddock                with open(self._event_log_filename, 'r') as out_log:
348a0dee8465654b79d97cbc1cfca82c42fc95bd8b5David Haddock                  self._event_log = json.loads(out_log.read())
349a0dee8465654b79d97cbc1cfca82c42fc95bd8b5David Haddock            except Exception as e:
350a0dee8465654b79d97cbc1cfca82c42fc95bd8b5David Haddock                raise error.TestFail('Error while reading the hostlogs '
351ab2884bab13c08f21aaa780a1861959131a6f682David Haddock                                     'from devserver: %s' % e)
3520ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
35303901089c0db27508a6b2ff61ae8b75eba77cf37Gilad Arnold        # Return next new event, if one is found.
3540ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        if len(self._event_log) > self._num_consumed_events:
3557572b05fd3356b2ddf6866958e80c7885e44647bGilad Arnold            new_event = {
3567572b05fd3356b2ddf6866958e80c7885e44647bGilad Arnold                    key: str(val) for key, val
3577572b05fd3356b2ddf6866958e80c7885e44647bGilad Arnold                    in self._event_log[self._num_consumed_events].iteritems()
3587572b05fd3356b2ddf6866958e80c7885e44647bGilad Arnold            }
3590ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold            self._num_consumed_events += 1
3600338ff3b754d52b41ca2d0432164a757daa1112dGilad Arnold            logging.info('Consumed new event: %s', new_event)
3610ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold            return new_event
3620ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
3630ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
364f1d1104c736f79145209c76a147c1334f8dbb6a9Gilad Arnoldclass TestPlatform(object):
365f1d1104c736f79145209c76a147c1334f8dbb6a9Gilad Arnold    """An interface and factory for platform-dependent functionality."""
36615384a4ded97f171906a72106791d19e96e68fa7Gilad Arnold
3672f1ae9f1f2464ebbeb226a6669fa90ef211a179cChris Sosa    # Named tuple containing urls for staged urls needed for test.
3682f1ae9f1f2464ebbeb226a6669fa90ef211a179cChris Sosa    # source_url: url to find the update payload for the source image.
3692f1ae9f1f2464ebbeb226a6669fa90ef211a179cChris Sosa    # source_stateful_url: url to find the stateful payload for the source
3702f1ae9f1f2464ebbeb226a6669fa90ef211a179cChris Sosa    #                      image.
3712f1ae9f1f2464ebbeb226a6669fa90ef211a179cChris Sosa    # target_url: url to find the update payload for the target image.
3722f1ae9f1f2464ebbeb226a6669fa90ef211a179cChris Sosa    # target_stateful_url: url to find the stateful payload for the target
3732f1ae9f1f2464ebbeb226a6669fa90ef211a179cChris Sosa    #                      image.
374f1d1104c736f79145209c76a147c1334f8dbb6a9Gilad Arnold    StagedURLs = collections.namedtuple(
375f1d1104c736f79145209c76a147c1334f8dbb6a9Gilad Arnold            'StagedURLs',
3762f1ae9f1f2464ebbeb226a6669fa90ef211a179cChris Sosa            ['source_url', 'source_stateful_url', 'target_url',
3772f1ae9f1f2464ebbeb226a6669fa90ef211a179cChris Sosa             'target_stateful_url'])
3782f1ae9f1f2464ebbeb226a6669fa90ef211a179cChris Sosa
3790ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
380f1d1104c736f79145209c76a147c1334f8dbb6a9Gilad Arnold    def __init__(self):
381f1d1104c736f79145209c76a147c1334f8dbb6a9Gilad Arnold        assert False, 'Cannot instantiate this interface'
382f1d1104c736f79145209c76a147c1334f8dbb6a9Gilad Arnold
383f1d1104c736f79145209c76a147c1334f8dbb6a9Gilad Arnold
384f1d1104c736f79145209c76a147c1334f8dbb6a9Gilad Arnold    @staticmethod
385f1d1104c736f79145209c76a147c1334f8dbb6a9Gilad Arnold    def create(host):
386f1d1104c736f79145209c76a147c1334f8dbb6a9Gilad Arnold        """Returns a TestPlatform implementation based on the host type.
387f1d1104c736f79145209c76a147c1334f8dbb6a9Gilad Arnold
388f1d1104c736f79145209c76a147c1334f8dbb6a9Gilad Arnold        *DO NOT* override this method.
389f1d1104c736f79145209c76a147c1334f8dbb6a9Gilad Arnold
390f1d1104c736f79145209c76a147c1334f8dbb6a9Gilad Arnold        @param host: a host object representing the DUT
391f1d1104c736f79145209c76a147c1334f8dbb6a9Gilad Arnold
392f1d1104c736f79145209c76a147c1334f8dbb6a9Gilad Arnold        @return A TestPlatform implementation.
393f1d1104c736f79145209c76a147c1334f8dbb6a9Gilad Arnold        """
394f1d1104c736f79145209c76a147c1334f8dbb6a9Gilad Arnold        os_type = host.get_os_type()
395585cbd6a5cc32db99c6745aa4333471b6b095b43Keith Haddow        if os_type in ('cros', 'moblab'):
396f1d1104c736f79145209c76a147c1334f8dbb6a9Gilad Arnold            return ChromiumOSTestPlatform(host)
397f1d1104c736f79145209c76a147c1334f8dbb6a9Gilad Arnold
398f1d1104c736f79145209c76a147c1334f8dbb6a9Gilad Arnold        raise error.TestError('Unknown OS type reported by host: %s' % os_type)
399f1d1104c736f79145209c76a147c1334f8dbb6a9Gilad Arnold
4000ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
401a0dee8465654b79d97cbc1cfca82c42fc95bd8b5David Haddock    def initialize(self, autotest_devserver):
402f1d1104c736f79145209c76a147c1334f8dbb6a9Gilad Arnold        """Initialize the object.
4030ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
404f1d1104c736f79145209c76a147c1334f8dbb6a9Gilad Arnold        @param autotest_devserver: Instance of client.common_lib.dev_server to
405f1d1104c736f79145209c76a147c1334f8dbb6a9Gilad Arnold                                   use to reach the devserver instance for this
406f1d1104c736f79145209c76a147c1334f8dbb6a9Gilad Arnold                                   build.
4070ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        """
408f1d1104c736f79145209c76a147c1334f8dbb6a9Gilad Arnold        raise NotImplementedError
4090ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
4100ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
411f1d1104c736f79145209c76a147c1334f8dbb6a9Gilad Arnold    def prep_artifacts(self, test_conf):
412f1d1104c736f79145209c76a147c1334f8dbb6a9Gilad Arnold        """Prepares update artifacts for the test.
41309706f18ea3878f1b173a63fdc92f2fc6450bb4dGilad Arnold
414f1d1104c736f79145209c76a147c1334f8dbb6a9Gilad Arnold        The test config must include 'source_payload_uri' and
415f1d1104c736f79145209c76a147c1334f8dbb6a9Gilad Arnold        'target_payload_uri'. In addition, it may include platform-specific
416f1d1104c736f79145209c76a147c1334f8dbb6a9Gilad Arnold        values as determined by the test control file.
41709706f18ea3878f1b173a63fdc92f2fc6450bb4dGilad Arnold
418f1d1104c736f79145209c76a147c1334f8dbb6a9Gilad Arnold        @param test_conf: Dictionary containing the test configuration.
41909706f18ea3878f1b173a63fdc92f2fc6450bb4dGilad Arnold
420f1d1104c736f79145209c76a147c1334f8dbb6a9Gilad Arnold        @return A tuple of staged URLs.
421f1d1104c736f79145209c76a147c1334f8dbb6a9Gilad Arnold
422f1d1104c736f79145209c76a147c1334f8dbb6a9Gilad Arnold        @raise error.TestError on failure.
42309706f18ea3878f1b173a63fdc92f2fc6450bb4dGilad Arnold        """
424f1d1104c736f79145209c76a147c1334f8dbb6a9Gilad Arnold        raise NotImplementedError
42509706f18ea3878f1b173a63fdc92f2fc6450bb4dGilad Arnold
42609706f18ea3878f1b173a63fdc92f2fc6450bb4dGilad Arnold
427f1d1104c736f79145209c76a147c1334f8dbb6a9Gilad Arnold    def reboot_device(self):
428f1d1104c736f79145209c76a147c1334f8dbb6a9Gilad Arnold        """Reboots the device."""
429f1d1104c736f79145209c76a147c1334f8dbb6a9Gilad Arnold        raise NotImplementedError
430f1d1104c736f79145209c76a147c1334f8dbb6a9Gilad Arnold
431f1d1104c736f79145209c76a147c1334f8dbb6a9Gilad Arnold
432a0dee8465654b79d97cbc1cfca82c42fc95bd8b5David Haddock    def prep_device_for_update(self, source_payload_uri):
433f1d1104c736f79145209c76a147c1334f8dbb6a9Gilad Arnold        """Prepares the device for update.
434f1d1104c736f79145209c76a147c1334f8dbb6a9Gilad Arnold
435a0dee8465654b79d97cbc1cfca82c42fc95bd8b5David Haddock        @param source_payload_uri: Source payload GS URI to install.
436f1d1104c736f79145209c76a147c1334f8dbb6a9Gilad Arnold
437f1d1104c736f79145209c76a147c1334f8dbb6a9Gilad Arnold        @raise error.TestError on failure.
438f1d1104c736f79145209c76a147c1334f8dbb6a9Gilad Arnold        """
439f1d1104c736f79145209c76a147c1334f8dbb6a9Gilad Arnold        raise NotImplementedError
440f1d1104c736f79145209c76a147c1334f8dbb6a9Gilad Arnold
441f1d1104c736f79145209c76a147c1334f8dbb6a9Gilad Arnold
442f1d1104c736f79145209c76a147c1334f8dbb6a9Gilad Arnold    def get_active_slot(self):
443f1d1104c736f79145209c76a147c1334f8dbb6a9Gilad Arnold        """Returns the active boot slot of the device."""
444f1d1104c736f79145209c76a147c1334f8dbb6a9Gilad Arnold        raise NotImplementedError
445f1d1104c736f79145209c76a147c1334f8dbb6a9Gilad Arnold
446f1d1104c736f79145209c76a147c1334f8dbb6a9Gilad Arnold
447f1d1104c736f79145209c76a147c1334f8dbb6a9Gilad Arnold    def start_update_perf(self, bindir):
448f1d1104c736f79145209c76a147c1334f8dbb6a9Gilad Arnold        """Starts performance monitoring (if available).
449f1d1104c736f79145209c76a147c1334f8dbb6a9Gilad Arnold
450f1d1104c736f79145209c76a147c1334f8dbb6a9Gilad Arnold        @param bindir: Directory containing test binary files.
451f1d1104c736f79145209c76a147c1334f8dbb6a9Gilad Arnold        """
452f1d1104c736f79145209c76a147c1334f8dbb6a9Gilad Arnold        raise NotImplementedError
453f1d1104c736f79145209c76a147c1334f8dbb6a9Gilad Arnold
454f1d1104c736f79145209c76a147c1334f8dbb6a9Gilad Arnold
455aa97f6e69d7a58bb4e0b20fbd41bcadb42575b9aDavid Haddock    def stop_update_perf(self, resultdir):
456f1d1104c736f79145209c76a147c1334f8dbb6a9Gilad Arnold        """Stops performance monitoring and returns data (if available).
457f1d1104c736f79145209c76a147c1334f8dbb6a9Gilad Arnold
458aa97f6e69d7a58bb4e0b20fbd41bcadb42575b9aDavid Haddock        @param resultdir: Directory containing test result files.
459f1d1104c736f79145209c76a147c1334f8dbb6a9Gilad Arnold        @return Dictionary containing performance attributes.
460f1d1104c736f79145209c76a147c1334f8dbb6a9Gilad Arnold        """
461f1d1104c736f79145209c76a147c1334f8dbb6a9Gilad Arnold        raise NotImplementedError
462f1d1104c736f79145209c76a147c1334f8dbb6a9Gilad Arnold
463f1d1104c736f79145209c76a147c1334f8dbb6a9Gilad Arnold
464a0dee8465654b79d97cbc1cfca82c42fc95bd8b5David Haddock    def trigger_update(self, target_payload_uri):
465f1d1104c736f79145209c76a147c1334f8dbb6a9Gilad Arnold        """Kicks off an update.
466f1d1104c736f79145209c76a147c1334f8dbb6a9Gilad Arnold
467a0dee8465654b79d97cbc1cfca82c42fc95bd8b5David Haddock        @param target_payload_uri: The GS URI to use for the update.
468f1d1104c736f79145209c76a147c1334f8dbb6a9Gilad Arnold        """
469f1d1104c736f79145209c76a147c1334f8dbb6a9Gilad Arnold        raise NotImplementedError
470f1d1104c736f79145209c76a147c1334f8dbb6a9Gilad Arnold
471f1d1104c736f79145209c76a147c1334f8dbb6a9Gilad Arnold
472f1d1104c736f79145209c76a147c1334f8dbb6a9Gilad Arnold    def finalize_update(self):
473f1d1104c736f79145209c76a147c1334f8dbb6a9Gilad Arnold        """Performs post-update procedures."""
474f1d1104c736f79145209c76a147c1334f8dbb6a9Gilad Arnold        raise NotImplementedError
475f1d1104c736f79145209c76a147c1334f8dbb6a9Gilad Arnold
476f1d1104c736f79145209c76a147c1334f8dbb6a9Gilad Arnold
477f1d1104c736f79145209c76a147c1334f8dbb6a9Gilad Arnold    def get_update_log(self, num_lines):
478f1d1104c736f79145209c76a147c1334f8dbb6a9Gilad Arnold        """Returns the update log.
479f1d1104c736f79145209c76a147c1334f8dbb6a9Gilad Arnold
480f1d1104c736f79145209c76a147c1334f8dbb6a9Gilad Arnold        @param num_lines: Number of log lines to return (tail), zero for all.
481f1d1104c736f79145209c76a147c1334f8dbb6a9Gilad Arnold
482f1d1104c736f79145209c76a147c1334f8dbb6a9Gilad Arnold        @return String containing the last |num_lines| from the update log.
483f1d1104c736f79145209c76a147c1334f8dbb6a9Gilad Arnold        """
484f1d1104c736f79145209c76a147c1334f8dbb6a9Gilad Arnold        raise NotImplementedError
485f1d1104c736f79145209c76a147c1334f8dbb6a9Gilad Arnold
486f1d1104c736f79145209c76a147c1334f8dbb6a9Gilad Arnold
487a0dee8465654b79d97cbc1cfca82c42fc95bd8b5David Haddock    def check_device_after_update(self):
488f1d1104c736f79145209c76a147c1334f8dbb6a9Gilad Arnold        """Runs final sanity checks.
489f1d1104c736f79145209c76a147c1334f8dbb6a9Gilad Arnold
490f1d1104c736f79145209c76a147c1334f8dbb6a9Gilad Arnold        @raise error.TestError on failure.
491f1d1104c736f79145209c76a147c1334f8dbb6a9Gilad Arnold        """
492f1d1104c736f79145209c76a147c1334f8dbb6a9Gilad Arnold        raise NotImplementedError
493f1d1104c736f79145209c76a147c1334f8dbb6a9Gilad Arnold
494f1d1104c736f79145209c76a147c1334f8dbb6a9Gilad Arnold
49534618870e735e83db984401a54ca1eecd8e4ac14Niranjan Kumar    def oobe_triggers_update(self):
49634618870e735e83db984401a54ca1eecd8e4ac14Niranjan Kumar        """Returns True if this host has an OOBE flow during which
49734618870e735e83db984401a54ca1eecd8e4ac14Niranjan Kumar        it will perform an update check and perhaps an update.
49834618870e735e83db984401a54ca1eecd8e4ac14Niranjan Kumar        One example of such a flow is Hands-Off Zero-Touch Enrollment.
49934618870e735e83db984401a54ca1eecd8e4ac14Niranjan Kumar
50034618870e735e83db984401a54ca1eecd8e4ac14Niranjan Kumar        @return Boolean indicating whether the DUT's OOBE triggers an update.
50134618870e735e83db984401a54ca1eecd8e4ac14Niranjan Kumar        """
50234618870e735e83db984401a54ca1eecd8e4ac14Niranjan Kumar        raise NotImplementedError
50334618870e735e83db984401a54ca1eecd8e4ac14Niranjan Kumar
50434618870e735e83db984401a54ca1eecd8e4ac14Niranjan Kumar
50534618870e735e83db984401a54ca1eecd8e4ac14Niranjan Kumar    def verify_version(self, version):
50634618870e735e83db984401a54ca1eecd8e4ac14Niranjan Kumar        """Compares the OS version on the DUT with the provided version.
50734618870e735e83db984401a54ca1eecd8e4ac14Niranjan Kumar
50834618870e735e83db984401a54ca1eecd8e4ac14Niranjan Kumar        @param version: The version to compare with (string).
50934618870e735e83db984401a54ca1eecd8e4ac14Niranjan Kumar        @raise error.TestFail if the versions differ.
51034618870e735e83db984401a54ca1eecd8e4ac14Niranjan Kumar        """
51134618870e735e83db984401a54ca1eecd8e4ac14Niranjan Kumar        actual_version = self._host.get_release_version()
51234618870e735e83db984401a54ca1eecd8e4ac14Niranjan Kumar        if actual_version != version:
51334618870e735e83db984401a54ca1eecd8e4ac14Niranjan Kumar            err_msg = 'Failed to verify OS version. Expected %s, was %s' % (
51434618870e735e83db984401a54ca1eecd8e4ac14Niranjan Kumar                version, actual_version)
51534618870e735e83db984401a54ca1eecd8e4ac14Niranjan Kumar            logging.error(err_msg)
51634618870e735e83db984401a54ca1eecd8e4ac14Niranjan Kumar            raise error.TestFail(err_msg)
51734618870e735e83db984401a54ca1eecd8e4ac14Niranjan Kumar
51834618870e735e83db984401a54ca1eecd8e4ac14Niranjan Kumar
519f1d1104c736f79145209c76a147c1334f8dbb6a9Gilad Arnoldclass ChromiumOSTestPlatform(TestPlatform):
520f1d1104c736f79145209c76a147c1334f8dbb6a9Gilad Arnold    """A TestPlatform implementation for Chromium OS."""
521f1d1104c736f79145209c76a147c1334f8dbb6a9Gilad Arnold
522f1d1104c736f79145209c76a147c1334f8dbb6a9Gilad Arnold    _STATEFUL_UPDATE_FILENAME = 'stateful.tgz'
523f1d1104c736f79145209c76a147c1334f8dbb6a9Gilad Arnold
524f1d1104c736f79145209c76a147c1334f8dbb6a9Gilad Arnold    def __init__(self, host):
525f1d1104c736f79145209c76a147c1334f8dbb6a9Gilad Arnold        self._host = host
526f1d1104c736f79145209c76a147c1334f8dbb6a9Gilad Arnold        self._autotest_devserver = None
527f1d1104c736f79145209c76a147c1334f8dbb6a9Gilad Arnold        self._staged_urls = None
528f1d1104c736f79145209c76a147c1334f8dbb6a9Gilad Arnold        self._perf_mon_pid = None
529f1d1104c736f79145209c76a147c1334f8dbb6a9Gilad Arnold
530f1d1104c736f79145209c76a147c1334f8dbb6a9Gilad Arnold
531a0dee8465654b79d97cbc1cfca82c42fc95bd8b5David Haddock    def _stage_payload(self, build_name, filename, archive_url=None):
5322f1ae9f1f2464ebbeb226a6669fa90ef211a179cChris Sosa        """Stage the given payload onto the devserver.
5330ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
5342f1ae9f1f2464ebbeb226a6669fa90ef211a179cChris Sosa        Works for either a stateful or full/delta test payload. Expects the
535a0dee8465654b79d97cbc1cfca82c42fc95bd8b5David Haddock        gs_path or a combo of build_name + filename.
536a0dee8465654b79d97cbc1cfca82c42fc95bd8b5David Haddock
537a0dee8465654b79d97cbc1cfca82c42fc95bd8b5David Haddock        @param build_name: The build name e.g. x86-mario-release/<version>.
538a0dee8465654b79d97cbc1cfca82c42fc95bd8b5David Haddock                           If set, assumes default gs archive bucket and
539a0dee8465654b79d97cbc1cfca82c42fc95bd8b5David Haddock                           requires filename to be specified.
540a0dee8465654b79d97cbc1cfca82c42fc95bd8b5David Haddock        @param filename: In conjunction with build_name, this is the file you
541a0dee8465654b79d97cbc1cfca82c42fc95bd8b5David Haddock                         are downloading.
54215384a4ded97f171906a72106791d19e96e68fa7Gilad Arnold        @param archive_url: An optional GS archive location, if not using the
54315384a4ded97f171906a72106791d19e96e68fa7Gilad Arnold                            devserver's default.
544ae6acd9b3e1c6f56310cdb4e05737122b7b2818cChris Sosa
5450ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        @return URL of the staged payload on the server.
5460ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
5470ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        @raise error.TestError if there's a problem with staging.
5480ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
5490ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        """
5502f1ae9f1f2464ebbeb226a6669fa90ef211a179cChris Sosa        try:
551a0dee8465654b79d97cbc1cfca82c42fc95bd8b5David Haddock            self._autotest_devserver.stage_artifacts(image=build_name,
552a0dee8465654b79d97cbc1cfca82c42fc95bd8b5David Haddock                                                     files=[filename],
553a0dee8465654b79d97cbc1cfca82c42fc95bd8b5David Haddock                                                     archive_url=archive_url)
554f1d1104c736f79145209c76a147c1334f8dbb6a9Gilad Arnold            return self._autotest_devserver.get_staged_file_url(filename,
555a0dee8465654b79d97cbc1cfca82c42fc95bd8b5David Haddock                                                                build_name)
5562f1ae9f1f2464ebbeb226a6669fa90ef211a179cChris Sosa        except dev_server.DevServerException, e:
5570338ff3b754d52b41ca2d0432164a757daa1112dGilad Arnold            raise error.TestError('Failed to stage payload: %s' % e)
5580ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
5590ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
560f1d1104c736f79145209c76a147c1334f8dbb6a9Gilad Arnold    def _stage_payload_by_uri(self, payload_uri):
56115384a4ded97f171906a72106791d19e96e68fa7Gilad Arnold        """Stage a payload based on its GS URI.
56215384a4ded97f171906a72106791d19e96e68fa7Gilad Arnold
56315384a4ded97f171906a72106791d19e96e68fa7Gilad Arnold        This infers the build's label, filename and GS archive from the
56415384a4ded97f171906a72106791d19e96e68fa7Gilad Arnold        provided GS URI.
56515384a4ded97f171906a72106791d19e96e68fa7Gilad Arnold
56615384a4ded97f171906a72106791d19e96e68fa7Gilad Arnold        @param payload_uri: The full GS URI of the payload.
56715384a4ded97f171906a72106791d19e96e68fa7Gilad Arnold
56815384a4ded97f171906a72106791d19e96e68fa7Gilad Arnold        @return URL of the staged payload on the server.
56915384a4ded97f171906a72106791d19e96e68fa7Gilad Arnold
57015384a4ded97f171906a72106791d19e96e68fa7Gilad Arnold        @raise error.TestError if there's a problem with staging.
57115384a4ded97f171906a72106791d19e96e68fa7Gilad Arnold
57215384a4ded97f171906a72106791d19e96e68fa7Gilad Arnold        """
57315384a4ded97f171906a72106791d19e96e68fa7Gilad Arnold        archive_url, _, filename = payload_uri.rpartition('/')
574a0dee8465654b79d97cbc1cfca82c42fc95bd8b5David Haddock        build_name = urlparse.urlsplit(archive_url).path.strip('/')
575a0dee8465654b79d97cbc1cfca82c42fc95bd8b5David Haddock        return self._stage_payload(build_name, filename,
57694694ec92c9e2c4d316aed34453ef75c21444c61Gilad Arnold                                   archive_url=archive_url)
57715384a4ded97f171906a72106791d19e96e68fa7Gilad Arnold
57815384a4ded97f171906a72106791d19e96e68fa7Gilad Arnold
57915384a4ded97f171906a72106791d19e96e68fa7Gilad Arnold    def _get_stateful_uri(self, build_uri):
58015384a4ded97f171906a72106791d19e96e68fa7Gilad Arnold        """Returns a complete GS URI of a stateful update given a build path."""
58115384a4ded97f171906a72106791d19e96e68fa7Gilad Arnold        return '/'.join([build_uri.rstrip('/'), self._STATEFUL_UPDATE_FILENAME])
58215384a4ded97f171906a72106791d19e96e68fa7Gilad Arnold
58315384a4ded97f171906a72106791d19e96e68fa7Gilad Arnold
58415384a4ded97f171906a72106791d19e96e68fa7Gilad Arnold    def _payload_to_stateful_uri(self, payload_uri):
58515384a4ded97f171906a72106791d19e96e68fa7Gilad Arnold        """Given a payload GS URI, returns the corresponding stateful URI."""
58615384a4ded97f171906a72106791d19e96e68fa7Gilad Arnold        build_uri = payload_uri.rpartition('/')[0]
587d8b9093f06cb882f7f46ca2f51d06630c9004f6dGwendal Grignou        if build_uri.endswith('payloads'):
588d8b9093f06cb882f7f46ca2f51d06630c9004f6dGwendal Grignou            build_uri = build_uri.rpartition('/')[0]
58915384a4ded97f171906a72106791d19e96e68fa7Gilad Arnold        return self._get_stateful_uri(build_uri)
5902f1ae9f1f2464ebbeb226a6669fa90ef211a179cChris Sosa
5912f1ae9f1f2464ebbeb226a6669fa90ef211a179cChris Sosa
592a0dee8465654b79d97cbc1cfca82c42fc95bd8b5David Haddock    @staticmethod
593a0dee8465654b79d97cbc1cfca82c42fc95bd8b5David Haddock    def _get_update_parameters_from_uri(payload_uri):
594a0dee8465654b79d97cbc1cfca82c42fc95bd8b5David Haddock        """Extract the two vars needed for cros_au from the Google Storage URI.
595cfa14a695654bc9cbfe9b9ce5868d669d0c57e0fGilad Arnold
596a0dee8465654b79d97cbc1cfca82c42fc95bd8b5David Haddock        dev_server.auto_update needs two values from this test:
597a0dee8465654b79d97cbc1cfca82c42fc95bd8b5David Haddock        (1) A build_name string e.g samus-release/R60-9583.0.0
598a0dee8465654b79d97cbc1cfca82c42fc95bd8b5David Haddock        (2) A filename of the exact payload file to use for the update. This
599a0dee8465654b79d97cbc1cfca82c42fc95bd8b5David Haddock        payload needs to have already been staged on the devserver.
600cfa14a695654bc9cbfe9b9ce5868d669d0c57e0fGilad Arnold
601a0dee8465654b79d97cbc1cfca82c42fc95bd8b5David Haddock        This function extracts those two values from a Google Storage URI.
602a0dee8465654b79d97cbc1cfca82c42fc95bd8b5David Haddock
603a0dee8465654b79d97cbc1cfca82c42fc95bd8b5David Haddock        @param payload_uri: Google Storage URI to extract values from
604cfa14a695654bc9cbfe9b9ce5868d669d0c57e0fGilad Arnold        """
605a0dee8465654b79d97cbc1cfca82c42fc95bd8b5David Haddock        archive_url, _, payload_file = payload_uri.rpartition('/')
606a0dee8465654b79d97cbc1cfca82c42fc95bd8b5David Haddock        build_name = urlparse.urlsplit(archive_url).path.strip('/')
607a0dee8465654b79d97cbc1cfca82c42fc95bd8b5David Haddock
608a0dee8465654b79d97cbc1cfca82c42fc95bd8b5David Haddock        # This test supports payload uris from two Google Storage buckets.
609a0dee8465654b79d97cbc1cfca82c42fc95bd8b5David Haddock        # They store their payloads slightly differently. One stores them in
610a0dee8465654b79d97cbc1cfca82c42fc95bd8b5David Haddock        # a separate payloads directory. E.g
611a0dee8465654b79d97cbc1cfca82c42fc95bd8b5David Haddock        # gs://chromeos-image-archive/samus-release/R60-9583.0.0/blah.bin
612a0dee8465654b79d97cbc1cfca82c42fc95bd8b5David Haddock        # gs://chromeos-releases/dev-channel/samus/9334.0.0/payloads/blah.bin
613a0dee8465654b79d97cbc1cfca82c42fc95bd8b5David Haddock        if build_name.endswith('payloads'):
614a0dee8465654b79d97cbc1cfca82c42fc95bd8b5David Haddock            build_name = build_name.rpartition('/')[0]
615a0dee8465654b79d97cbc1cfca82c42fc95bd8b5David Haddock            payload_file = 'payloads/' + payload_file
616a0dee8465654b79d97cbc1cfca82c42fc95bd8b5David Haddock
617a0dee8465654b79d97cbc1cfca82c42fc95bd8b5David Haddock        logging.debug('Extracted build_name: %s, payload_file: %s from %s.',
618a0dee8465654b79d97cbc1cfca82c42fc95bd8b5David Haddock                      build_name, payload_file, payload_uri)
619a0dee8465654b79d97cbc1cfca82c42fc95bd8b5David Haddock        return build_name, payload_file
620a0dee8465654b79d97cbc1cfca82c42fc95bd8b5David Haddock
621a0dee8465654b79d97cbc1cfca82c42fc95bd8b5David Haddock
6220c13495f06d389adeabbb95f858c9b0c836d2492Gwendal Grignou    def _install_version(self, payload_uri, clobber_stateful=False):
6230c13495f06d389adeabbb95f858c9b0c836d2492Gwendal Grignou        """Install the specified host with the image given by the url.
624a0dee8465654b79d97cbc1cfca82c42fc95bd8b5David Haddock
625a0dee8465654b79d97cbc1cfca82c42fc95bd8b5David Haddock        @param payload_uri: GS URI used to compute values for devserver cros_au
6260c13495f06d389adeabbb95f858c9b0c836d2492Gwendal Grignou        @param clobber_stateful: force a reinstall of the stateful image.
6272f1ae9f1f2464ebbeb226a6669fa90ef211a179cChris Sosa        """
62803286f0f93ff59fc1744099378201f85db9c1883Gwendal Grignou
629a0dee8465654b79d97cbc1cfca82c42fc95bd8b5David Haddock        build_name, payload_file = self._get_update_parameters_from_uri(
630a0dee8465654b79d97cbc1cfca82c42fc95bd8b5David Haddock            payload_uri)
6310c13495f06d389adeabbb95f858c9b0c836d2492Gwendal Grignou        logging.info('Installing image %s on the DUT', payload_uri)
632a0dee8465654b79d97cbc1cfca82c42fc95bd8b5David Haddock
633a0dee8465654b79d97cbc1cfca82c42fc95bd8b5David Haddock        try:
6340c13495f06d389adeabbb95f858c9b0c836d2492Gwendal Grignou            ds = self._autotest_devserver
6350c13495f06d389adeabbb95f858c9b0c836d2492Gwendal Grignou            _, pid =  ds.auto_update(host_name=self._host.hostname,
6360c13495f06d389adeabbb95f858c9b0c836d2492Gwendal Grignou                                     build_name=build_name,
6370c13495f06d389adeabbb95f858c9b0c836d2492Gwendal Grignou                                     force_update=True,
6380c13495f06d389adeabbb95f858c9b0c836d2492Gwendal Grignou                                     full_update=True,
6390c13495f06d389adeabbb95f858c9b0c836d2492Gwendal Grignou                                     log_dir=self._results_dir,
6400c13495f06d389adeabbb95f858c9b0c836d2492Gwendal Grignou                                     payload_filename=payload_file,
6410c13495f06d389adeabbb95f858c9b0c836d2492Gwendal Grignou                                     clobber_stateful=clobber_stateful)
642a0dee8465654b79d97cbc1cfca82c42fc95bd8b5David Haddock        except:
6430c13495f06d389adeabbb95f858c9b0c836d2492Gwendal Grignou            logging.fatal('ERROR: Failed to install image on the DUT.')
644a0dee8465654b79d97cbc1cfca82c42fc95bd8b5David Haddock            raise
6450c13495f06d389adeabbb95f858c9b0c836d2492Gwendal Grignou        return pid
646f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa
647f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa
648f1d1104c736f79145209c76a147c1334f8dbb6a9Gilad Arnold    def _stage_artifacts_onto_devserver(self, test_conf):
6492f1ae9f1f2464ebbeb226a6669fa90ef211a179cChris Sosa        """Stages artifacts that will be used by the test onto the devserver.
6502f1ae9f1f2464ebbeb226a6669fa90ef211a179cChris Sosa
6512f1ae9f1f2464ebbeb226a6669fa90ef211a179cChris Sosa        @param test_conf: a dictionary containing test configuration values
6522f1ae9f1f2464ebbeb226a6669fa90ef211a179cChris Sosa
653f1d1104c736f79145209c76a147c1334f8dbb6a9Gilad Arnold        @return a StagedURLs tuple containing the staged urls.
654f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa        """
6550338ff3b754d52b41ca2d0432164a757daa1112dGilad Arnold        logging.info('Staging images onto autotest devserver (%s)',
656f1d1104c736f79145209c76a147c1334f8dbb6a9Gilad Arnold                     self._autotest_devserver.url())
657f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa
65815384a4ded97f171906a72106791d19e96e68fa7Gilad Arnold        staged_source_url = None
659a0dee8465654b79d97cbc1cfca82c42fc95bd8b5David Haddock        source_payload_uri = test_conf['source_payload_uri']
660a0dee8465654b79d97cbc1cfca82c42fc95bd8b5David Haddock
6614cec366fff66e4603d9851c76680a6bb3f10fbe6Gilad Arnold        if source_payload_uri:
66294694ec92c9e2c4d316aed34453ef75c21444c61Gilad Arnold            staged_source_url = self._stage_payload_by_uri(source_payload_uri)
6634cec366fff66e4603d9851c76680a6bb3f10fbe6Gilad Arnold
6644cec366fff66e4603d9851c76680a6bb3f10fbe6Gilad Arnold            # In order to properly install the source image using a full
6654cec366fff66e4603d9851c76680a6bb3f10fbe6Gilad Arnold            # payload we'll also need the stateful update that comes with it.
6664cec366fff66e4603d9851c76680a6bb3f10fbe6Gilad Arnold            # In general, tests may have their source artifacts in a different
6674cec366fff66e4603d9851c76680a6bb3f10fbe6Gilad Arnold            # location than their payloads. This is determined by whether or
6684cec366fff66e4603d9851c76680a6bb3f10fbe6Gilad Arnold            # not the source_archive_uri attribute is set; if it isn't set,
6694cec366fff66e4603d9851c76680a6bb3f10fbe6Gilad Arnold            # then we derive it from the dirname of the source payload.
6704cec366fff66e4603d9851c76680a6bb3f10fbe6Gilad Arnold            source_archive_uri = test_conf.get('source_archive_uri')
6714cec366fff66e4603d9851c76680a6bb3f10fbe6Gilad Arnold            if source_archive_uri:
6724cec366fff66e4603d9851c76680a6bb3f10fbe6Gilad Arnold                source_stateful_uri = self._get_stateful_uri(source_archive_uri)
6732f1ae9f1f2464ebbeb226a6669fa90ef211a179cChris Sosa            else:
6744cec366fff66e4603d9851c76680a6bb3f10fbe6Gilad Arnold                source_stateful_uri = self._payload_to_stateful_uri(
6754cec366fff66e4603d9851c76680a6bb3f10fbe6Gilad Arnold                        source_payload_uri)
676fea7e766e59f8356dad6f70e46c0ee25997863a5Gilad Arnold
6774cec366fff66e4603d9851c76680a6bb3f10fbe6Gilad Arnold            staged_source_stateful_url = self._stage_payload_by_uri(
67894694ec92c9e2c4d316aed34453ef75c21444c61Gilad Arnold                    source_stateful_uri)
67915384a4ded97f171906a72106791d19e96e68fa7Gilad Arnold
6804cec366fff66e4603d9851c76680a6bb3f10fbe6Gilad Arnold            # Log source image URLs.
6814cec366fff66e4603d9851c76680a6bb3f10fbe6Gilad Arnold            logging.info('Source full payload from %s staged at %s',
6824cec366fff66e4603d9851c76680a6bb3f10fbe6Gilad Arnold                         source_payload_uri, staged_source_url)
6834cec366fff66e4603d9851c76680a6bb3f10fbe6Gilad Arnold            if staged_source_stateful_url:
6844cec366fff66e4603d9851c76680a6bb3f10fbe6Gilad Arnold                logging.info('Source stateful update from %s staged at %s',
6854cec366fff66e4603d9851c76680a6bb3f10fbe6Gilad Arnold                             source_stateful_uri, staged_source_stateful_url)
68615384a4ded97f171906a72106791d19e96e68fa7Gilad Arnold
68715384a4ded97f171906a72106791d19e96e68fa7Gilad Arnold        target_payload_uri = test_conf['target_payload_uri']
68894694ec92c9e2c4d316aed34453ef75c21444c61Gilad Arnold        staged_target_url = self._stage_payload_by_uri(target_payload_uri)
68915384a4ded97f171906a72106791d19e96e68fa7Gilad Arnold        target_stateful_uri = None
6900e18ad5f71d48432747733e10aad1e1cf5d93113Gilad Arnold        staged_target_stateful_url = None
69115384a4ded97f171906a72106791d19e96e68fa7Gilad Arnold        target_archive_uri = test_conf.get('target_archive_uri')
6920e18ad5f71d48432747733e10aad1e1cf5d93113Gilad Arnold        if target_archive_uri:
6930e18ad5f71d48432747733e10aad1e1cf5d93113Gilad Arnold            target_stateful_uri = self._get_stateful_uri(target_archive_uri)
6942f1ae9f1f2464ebbeb226a6669fa90ef211a179cChris Sosa        else:
6957b8e9b580b9883fc60bee7d26973c6d8d14cb5c8David Haddock            target_stateful_uri = self._payload_to_stateful_uri(
69615384a4ded97f171906a72106791d19e96e68fa7Gilad Arnold                    target_payload_uri)
697f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa
6980e18ad5f71d48432747733e10aad1e1cf5d93113Gilad Arnold        if not staged_target_stateful_url and target_stateful_uri:
69915384a4ded97f171906a72106791d19e96e68fa7Gilad Arnold            staged_target_stateful_url = self._stage_payload_by_uri(
70094694ec92c9e2c4d316aed34453ef75c21444c61Gilad Arnold                    target_stateful_uri)
7012f1ae9f1f2464ebbeb226a6669fa90ef211a179cChris Sosa
702fea7e766e59f8356dad6f70e46c0ee25997863a5Gilad Arnold        # Log target payload URLs.
70315384a4ded97f171906a72106791d19e96e68fa7Gilad Arnold        logging.info('%s test payload from %s staged at %s',
70415384a4ded97f171906a72106791d19e96e68fa7Gilad Arnold                     test_conf['update_type'], target_payload_uri,
70515384a4ded97f171906a72106791d19e96e68fa7Gilad Arnold                     staged_target_url)
7060338ff3b754d52b41ca2d0432164a757daa1112dGilad Arnold        logging.info('Target stateful update from %s staged at %s',
7077b8e9b580b9883fc60bee7d26973c6d8d14cb5c8David Haddock                     target_stateful_uri, staged_target_stateful_url)
70815384a4ded97f171906a72106791d19e96e68fa7Gilad Arnold
709f1d1104c736f79145209c76a147c1334f8dbb6a9Gilad Arnold        return self.StagedURLs(staged_source_url, staged_source_stateful_url,
710f1d1104c736f79145209c76a147c1334f8dbb6a9Gilad Arnold                               staged_target_url, staged_target_stateful_url)
711f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa
712f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa
713a0dee8465654b79d97cbc1cfca82c42fc95bd8b5David Haddock    def _run_login_test(self, tag):
7141192c41145b533f4138434bfa7e6ba2af9f75330David Haddock        """Runs login_LoginSuccess test on the DUT."""
715a0dee8465654b79d97cbc1cfca82c42fc95bd8b5David Haddock        client_at = autotest.Autotest(self._host)
716a0dee8465654b79d97cbc1cfca82c42fc95bd8b5David Haddock        client_at.run_test('login_LoginSuccess', tag=tag)
717c6ed2f633a1c4609a4b212da66b3e506008ee3a7Nam T. Nguyen
718c6ed2f633a1c4609a4b212da66b3e506008ee3a7Nam T. Nguyen
719f1d1104c736f79145209c76a147c1334f8dbb6a9Gilad Arnold    # Interface overrides.
720f1d1104c736f79145209c76a147c1334f8dbb6a9Gilad Arnold    #
721a0dee8465654b79d97cbc1cfca82c42fc95bd8b5David Haddock    def initialize(self, autotest_devserver, results_dir):
722f1d1104c736f79145209c76a147c1334f8dbb6a9Gilad Arnold        self._autotest_devserver = autotest_devserver
723a0dee8465654b79d97cbc1cfca82c42fc95bd8b5David Haddock        self._results_dir = results_dir
724f1d1104c736f79145209c76a147c1334f8dbb6a9Gilad Arnold
725f1d1104c736f79145209c76a147c1334f8dbb6a9Gilad Arnold
726f1d1104c736f79145209c76a147c1334f8dbb6a9Gilad Arnold    def reboot_device(self):
727f1d1104c736f79145209c76a147c1334f8dbb6a9Gilad Arnold        self._host.reboot()
728f1d1104c736f79145209c76a147c1334f8dbb6a9Gilad Arnold
729f1d1104c736f79145209c76a147c1334f8dbb6a9Gilad Arnold
730f1d1104c736f79145209c76a147c1334f8dbb6a9Gilad Arnold    def prep_artifacts(self, test_conf):
731f1d1104c736f79145209c76a147c1334f8dbb6a9Gilad Arnold        self._staged_urls = self._stage_artifacts_onto_devserver(test_conf)
732f1d1104c736f79145209c76a147c1334f8dbb6a9Gilad Arnold        return self._staged_urls
733f1d1104c736f79145209c76a147c1334f8dbb6a9Gilad Arnold
734f1d1104c736f79145209c76a147c1334f8dbb6a9Gilad Arnold
735a0dee8465654b79d97cbc1cfca82c42fc95bd8b5David Haddock    def prep_device_for_update(self, source_payload_uri):
736f1d1104c736f79145209c76a147c1334f8dbb6a9Gilad Arnold        # Install the source version onto the DUT.
737f1d1104c736f79145209c76a147c1334f8dbb6a9Gilad Arnold        if self._staged_urls.source_url:
7380c13495f06d389adeabbb95f858c9b0c836d2492Gwendal Grignou            self._install_version(source_payload_uri, clobber_stateful=True)
739f1d1104c736f79145209c76a147c1334f8dbb6a9Gilad Arnold
740a0dee8465654b79d97cbc1cfca82c42fc95bd8b5David Haddock        # Make sure we can login before the target update.
741a0dee8465654b79d97cbc1cfca82c42fc95bd8b5David Haddock        self._run_login_test('source_update')
742f1d1104c736f79145209c76a147c1334f8dbb6a9Gilad Arnold
743f1d1104c736f79145209c76a147c1334f8dbb6a9Gilad Arnold
744f1d1104c736f79145209c76a147c1334f8dbb6a9Gilad Arnold    def get_active_slot(self):
745f1d1104c736f79145209c76a147c1334f8dbb6a9Gilad Arnold        return self._host.run('rootdev -s').stdout.strip()
746f1d1104c736f79145209c76a147c1334f8dbb6a9Gilad Arnold
747f1d1104c736f79145209c76a147c1334f8dbb6a9Gilad Arnold
748f1d1104c736f79145209c76a147c1334f8dbb6a9Gilad Arnold    def start_update_perf(self, bindir):
749aa97f6e69d7a58bb4e0b20fbd41bcadb42575b9aDavid Haddock        """Copy performance monitoring script to DUT.
750f1d1104c736f79145209c76a147c1334f8dbb6a9Gilad Arnold
751aa97f6e69d7a58bb4e0b20fbd41bcadb42575b9aDavid Haddock        The updater will kick off the script during the update.
752aa97f6e69d7a58bb4e0b20fbd41bcadb42575b9aDavid Haddock        """
753aa97f6e69d7a58bb4e0b20fbd41bcadb42575b9aDavid Haddock        path = os.path.join(bindir, UPDATE_ENGINE_PERF_SCRIPT)
754aa97f6e69d7a58bb4e0b20fbd41bcadb42575b9aDavid Haddock        self._host.send_file(path, UPDATE_ENGINE_PERF_PATH)
755f1d1104c736f79145209c76a147c1334f8dbb6a9Gilad Arnold
756f1d1104c736f79145209c76a147c1334f8dbb6a9Gilad Arnold
757aa97f6e69d7a58bb4e0b20fbd41bcadb42575b9aDavid Haddock    def stop_update_perf(self, resultdir):
758aa97f6e69d7a58bb4e0b20fbd41bcadb42575b9aDavid Haddock        """ Copy the performance metrics back from the DUT."""
759aa97f6e69d7a58bb4e0b20fbd41bcadb42575b9aDavid Haddock        try:
760aa97f6e69d7a58bb4e0b20fbd41bcadb42575b9aDavid Haddock            path = os.path.join('/var/log', UPDATE_ENGINE_PERF_RESULTS_FILE)
761aa97f6e69d7a58bb4e0b20fbd41bcadb42575b9aDavid Haddock            self._host.get_file(path, resultdir)
762aa97f6e69d7a58bb4e0b20fbd41bcadb42575b9aDavid Haddock            self._host.run('rm %s' % path)
763aa97f6e69d7a58bb4e0b20fbd41bcadb42575b9aDavid Haddock            script = os.path.join(UPDATE_ENGINE_PERF_PATH,
764aa97f6e69d7a58bb4e0b20fbd41bcadb42575b9aDavid Haddock                                  UPDATE_ENGINE_PERF_SCRIPT)
765aa97f6e69d7a58bb4e0b20fbd41bcadb42575b9aDavid Haddock            self._host.run('rm %s' % script)
766aa97f6e69d7a58bb4e0b20fbd41bcadb42575b9aDavid Haddock            return os.path.join(resultdir, UPDATE_ENGINE_PERF_RESULTS_FILE)
767aa97f6e69d7a58bb4e0b20fbd41bcadb42575b9aDavid Haddock        except:
768aa97f6e69d7a58bb4e0b20fbd41bcadb42575b9aDavid Haddock            logging.debug('Failed to copy performance metrics from DUT.')
769aa97f6e69d7a58bb4e0b20fbd41bcadb42575b9aDavid Haddock            return None
770f1d1104c736f79145209c76a147c1334f8dbb6a9Gilad Arnold
771f1d1104c736f79145209c76a147c1334f8dbb6a9Gilad Arnold
772a0dee8465654b79d97cbc1cfca82c42fc95bd8b5David Haddock    def trigger_update(self, target_payload_uri):
773a0dee8465654b79d97cbc1cfca82c42fc95bd8b5David Haddock        logging.info('Updating device to target image.')
7740c13495f06d389adeabbb95f858c9b0c836d2492Gwendal Grignou        return self._install_version(target_payload_uri)
775f1d1104c736f79145209c76a147c1334f8dbb6a9Gilad Arnold
776f1d1104c736f79145209c76a147c1334f8dbb6a9Gilad Arnold    def finalize_update(self):
777a0dee8465654b79d97cbc1cfca82c42fc95bd8b5David Haddock        # Stateful update is controlled by cros_au
778a0dee8465654b79d97cbc1cfca82c42fc95bd8b5David Haddock        pass
779f1d1104c736f79145209c76a147c1334f8dbb6a9Gilad Arnold
780f1d1104c736f79145209c76a147c1334f8dbb6a9Gilad Arnold
781f1d1104c736f79145209c76a147c1334f8dbb6a9Gilad Arnold    def get_update_log(self, num_lines):
782f1d1104c736f79145209c76a147c1334f8dbb6a9Gilad Arnold        return self._host.run_output(
783fffb6d7274294d9a57523fb7535dd627d0378e5fGilad Arnold                'tail -n %d /var/log/update_engine.log' % num_lines,
784fffb6d7274294d9a57523fb7535dd627d0378e5fGilad Arnold                stdout_tee=None)
785f1d1104c736f79145209c76a147c1334f8dbb6a9Gilad Arnold
786f1d1104c736f79145209c76a147c1334f8dbb6a9Gilad Arnold
787a0dee8465654b79d97cbc1cfca82c42fc95bd8b5David Haddock    def check_device_after_update(self):
788f1d1104c736f79145209c76a147c1334f8dbb6a9Gilad Arnold        # Make sure we can login after update.
789a0dee8465654b79d97cbc1cfca82c42fc95bd8b5David Haddock        self._run_login_test('target_update')
790f1d1104c736f79145209c76a147c1334f8dbb6a9Gilad Arnold
791f1d1104c736f79145209c76a147c1334f8dbb6a9Gilad Arnold
79234618870e735e83db984401a54ca1eecd8e4ac14Niranjan Kumar    def oobe_triggers_update(self):
79334618870e735e83db984401a54ca1eecd8e4ac14Niranjan Kumar        return self._host.oobe_triggers_update()
79434618870e735e83db984401a54ca1eecd8e4ac14Niranjan Kumar
79534618870e735e83db984401a54ca1eecd8e4ac14Niranjan Kumar
796f1d1104c736f79145209c76a147c1334f8dbb6a9Gilad Arnoldclass autoupdate_EndToEndTest(test.test):
797f1d1104c736f79145209c76a147c1334f8dbb6a9Gilad Arnold    """Complete update test between two Chrome OS releases.
798f1d1104c736f79145209c76a147c1334f8dbb6a9Gilad Arnold
799f1d1104c736f79145209c76a147c1334f8dbb6a9Gilad Arnold    Performs an end-to-end test of updating a ChromeOS device from one version
800f1d1104c736f79145209c76a147c1334f8dbb6a9Gilad Arnold    to another. The test performs the following steps:
801f1d1104c736f79145209c76a147c1334f8dbb6a9Gilad Arnold
802f1d1104c736f79145209c76a147c1334f8dbb6a9Gilad Arnold      1. Stages the source (full) and target update payload on the central
803f1d1104c736f79145209c76a147c1334f8dbb6a9Gilad Arnold         devserver.
804a0dee8465654b79d97cbc1cfca82c42fc95bd8b5David Haddock      2. Installs a source image on the DUT (if provided) and reboots to it.
805a0dee8465654b79d97cbc1cfca82c42fc95bd8b5David Haddock      3. Then starts the target update by calling cros_au RPC on the devserver.
806a0dee8465654b79d97cbc1cfca82c42fc95bd8b5David Haddock      4. This copies the devserver code and all payloads to the DUT.
807a0dee8465654b79d97cbc1cfca82c42fc95bd8b5David Haddock      5. Starts a devserver on the DUT.
808a0dee8465654b79d97cbc1cfca82c42fc95bd8b5David Haddock      6. Starts an update pointing to this local devserver.
809a0dee8465654b79d97cbc1cfca82c42fc95bd8b5David Haddock      7. Watches as the DUT applies the update to rootfs and stateful.
810a0dee8465654b79d97cbc1cfca82c42fc95bd8b5David Haddock      8. Reboots and repeats steps 5-6, ensuring that the next update check
811f1d1104c736f79145209c76a147c1334f8dbb6a9Gilad Arnold         shows the new image version.
812a0dee8465654b79d97cbc1cfca82c42fc95bd8b5David Haddock      9. Returns the hostlogs collected during each update check for
813a0dee8465654b79d97cbc1cfca82c42fc95bd8b5David Haddock         verification against expected update events.
814f1d1104c736f79145209c76a147c1334f8dbb6a9Gilad Arnold
815f1d1104c736f79145209c76a147c1334f8dbb6a9Gilad Arnold    Some notes on naming:
816f1d1104c736f79145209c76a147c1334f8dbb6a9Gilad Arnold      devserver: Refers to a machine running the Chrome OS Update Devserver.
817f1d1104c736f79145209c76a147c1334f8dbb6a9Gilad Arnold      autotest_devserver: An autotest wrapper to interact with a devserver.
818f1d1104c736f79145209c76a147c1334f8dbb6a9Gilad Arnold                          Can be used to stage artifacts to a devserver. While
819f1d1104c736f79145209c76a147c1334f8dbb6a9Gilad Arnold                          this can also be used to update a machine, we do not
820f1d1104c736f79145209c76a147c1334f8dbb6a9Gilad Arnold                          use it for that purpose in this test as we manage
821f1d1104c736f79145209c76a147c1334f8dbb6a9Gilad Arnold                          updates with out own devserver instances (see below).
822f1d1104c736f79145209c76a147c1334f8dbb6a9Gilad Arnold      *staged_url's: In this case staged refers to the fact that these items
823f1d1104c736f79145209c76a147c1334f8dbb6a9Gilad Arnold                     are available to be downloaded statically from these urls
824f1d1104c736f79145209c76a147c1334f8dbb6a9Gilad Arnold                     e.g. 'localhost:8080/static/my_file.gz'. These are usually
825f1d1104c736f79145209c76a147c1334f8dbb6a9Gilad Arnold                     given after staging an artifact using a autotest_devserver
826f1d1104c736f79145209c76a147c1334f8dbb6a9Gilad Arnold                     though they can be re-created given enough assumptions.
827f1d1104c736f79145209c76a147c1334f8dbb6a9Gilad Arnold    """
828f1d1104c736f79145209c76a147c1334f8dbb6a9Gilad Arnold    version = 1
829f1d1104c736f79145209c76a147c1334f8dbb6a9Gilad Arnold
830f1d1104c736f79145209c76a147c1334f8dbb6a9Gilad Arnold    # Timeout periods, given in seconds.
831f1d1104c736f79145209c76a147c1334f8dbb6a9Gilad Arnold    _WAIT_FOR_INITIAL_UPDATE_CHECK_SECONDS = 12 * 60
832f1d1104c736f79145209c76a147c1334f8dbb6a9Gilad Arnold    # TODO(sosa): Investigate why this needs to be so long (this used to be
833f1d1104c736f79145209c76a147c1334f8dbb6a9Gilad Arnold    # 120 and regressed).
834f1d1104c736f79145209c76a147c1334f8dbb6a9Gilad Arnold    _WAIT_FOR_DOWNLOAD_STARTED_SECONDS = 4 * 60
835f3c5dcd63105ae2f98a9dbb4da1cdbeacd49e832Grant Grundler    # See https://crbug.com/731214 before changing WAIT_FOR_DOWNLOAD
836f3c5dcd63105ae2f98a9dbb4da1cdbeacd49e832Grant Grundler    _WAIT_FOR_DOWNLOAD_COMPLETED_SECONDS = 20 * 60
837f1d1104c736f79145209c76a147c1334f8dbb6a9Gilad Arnold    _WAIT_FOR_UPDATE_COMPLETED_SECONDS = 4 * 60
838f1d1104c736f79145209c76a147c1334f8dbb6a9Gilad Arnold    _WAIT_FOR_UPDATE_CHECK_AFTER_REBOOT_SECONDS = 15 * 60
839f1d1104c736f79145209c76a147c1334f8dbb6a9Gilad Arnold
840a0ca5707ed10a6575ed290f341294331455f7769Gilad Arnold    # Logs and their whereabouts.
841a0ca5707ed10a6575ed290f341294331455f7769Gilad Arnold    _WHERE_UPDATE_LOG = ('update_engine log (in sysinfo or on the DUT, also '
842a0ca5707ed10a6575ed290f341294331455f7769Gilad Arnold                         'included in the test log)')
843a0ca5707ed10a6575ed290f341294331455f7769Gilad Arnold    _WHERE_OMAHA_LOG = 'Omaha-devserver log (included in the test log)'
844a0ca5707ed10a6575ed290f341294331455f7769Gilad Arnold
845f1d1104c736f79145209c76a147c1334f8dbb6a9Gilad Arnold
846f1d1104c736f79145209c76a147c1334f8dbb6a9Gilad Arnold    def initialize(self):
847f1d1104c736f79145209c76a147c1334f8dbb6a9Gilad Arnold        """Sets up variables that will be used by test."""
848f1d1104c736f79145209c76a147c1334f8dbb6a9Gilad Arnold        self._host = None
849f1d1104c736f79145209c76a147c1334f8dbb6a9Gilad Arnold        self._omaha_devserver = None
850a0ca5707ed10a6575ed290f341294331455f7769Gilad Arnold        self._source_image_installed = False
851f1d1104c736f79145209c76a147c1334f8dbb6a9Gilad Arnold
852f1d1104c736f79145209c76a147c1334f8dbb6a9Gilad Arnold
853f1d1104c736f79145209c76a147c1334f8dbb6a9Gilad Arnold    def cleanup(self):
854f1d1104c736f79145209c76a147c1334f8dbb6a9Gilad Arnold        """Kill the omaha devserver if it's still around."""
855f1d1104c736f79145209c76a147c1334f8dbb6a9Gilad Arnold        if self._omaha_devserver:
856f1d1104c736f79145209c76a147c1334f8dbb6a9Gilad Arnold            self._omaha_devserver.stop_devserver()
857f1d1104c736f79145209c76a147c1334f8dbb6a9Gilad Arnold
858f1d1104c736f79145209c76a147c1334f8dbb6a9Gilad Arnold        self._omaha_devserver = None
859f1d1104c736f79145209c76a147c1334f8dbb6a9Gilad Arnold
860f1d1104c736f79145209c76a147c1334f8dbb6a9Gilad Arnold
861ab2884bab13c08f21aaa780a1861959131a6f682David Haddock    def _get_hostlog_file(self, filename, pid):
862ab2884bab13c08f21aaa780a1861959131a6f682David Haddock        """Return the hostlog file location.
863ab2884bab13c08f21aaa780a1861959131a6f682David Haddock
864ab2884bab13c08f21aaa780a1861959131a6f682David Haddock        @param filename: The partial filename to look for.
865ab2884bab13c08f21aaa780a1861959131a6f682David Haddock        @param pid: The pid of the update.
866ab2884bab13c08f21aaa780a1861959131a6f682David Haddock
867ab2884bab13c08f21aaa780a1861959131a6f682David Haddock        """
868ab2884bab13c08f21aaa780a1861959131a6f682David Haddock        hosts = [self._host.hostname, self._host.ip]
869ab2884bab13c08f21aaa780a1861959131a6f682David Haddock        for host in hosts:
870ab2884bab13c08f21aaa780a1861959131a6f682David Haddock            hostlog = '%s_%s_%s' % (filename, host, pid)
871ab2884bab13c08f21aaa780a1861959131a6f682David Haddock            file_url = os.path.join(self.job.resultdir,
872ab2884bab13c08f21aaa780a1861959131a6f682David Haddock                                    dev_server.AUTO_UPDATE_LOG_DIR,
873ab2884bab13c08f21aaa780a1861959131a6f682David Haddock                                    hostlog)
874ab2884bab13c08f21aaa780a1861959131a6f682David Haddock            if os.path.exists(file_url):
875ab2884bab13c08f21aaa780a1861959131a6f682David Haddock                return file_url
876ab2884bab13c08f21aaa780a1861959131a6f682David Haddock        raise error.TestFail('Could not find %s for pid %s' % (filename, pid))
877ab2884bab13c08f21aaa780a1861959131a6f682David Haddock
878ab2884bab13c08f21aaa780a1861959131a6f682David Haddock
879f1d1104c736f79145209c76a147c1334f8dbb6a9Gilad Arnold    def _dump_update_engine_log(self, test_platform):
880f1d1104c736f79145209c76a147c1334f8dbb6a9Gilad Arnold        """Dumps relevant AU error log."""
881f1d1104c736f79145209c76a147c1334f8dbb6a9Gilad Arnold        try:
882fffb6d7274294d9a57523fb7535dd627d0378e5fGilad Arnold            error_log = test_platform.get_update_log(80)
883fffb6d7274294d9a57523fb7535dd627d0378e5fGilad Arnold            logging.error('Dumping snippet of update_engine log:\n%s',
884fffb6d7274294d9a57523fb7535dd627d0378e5fGilad Arnold                          snippet(error_log))
885f1d1104c736f79145209c76a147c1334f8dbb6a9Gilad Arnold        except Exception:
886f1d1104c736f79145209c76a147c1334f8dbb6a9Gilad Arnold            # Mute any exceptions we get printing debug logs.
887f1d1104c736f79145209c76a147c1334f8dbb6a9Gilad Arnold            pass
888f1d1104c736f79145209c76a147c1334f8dbb6a9Gilad Arnold
889f1d1104c736f79145209c76a147c1334f8dbb6a9Gilad Arnold
890aa97f6e69d7a58bb4e0b20fbd41bcadb42575b9aDavid Haddock    def _report_perf_data(self, perf_file):
89131887b7a4ea7e1517d468c2ccf94df14ba6d0315David Zeuthen        """Reports performance and resource data.
89231887b7a4ea7e1517d468c2ccf94df14ba6d0315David Zeuthen
893f1d1104c736f79145209c76a147c1334f8dbb6a9Gilad Arnold        Currently, performance attributes are expected to include 'rss_peak'
894f1d1104c736f79145209c76a147c1334f8dbb6a9Gilad Arnold        (peak memory usage in bytes).
895f1d1104c736f79145209c76a147c1334f8dbb6a9Gilad Arnold
896aa97f6e69d7a58bb4e0b20fbd41bcadb42575b9aDavid Haddock        @param perf_file: A file with performance metrics.
89731887b7a4ea7e1517d468c2ccf94df14ba6d0315David Zeuthen        """
898aa97f6e69d7a58bb4e0b20fbd41bcadb42575b9aDavid Haddock        logging.debug('Reading perf results from %s.' % perf_file)
899aa97f6e69d7a58bb4e0b20fbd41bcadb42575b9aDavid Haddock        try:
900aa97f6e69d7a58bb4e0b20fbd41bcadb42575b9aDavid Haddock            with open(perf_file, 'r') as perf_file_handle:
901aa97f6e69d7a58bb4e0b20fbd41bcadb42575b9aDavid Haddock                perf_data = json.loads(perf_file_handle.read())
902aa97f6e69d7a58bb4e0b20fbd41bcadb42575b9aDavid Haddock        except Exception as e:
903aa97f6e69d7a58bb4e0b20fbd41bcadb42575b9aDavid Haddock            logging.warning('Error while reading the perf data file: %s' % e)
904aa97f6e69d7a58bb4e0b20fbd41bcadb42575b9aDavid Haddock            return
905aa97f6e69d7a58bb4e0b20fbd41bcadb42575b9aDavid Haddock
90631887b7a4ea7e1517d468c2ccf94df14ba6d0315David Zeuthen        rss_peak = perf_data.get('rss_peak')
90731887b7a4ea7e1517d468c2ccf94df14ba6d0315David Zeuthen        if rss_peak:
90831887b7a4ea7e1517d468c2ccf94df14ba6d0315David Zeuthen            rss_peak_kib = rss_peak / 1024
90931887b7a4ea7e1517d468c2ccf94df14ba6d0315David Zeuthen            logging.info('Peak memory (RSS) usage on DUT: %d KiB', rss_peak_kib)
91031887b7a4ea7e1517d468c2ccf94df14ba6d0315David Zeuthen            self.output_perf_value(description='mem_usage_peak',
91131887b7a4ea7e1517d468c2ccf94df14ba6d0315David Zeuthen                                   value=int(rss_peak_kib),
91231887b7a4ea7e1517d468c2ccf94df14ba6d0315David Zeuthen                                   units='KiB',
91331887b7a4ea7e1517d468c2ccf94df14ba6d0315David Zeuthen                                   higher_is_better=False)
91431887b7a4ea7e1517d468c2ccf94df14ba6d0315David Zeuthen        else:
915aa97f6e69d7a58bb4e0b20fbd41bcadb42575b9aDavid Haddock            logging.warning('No rss_peak key in JSON returned by %s',
916aa97f6e69d7a58bb4e0b20fbd41bcadb42575b9aDavid Haddock                            UPDATE_ENGINE_PERF_SCRIPT)
91731887b7a4ea7e1517d468c2ccf94df14ba6d0315David Zeuthen
91831887b7a4ea7e1517d468c2ccf94df14ba6d0315David Zeuthen
919a0ca5707ed10a6575ed290f341294331455f7769Gilad Arnold    def _error_initial_check(self, expected, actual, mismatched_attrs):
920a0ca5707ed10a6575ed290f341294331455f7769Gilad Arnold        if 'version' in mismatched_attrs:
921a0ca5707ed10a6575ed290f341294331455f7769Gilad Arnold            err_msg = ('Initial update check was received but the reported '
922a0ca5707ed10a6575ed290f341294331455f7769Gilad Arnold                       'version is different from what was expected.')
923a0ca5707ed10a6575ed290f341294331455f7769Gilad Arnold            if self._source_image_installed:
924a0ca5707ed10a6575ed290f341294331455f7769Gilad Arnold                err_msg += (' The source payload we installed was probably '
925a0ca5707ed10a6575ed290f341294331455f7769Gilad Arnold                            'incorrect or corrupt.')
926a0ca5707ed10a6575ed290f341294331455f7769Gilad Arnold            else:
927a0ca5707ed10a6575ed290f341294331455f7769Gilad Arnold                err_msg += (' The DUT is probably not running the correct '
928a0ca5707ed10a6575ed290f341294331455f7769Gilad Arnold                            'source image.')
929a0ca5707ed10a6575ed290f341294331455f7769Gilad Arnold            return err_msg
930a0ca5707ed10a6575ed290f341294331455f7769Gilad Arnold
931a0ca5707ed10a6575ed290f341294331455f7769Gilad Arnold        return 'A test bug occurred; inspect the test log.'
932a0ca5707ed10a6575ed290f341294331455f7769Gilad Arnold
933a0ca5707ed10a6575ed290f341294331455f7769Gilad Arnold
934a0ca5707ed10a6575ed290f341294331455f7769Gilad Arnold    def _error_intermediate(self, expected, actual, mismatched_attrs, action,
935a0ca5707ed10a6575ed290f341294331455f7769Gilad Arnold                            problem):
936a0ca5707ed10a6575ed290f341294331455f7769Gilad Arnold        if 'event_result' in mismatched_attrs:
9370ba4fcd882643427524c35522bf77959b57c6f48Gilad Arnold            event_result = actual.get('event_result')
9380ba4fcd882643427524c35522bf77959b57c6f48Gilad Arnold            reported = (('different than expected (%s)' %
9390ba4fcd882643427524c35522bf77959b57c6f48Gilad Arnold                         EVENT_RESULT_DICT[event_result])
9400ba4fcd882643427524c35522bf77959b57c6f48Gilad Arnold                        if event_result else 'missing')
9410ba4fcd882643427524c35522bf77959b57c6f48Gilad Arnold            return ('The updater reported result code is %s. This could be an '
9420ba4fcd882643427524c35522bf77959b57c6f48Gilad Arnold                    'updater bug or a connectivity problem; check the %s. For '
9430ba4fcd882643427524c35522bf77959b57c6f48Gilad Arnold                    'a detailed log of update events, check the %s.' %
9440ba4fcd882643427524c35522bf77959b57c6f48Gilad Arnold                    (reported, self._WHERE_UPDATE_LOG, self._WHERE_OMAHA_LOG))
945a0ca5707ed10a6575ed290f341294331455f7769Gilad Arnold        if 'event_type' in mismatched_attrs:
9460ba4fcd882643427524c35522bf77959b57c6f48Gilad Arnold            event_type = actual.get('event_type')
9470ba4fcd882643427524c35522bf77959b57c6f48Gilad Arnold            reported = ('different (%s)' % EVENT_TYPE_DICT[event_type]
9480ba4fcd882643427524c35522bf77959b57c6f48Gilad Arnold                        if event_type else 'missing')
9490ba4fcd882643427524c35522bf77959b57c6f48Gilad Arnold            return ('Expected the updater to %s (%s) but received event type '
9500ba4fcd882643427524c35522bf77959b57c6f48Gilad Arnold                    'is %s. This could be an updater %s; check the '
951a0ca5707ed10a6575ed290f341294331455f7769Gilad Arnold                    '%s. For a detailed log of update events, check the %s.' %
9520ba4fcd882643427524c35522bf77959b57c6f48Gilad Arnold                    (action, EVENT_TYPE_DICT[expected['event_type']], reported,
9530ba4fcd882643427524c35522bf77959b57c6f48Gilad Arnold                     problem, self._WHERE_UPDATE_LOG, self._WHERE_OMAHA_LOG))
954a0ca5707ed10a6575ed290f341294331455f7769Gilad Arnold        if 'version' in mismatched_attrs:
955a0ca5707ed10a6575ed290f341294331455f7769Gilad Arnold            return ('The updater reported an unexpected version despite '
956a0ca5707ed10a6575ed290f341294331455f7769Gilad Arnold                    'previously reporting the correct one. This is most likely '
957a0ca5707ed10a6575ed290f341294331455f7769Gilad Arnold                    'a bug in update engine; check the %s.' %
958a0ca5707ed10a6575ed290f341294331455f7769Gilad Arnold                    self._WHERE_UPDATE_LOG)
959a0ca5707ed10a6575ed290f341294331455f7769Gilad Arnold
960a0ca5707ed10a6575ed290f341294331455f7769Gilad Arnold        return 'A test bug occurred; inspect the test log.'
961a0ca5707ed10a6575ed290f341294331455f7769Gilad Arnold
962a0ca5707ed10a6575ed290f341294331455f7769Gilad Arnold
963a0ca5707ed10a6575ed290f341294331455f7769Gilad Arnold    def _error_download_started(self, expected, actual, mismatched_attrs):
964a0ca5707ed10a6575ed290f341294331455f7769Gilad Arnold        return self._error_intermediate(expected, actual, mismatched_attrs,
965a0ca5707ed10a6575ed290f341294331455f7769Gilad Arnold                                        'begin downloading',
966a0ca5707ed10a6575ed290f341294331455f7769Gilad Arnold                                        'bug, crash or provisioning error')
967a0ca5707ed10a6575ed290f341294331455f7769Gilad Arnold
968a0ca5707ed10a6575ed290f341294331455f7769Gilad Arnold
969a0ca5707ed10a6575ed290f341294331455f7769Gilad Arnold    def _error_download_finished(self, expected, actual, mismatched_attrs):
970a0ca5707ed10a6575ed290f341294331455f7769Gilad Arnold        return self._error_intermediate(expected, actual, mismatched_attrs,
971a0ca5707ed10a6575ed290f341294331455f7769Gilad Arnold                                        'finish downloading', 'bug or crash')
972a0ca5707ed10a6575ed290f341294331455f7769Gilad Arnold
973a0ca5707ed10a6575ed290f341294331455f7769Gilad Arnold
974a0ca5707ed10a6575ed290f341294331455f7769Gilad Arnold    def _error_update_complete(self, expected, actual, mismatched_attrs):
975a0ca5707ed10a6575ed290f341294331455f7769Gilad Arnold        return self._error_intermediate(expected, actual, mismatched_attrs,
976a0ca5707ed10a6575ed290f341294331455f7769Gilad Arnold                                        'complete the update', 'bug or crash')
977a0ca5707ed10a6575ed290f341294331455f7769Gilad Arnold
978a0ca5707ed10a6575ed290f341294331455f7769Gilad Arnold
97916e76893c34739649a751f42b3881b57620d665cGilad Arnold    def _error_reboot_after_update(self, expected, actual, mismatched_attrs):
980a0ca5707ed10a6575ed290f341294331455f7769Gilad Arnold        if 'event_result' in mismatched_attrs:
9810ba4fcd882643427524c35522bf77959b57c6f48Gilad Arnold            event_result = actual.get('event_result')
9820ba4fcd882643427524c35522bf77959b57c6f48Gilad Arnold            reported = ('different (%s)' % EVENT_RESULT_DICT[event_result]
9830ba4fcd882643427524c35522bf77959b57c6f48Gilad Arnold                        if event_result else 'missing')
984a0ca5707ed10a6575ed290f341294331455f7769Gilad Arnold            return ('The updater was expected to reboot (%s) but reported '
9850ba4fcd882643427524c35522bf77959b57c6f48Gilad Arnold                    'result code is %s. This could be a failure to reboot, an '
9860ba4fcd882643427524c35522bf77959b57c6f48Gilad Arnold                    'updater bug or a connectivity problem; check the %s and '
9870ba4fcd882643427524c35522bf77959b57c6f48Gilad Arnold                    'the system log. For a detailed log of update events, '
9880ba4fcd882643427524c35522bf77959b57c6f48Gilad Arnold                    'check the %s.' %
9890ba4fcd882643427524c35522bf77959b57c6f48Gilad Arnold                    (EVENT_RESULT_DICT[expected['event_result']], reported,
990a0ca5707ed10a6575ed290f341294331455f7769Gilad Arnold                     self._WHERE_UPDATE_LOG, self._WHERE_OMAHA_LOG))
991a0ca5707ed10a6575ed290f341294331455f7769Gilad Arnold        if 'event_type' in mismatched_attrs:
9920ba4fcd882643427524c35522bf77959b57c6f48Gilad Arnold            event_type = actual.get('event_type')
9930ba4fcd882643427524c35522bf77959b57c6f48Gilad Arnold            reported = ('different (%s)' % EVENT_TYPE_DICT[event_type]
9940ba4fcd882643427524c35522bf77959b57c6f48Gilad Arnold                        if event_type else 'missing')
995a0ca5707ed10a6575ed290f341294331455f7769Gilad Arnold            return ('Expected to successfully reboot into the new image (%s) '
9960ba4fcd882643427524c35522bf77959b57c6f48Gilad Arnold                    'but received event type is %s. This probably means that '
9970ba4fcd882643427524c35522bf77959b57c6f48Gilad Arnold                    'the new image failed to verify after reboot, possibly '
9980ba4fcd882643427524c35522bf77959b57c6f48Gilad Arnold                    'because the payload is corrupt. This might also be an '
9990ba4fcd882643427524c35522bf77959b57c6f48Gilad Arnold                    'updater bug or crash; check the %s. For a detailed log of '
10000ba4fcd882643427524c35522bf77959b57c6f48Gilad Arnold                    'update events, check the %s.' %
10010ba4fcd882643427524c35522bf77959b57c6f48Gilad Arnold                    (EVENT_TYPE_DICT[expected['event_type']], reported,
1002a0ca5707ed10a6575ed290f341294331455f7769Gilad Arnold                     self._WHERE_UPDATE_LOG, self._WHERE_OMAHA_LOG))
1003a0ca5707ed10a6575ed290f341294331455f7769Gilad Arnold        if 'version' in mismatched_attrs:
1004a0ca5707ed10a6575ed290f341294331455f7769Gilad Arnold            return ('The DUT rebooted after the update but reports a different '
1005a0ca5707ed10a6575ed290f341294331455f7769Gilad Arnold                    'image version than the one expected. This probably means '
1006a0ca5707ed10a6575ed290f341294331455f7769Gilad Arnold                    'that the payload we applied was incorrect or corrupt.')
1007a0ca5707ed10a6575ed290f341294331455f7769Gilad Arnold        if 'previous_version' in mismatched_attrs:
1008a0ca5707ed10a6575ed290f341294331455f7769Gilad Arnold            return ('The DUT rebooted after the update and reports the '
1009a0ca5707ed10a6575ed290f341294331455f7769Gilad Arnold                    'expected version. However, it reports a previous version '
1010a0ca5707ed10a6575ed290f341294331455f7769Gilad Arnold                    'that is different from the one previously reported. This '
1011a0ca5707ed10a6575ed290f341294331455f7769Gilad Arnold                    'is most likely a bug in update engine; check the %s.' %
1012a0ca5707ed10a6575ed290f341294331455f7769Gilad Arnold                    self._WHERE_UPDATE_LOG)
1013a0ca5707ed10a6575ed290f341294331455f7769Gilad Arnold
1014a0ca5707ed10a6575ed290f341294331455f7769Gilad Arnold        return 'A test bug occurred; inspect the test log.'
1015a0ca5707ed10a6575ed290f341294331455f7769Gilad Arnold
1016a0ca5707ed10a6575ed290f341294331455f7769Gilad Arnold
1017a0ca5707ed10a6575ed290f341294331455f7769Gilad Arnold    def _timeout_err(self, desc, timeout, event_type=None):
1018a0ca5707ed10a6575ed290f341294331455f7769Gilad Arnold        if event_type is not None:
1019a0ca5707ed10a6575ed290f341294331455f7769Gilad Arnold            desc += ' (%s)' % EVENT_TYPE_DICT[event_type]
1020a0ca5707ed10a6575ed290f341294331455f7769Gilad Arnold        return ('Failed to receive %s within %d seconds. This could be a '
1021a0ca5707ed10a6575ed290f341294331455f7769Gilad Arnold                'problem with the updater or a connectivity issue. For more '
1022a0ca5707ed10a6575ed290f341294331455f7769Gilad Arnold                'details, check the %s.' %
1023a0ca5707ed10a6575ed290f341294331455f7769Gilad Arnold                (desc, timeout, self._WHERE_UPDATE_LOG))
1024a0ca5707ed10a6575ed290f341294331455f7769Gilad Arnold
1025a0ca5707ed10a6575ed290f341294331455f7769Gilad Arnold
1026f1d1104c736f79145209c76a147c1334f8dbb6a9Gilad Arnold    def run_update_test(self, test_platform, test_conf):
1027ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa        """Runs the actual update test once preconditions are met.
10280ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
1029f1d1104c736f79145209c76a147c1334f8dbb6a9Gilad Arnold        @param test_platform: TestPlatform implementation.
1030ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa        @param test_conf: A dictionary containing test configuration values
10310ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
1032ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa        @raises ExpectedUpdateEventChainFailed if we failed to verify an update
1033ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa                event.
10340ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold        """
1035c6ed2f633a1c4609a4b212da66b3e506008ee3a7Nam T. Nguyen
1036f1d1104c736f79145209c76a147c1334f8dbb6a9Gilad Arnold        # Record the active root partition.
1037f1d1104c736f79145209c76a147c1334f8dbb6a9Gilad Arnold        source_active_slot = test_platform.get_active_slot()
1038f1d1104c736f79145209c76a147c1334f8dbb6a9Gilad Arnold        logging.info('Source active slot: %s', source_active_slot)
10390ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
10407ac0a621527b1ecb4b82fea910c730682f9c1f76Gilad Arnold        source_release = test_conf['source_release']
10417ac0a621527b1ecb4b82fea910c730682f9c1f76Gilad Arnold        target_release = test_conf['target_release']
10427ac0a621527b1ecb4b82fea910c730682f9c1f76Gilad Arnold
1043aa97f6e69d7a58bb4e0b20fbd41bcadb42575b9aDavid Haddock        test_platform.start_update_perf(self.bindir)
104431887b7a4ea7e1517d468c2ccf94df14ba6d0315David Zeuthen        try:
1045a0dee8465654b79d97cbc1cfca82c42fc95bd8b5David Haddock            # Update the DUT to the target image.
1046a0dee8465654b79d97cbc1cfca82c42fc95bd8b5David Haddock            pid = test_platform.trigger_update(test_conf['target_payload_uri'])
1047a0dee8465654b79d97cbc1cfca82c42fc95bd8b5David Haddock
1048a0dee8465654b79d97cbc1cfca82c42fc95bd8b5David Haddock            # Verify the host log that was returned from the update.
1049ab2884bab13c08f21aaa780a1861959131a6f682David Haddock            file_url = self._get_hostlog_file('devserver_hostlog_rootfs', pid)
1050a0dee8465654b79d97cbc1cfca82c42fc95bd8b5David Haddock
1051a0dee8465654b79d97cbc1cfca82c42fc95bd8b5David Haddock            logging.info('Checking update steps with devserver hostlog file: '
1052a0dee8465654b79d97cbc1cfca82c42fc95bd8b5David Haddock                         '%s' % file_url)
1053a0dee8465654b79d97cbc1cfca82c42fc95bd8b5David Haddock            log_verifier = UpdateEventLogVerifier(file_url)
105431887b7a4ea7e1517d468c2ccf94df14ba6d0315David Zeuthen
105531887b7a4ea7e1517d468c2ccf94df14ba6d0315David Zeuthen            # Verify chain of events in a successful update process.
1056248108c1c925791d926105f42212b1033213a4dcGilad Arnold            chain = ExpectedUpdateEventChain()
1057248108c1c925791d926105f42212b1033213a4dcGilad Arnold            chain.add_event(
1058248108c1c925791d926105f42212b1033213a4dcGilad Arnold                    ExpectedUpdateEvent(
10597ac0a621527b1ecb4b82fea910c730682f9c1f76Gilad Arnold                        version=source_release,
1060a0ca5707ed10a6575ed290f341294331455f7769Gilad Arnold                        on_error=self._error_initial_check),
1061a0ca5707ed10a6575ed290f341294331455f7769Gilad Arnold                    self._WAIT_FOR_INITIAL_UPDATE_CHECK_SECONDS,
1062a0ca5707ed10a6575ed290f341294331455f7769Gilad Arnold                    on_timeout=self._timeout_err(
1063a0ca5707ed10a6575ed290f341294331455f7769Gilad Arnold                            'an initial update check',
1064a0ca5707ed10a6575ed290f341294331455f7769Gilad Arnold                            self._WAIT_FOR_INITIAL_UPDATE_CHECK_SECONDS))
1065248108c1c925791d926105f42212b1033213a4dcGilad Arnold            chain.add_event(
1066248108c1c925791d926105f42212b1033213a4dcGilad Arnold                    ExpectedUpdateEvent(
1067248108c1c925791d926105f42212b1033213a4dcGilad Arnold                        event_type=EVENT_TYPE_DOWNLOAD_STARTED,
1068248108c1c925791d926105f42212b1033213a4dcGilad Arnold                        event_result=EVENT_RESULT_SUCCESS,
10697ac0a621527b1ecb4b82fea910c730682f9c1f76Gilad Arnold                        version=source_release,
1070a0ca5707ed10a6575ed290f341294331455f7769Gilad Arnold                        on_error=self._error_download_started),
1071a0ca5707ed10a6575ed290f341294331455f7769Gilad Arnold                    self._WAIT_FOR_DOWNLOAD_STARTED_SECONDS,
1072a0ca5707ed10a6575ed290f341294331455f7769Gilad Arnold                    on_timeout=self._timeout_err(
1073a0ca5707ed10a6575ed290f341294331455f7769Gilad Arnold                            'a download started notification',
1074a0ca5707ed10a6575ed290f341294331455f7769Gilad Arnold                            self._WAIT_FOR_DOWNLOAD_STARTED_SECONDS,
1075a0ca5707ed10a6575ed290f341294331455f7769Gilad Arnold                            event_type=EVENT_TYPE_DOWNLOAD_STARTED))
1076248108c1c925791d926105f42212b1033213a4dcGilad Arnold            chain.add_event(
1077248108c1c925791d926105f42212b1033213a4dcGilad Arnold                    ExpectedUpdateEvent(
1078248108c1c925791d926105f42212b1033213a4dcGilad Arnold                        event_type=EVENT_TYPE_DOWNLOAD_FINISHED,
1079248108c1c925791d926105f42212b1033213a4dcGilad Arnold                        event_result=EVENT_RESULT_SUCCESS,
10807ac0a621527b1ecb4b82fea910c730682f9c1f76Gilad Arnold                        version=source_release,
1081a0ca5707ed10a6575ed290f341294331455f7769Gilad Arnold                        on_error=self._error_download_finished),
1082a0ca5707ed10a6575ed290f341294331455f7769Gilad Arnold                    self._WAIT_FOR_DOWNLOAD_COMPLETED_SECONDS,
1083a0ca5707ed10a6575ed290f341294331455f7769Gilad Arnold                    on_timeout=self._timeout_err(
1084a0ca5707ed10a6575ed290f341294331455f7769Gilad Arnold                            'a download finished notification',
1085a0ca5707ed10a6575ed290f341294331455f7769Gilad Arnold                            self._WAIT_FOR_DOWNLOAD_COMPLETED_SECONDS,
1086a0ca5707ed10a6575ed290f341294331455f7769Gilad Arnold                            event_type=EVENT_TYPE_DOWNLOAD_FINISHED))
1087248108c1c925791d926105f42212b1033213a4dcGilad Arnold            chain.add_event(
1088248108c1c925791d926105f42212b1033213a4dcGilad Arnold                    ExpectedUpdateEvent(
1089248108c1c925791d926105f42212b1033213a4dcGilad Arnold                        event_type=EVENT_TYPE_UPDATE_COMPLETE,
1090248108c1c925791d926105f42212b1033213a4dcGilad Arnold                        event_result=EVENT_RESULT_SUCCESS,
10917ac0a621527b1ecb4b82fea910c730682f9c1f76Gilad Arnold                        version=source_release,
1092a0ca5707ed10a6575ed290f341294331455f7769Gilad Arnold                        on_error=self._error_update_complete),
1093a0ca5707ed10a6575ed290f341294331455f7769Gilad Arnold                    self._WAIT_FOR_UPDATE_COMPLETED_SECONDS,
1094a0ca5707ed10a6575ed290f341294331455f7769Gilad Arnold                    on_timeout=self._timeout_err(
1095a0ca5707ed10a6575ed290f341294331455f7769Gilad Arnold                            'an update complete notification',
1096a0ca5707ed10a6575ed290f341294331455f7769Gilad Arnold                            self._WAIT_FOR_UPDATE_COMPLETED_SECONDS,
1097a0ca5707ed10a6575ed290f341294331455f7769Gilad Arnold                            event_type=EVENT_TYPE_UPDATE_COMPLETE))
109831887b7a4ea7e1517d468c2ccf94df14ba6d0315David Zeuthen
1099fac9a5d238337da91939328386c2fa4fdfb6d957Alex Deymo            log_verifier.verify_expected_events_chain(chain)
110031887b7a4ea7e1517d468c2ccf94df14ba6d0315David Zeuthen
1101a0dee8465654b79d97cbc1cfca82c42fc95bd8b5David Haddock        except:
1102a0dee8465654b79d97cbc1cfca82c42fc95bd8b5David Haddock            logging.fatal('ERROR: Failure occurred during the target update.')
1103a0dee8465654b79d97cbc1cfca82c42fc95bd8b5David Haddock            raise
11040ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
1105aa97f6e69d7a58bb4e0b20fbd41bcadb42575b9aDavid Haddock        perf_file = test_platform.stop_update_perf(self.job.resultdir)
1106aa97f6e69d7a58bb4e0b20fbd41bcadb42575b9aDavid Haddock        if perf_file is not None:
1107aa97f6e69d7a58bb4e0b20fbd41bcadb42575b9aDavid Haddock            self._report_perf_data(perf_file)
1108aa97f6e69d7a58bb4e0b20fbd41bcadb42575b9aDavid Haddock
110934618870e735e83db984401a54ca1eecd8e4ac14Niranjan Kumar        if test_platform.oobe_triggers_update():
111034618870e735e83db984401a54ca1eecd8e4ac14Niranjan Kumar            # If DUT automatically checks for update during OOBE,
111134618870e735e83db984401a54ca1eecd8e4ac14Niranjan Kumar            # checking the post-update CrOS version and slot is sufficient.
111234618870e735e83db984401a54ca1eecd8e4ac14Niranjan Kumar            # This command checks the OS version.
111334618870e735e83db984401a54ca1eecd8e4ac14Niranjan Kumar            # The slot is checked a little later, after the else block.
1114a0dee8465654b79d97cbc1cfca82c42fc95bd8b5David Haddock            logging.info('Skipping post reboot update check.')
111534618870e735e83db984401a54ca1eecd8e4ac14Niranjan Kumar            test_platform.verify_version(target_release)
111634618870e735e83db984401a54ca1eecd8e4ac14Niranjan Kumar        else:
111734618870e735e83db984401a54ca1eecd8e4ac14Niranjan Kumar            # Observe post-reboot update check, which should indicate that the
111834618870e735e83db984401a54ca1eecd8e4ac14Niranjan Kumar            # image version has been updated.
1119a0dee8465654b79d97cbc1cfca82c42fc95bd8b5David Haddock            # Verify the host log that was returned from the update.
1120ab2884bab13c08f21aaa780a1861959131a6f682David Haddock            file_url = self._get_hostlog_file('devserver_hostlog_reboot', pid)
1121a0dee8465654b79d97cbc1cfca82c42fc95bd8b5David Haddock
1122a0dee8465654b79d97cbc1cfca82c42fc95bd8b5David Haddock            logging.info('Checking post-reboot devserver hostlogs: %s' %
1123a0dee8465654b79d97cbc1cfca82c42fc95bd8b5David Haddock                         file_url)
1124a0dee8465654b79d97cbc1cfca82c42fc95bd8b5David Haddock            log_verifier = UpdateEventLogVerifier(file_url)
1125a0dee8465654b79d97cbc1cfca82c42fc95bd8b5David Haddock
112634618870e735e83db984401a54ca1eecd8e4ac14Niranjan Kumar            chain = ExpectedUpdateEventChain()
112734618870e735e83db984401a54ca1eecd8e4ac14Niranjan Kumar            expected_events = [
112834618870e735e83db984401a54ca1eecd8e4ac14Niranjan Kumar                ExpectedUpdateEvent(
112934618870e735e83db984401a54ca1eecd8e4ac14Niranjan Kumar                    event_type=EVENT_TYPE_UPDATE_COMPLETE,
113034618870e735e83db984401a54ca1eecd8e4ac14Niranjan Kumar                    event_result=EVENT_RESULT_SUCCESS_REBOOT,
113134618870e735e83db984401a54ca1eecd8e4ac14Niranjan Kumar                    version=target_release,
113234618870e735e83db984401a54ca1eecd8e4ac14Niranjan Kumar                    previous_version=source_release,
113334618870e735e83db984401a54ca1eecd8e4ac14Niranjan Kumar                    on_error=self._error_reboot_after_update),
113434618870e735e83db984401a54ca1eecd8e4ac14Niranjan Kumar                # Newer versions send a "rebooted_after_update" message
113534618870e735e83db984401a54ca1eecd8e4ac14Niranjan Kumar                # after reboot with the previous version instead of another
113634618870e735e83db984401a54ca1eecd8e4ac14Niranjan Kumar                # "update_complete".
113734618870e735e83db984401a54ca1eecd8e4ac14Niranjan Kumar                ExpectedUpdateEvent(
113834618870e735e83db984401a54ca1eecd8e4ac14Niranjan Kumar                    event_type=EVENT_TYPE_REBOOTED_AFTER_UPDATE,
113934618870e735e83db984401a54ca1eecd8e4ac14Niranjan Kumar                    event_result=EVENT_RESULT_SUCCESS,
114034618870e735e83db984401a54ca1eecd8e4ac14Niranjan Kumar                    version=target_release,
114134618870e735e83db984401a54ca1eecd8e4ac14Niranjan Kumar                    previous_version=source_release,
114234618870e735e83db984401a54ca1eecd8e4ac14Niranjan Kumar                    on_error=self._error_reboot_after_update),
114334618870e735e83db984401a54ca1eecd8e4ac14Niranjan Kumar            ]
114434618870e735e83db984401a54ca1eecd8e4ac14Niranjan Kumar            chain.add_event(
114534618870e735e83db984401a54ca1eecd8e4ac14Niranjan Kumar                    expected_events,
114634618870e735e83db984401a54ca1eecd8e4ac14Niranjan Kumar                    self._WAIT_FOR_UPDATE_CHECK_AFTER_REBOOT_SECONDS,
114734618870e735e83db984401a54ca1eecd8e4ac14Niranjan Kumar                    on_timeout=self._timeout_err(
114834618870e735e83db984401a54ca1eecd8e4ac14Niranjan Kumar                            'a successful reboot notification',
114934618870e735e83db984401a54ca1eecd8e4ac14Niranjan Kumar                            self._WAIT_FOR_UPDATE_CHECK_AFTER_REBOOT_SECONDS,
115034618870e735e83db984401a54ca1eecd8e4ac14Niranjan Kumar                            event_type=EVENT_TYPE_UPDATE_COMPLETE))
115134618870e735e83db984401a54ca1eecd8e4ac14Niranjan Kumar
115234618870e735e83db984401a54ca1eecd8e4ac14Niranjan Kumar            log_verifier.verify_expected_events_chain(chain)
1153f4fa49b09cfa64e039355f78df2c34a9acca0c0cChris Sosa
1154f1d1104c736f79145209c76a147c1334f8dbb6a9Gilad Arnold        # Make sure we're using a different slot after the update.
1155f1d1104c736f79145209c76a147c1334f8dbb6a9Gilad Arnold        target_active_slot = test_platform.get_active_slot()
1156f1d1104c736f79145209c76a147c1334f8dbb6a9Gilad Arnold        if target_active_slot == source_active_slot:
11571b4da911c86c446bef8d0d5cb63dcf605ce6d289Gilad Arnold            err_msg = 'The active image slot did not change after the update.'
11587ac0a621527b1ecb4b82fea910c730682f9c1f76Gilad Arnold            if None in (source_release, target_release):
11597ac0a621527b1ecb4b82fea910c730682f9c1f76Gilad Arnold                err_msg += (' The DUT likely rebooted into the old image, which '
11607ac0a621527b1ecb4b82fea910c730682f9c1f76Gilad Arnold                            'probably means that the payload we applied was '
11617ac0a621527b1ecb4b82fea910c730682f9c1f76Gilad Arnold                            'corrupt. But since we did not check the source '
11627ac0a621527b1ecb4b82fea910c730682f9c1f76Gilad Arnold                            'and/or target version we cannot say for sure.')
11637ac0a621527b1ecb4b82fea910c730682f9c1f76Gilad Arnold            elif source_release == target_release:
11641b4da911c86c446bef8d0d5cb63dcf605ce6d289Gilad Arnold                err_msg += (' Given that the source and target versions are '
11651b4da911c86c446bef8d0d5cb63dcf605ce6d289Gilad Arnold                            'identical, the DUT likely rebooted into the old '
11661b4da911c86c446bef8d0d5cb63dcf605ce6d289Gilad Arnold                            'image. This probably means that the payload we '
11671b4da911c86c446bef8d0d5cb63dcf605ce6d289Gilad Arnold                            'applied was corrupt.')
11681b4da911c86c446bef8d0d5cb63dcf605ce6d289Gilad Arnold            else:
11691b4da911c86c446bef8d0d5cb63dcf605ce6d289Gilad Arnold                err_msg += (' This is strange since the DUT reported the '
11701b4da911c86c446bef8d0d5cb63dcf605ce6d289Gilad Arnold                            'correct target version. This is probably a system '
11711b4da911c86c446bef8d0d5cb63dcf605ce6d289Gilad Arnold                            'bug; check the DUT system log.')
11721b4da911c86c446bef8d0d5cb63dcf605ce6d289Gilad Arnold            raise error.TestFail(err_msg)
11730338ff3b754d52b41ca2d0432164a757daa1112dGilad Arnold
1174f1d1104c736f79145209c76a147c1334f8dbb6a9Gilad Arnold        logging.info('Target active slot changed as expected: %s',
1175f1d1104c736f79145209c76a147c1334f8dbb6a9Gilad Arnold                     target_active_slot)
1176eb300ac8af429e51f751b95ce375636fbb649e37Gilad Arnold
11770338ff3b754d52b41ca2d0432164a757daa1112dGilad Arnold        logging.info('Update successful, test completed')
11780ed760cbee4ce839988585003d445465bb0b95b9Gilad Arnold
1179ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa
11808e00e8b4a421c53a15f751fc90bb06d738ccbd78David Haddock    def run_once(self, host, test_conf):
1181ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa        """Performs a complete auto update test.
1182ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa
1183ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa        @param host: a host object representing the DUT
1184ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa        @param test_conf: a dictionary containing test configuration values
1185ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa
1186ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa        @raise error.TestError if anything went wrong with setting up the test;
1187ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa               error.TestFail if any part of the test has failed.
1188ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa        """
1189ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa        self._host = host
1190a0dee8465654b79d97cbc1cfca82c42fc95bd8b5David Haddock        logging.debug('The test configuration supplied: %s', test_conf)
1191ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa
1192009ab000fbeeec0e0be9a2d6e744107a2b82306bDan Shi        # Find a devserver to use. We first try to pick a devserver with the
1193009ab000fbeeec0e0be9a2d6e744107a2b82306bDan Shi        # least load. In case all devservers' load are higher than threshold,
1194009ab000fbeeec0e0be9a2d6e744107a2b82306bDan Shi        # fall back to the old behavior by picking a devserver based on the
1195009ab000fbeeec0e0be9a2d6e744107a2b82306bDan Shi        # payload URI, with which ImageServer.resolve will return a random
1196009ab000fbeeec0e0be9a2d6e744107a2b82306bDan Shi        # devserver based on the hash of the URI.
119701b28a630230336c3277dd7b9d375ee012931b77Dan Shi        # The picked devserver needs to respect the location of the host if
1198a0dee8465654b79d97cbc1cfca82c42fc95bd8b5David Haddock        # 'prefer_local_devserver' is set to True or 'restricted_subnets' is
119901b28a630230336c3277dd7b9d375ee012931b77Dan Shi        # set.
120001b28a630230336c3277dd7b9d375ee012931b77Dan Shi        hostname = self._host.hostname if self._host else None
120101b28a630230336c3277dd7b9d375ee012931b77Dan Shi        least_loaded_devserver = dev_server.get_least_loaded_devserver(
120201b28a630230336c3277dd7b9d375ee012931b77Dan Shi                hostname=hostname)
12034e2153dd994a088d92e2a4c1cbee9043e08f9fe7Dan Shi        if least_loaded_devserver:
1204a0dee8465654b79d97cbc1cfca82c42fc95bd8b5David Haddock            logging.debug('Choosing the least loaded devserver: %s',
12054e2153dd994a088d92e2a4c1cbee9043e08f9fe7Dan Shi                          least_loaded_devserver)
12064e2153dd994a088d92e2a4c1cbee9043e08f9fe7Dan Shi            autotest_devserver = dev_server.ImageServer(least_loaded_devserver)
12074e2153dd994a088d92e2a4c1cbee9043e08f9fe7Dan Shi        else:
1208009ab000fbeeec0e0be9a2d6e744107a2b82306bDan Shi            logging.warning('No devserver meets the maximum load requirement. '
1209a0dee8465654b79d97cbc1cfca82c42fc95bd8b5David Haddock                            'Picking a random devserver to use.')
1210009ab000fbeeec0e0be9a2d6e744107a2b82306bDan Shi            autotest_devserver = dev_server.ImageServer.resolve(
121169b9b975fccab1ede1e8849ed17e7e655a9d6b48Dan Shi                    test_conf['target_payload_uri'], host.hostname)
1212ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa        devserver_hostname = urlparse.urlparse(
1213ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa                autotest_devserver.url()).hostname
1214ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa
1215a0dee8465654b79d97cbc1cfca82c42fc95bd8b5David Haddock        logging.info('Devserver chosen for this run: %s', devserver_hostname)
1216a0dee8465654b79d97cbc1cfca82c42fc95bd8b5David Haddock
1217f1d1104c736f79145209c76a147c1334f8dbb6a9Gilad Arnold        # Obtain a test platform implementation.
1218f1d1104c736f79145209c76a147c1334f8dbb6a9Gilad Arnold        test_platform = TestPlatform.create(host)
1219a0dee8465654b79d97cbc1cfca82c42fc95bd8b5David Haddock        test_platform.initialize(autotest_devserver, self.job.resultdir)
1220f1d1104c736f79145209c76a147c1334f8dbb6a9Gilad Arnold
1221a0dee8465654b79d97cbc1cfca82c42fc95bd8b5David Haddock        # Stage source images and update payloads onto the devserver.
1222f1d1104c736f79145209c76a147c1334f8dbb6a9Gilad Arnold        staged_urls = test_platform.prep_artifacts(test_conf)
1223a0ca5707ed10a6575ed290f341294331455f7769Gilad Arnold        self._source_image_installed = bool(staged_urls.source_url)
1224ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa
1225f1d1104c736f79145209c76a147c1334f8dbb6a9Gilad Arnold        # Prepare the DUT (install source version etc).
1226a0dee8465654b79d97cbc1cfca82c42fc95bd8b5David Haddock        test_platform.prep_device_for_update(test_conf['source_payload_uri'])
1227c6ed2f633a1c4609a4b212da66b3e506008ee3a7Nam T. Nguyen
1228a0dee8465654b79d97cbc1cfca82c42fc95bd8b5David Haddock        # Start the update.
1229ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa        try:
1230f1d1104c736f79145209c76a147c1334f8dbb6a9Gilad Arnold            self.run_update_test(test_platform, test_conf)
1231ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa        except ExpectedUpdateEventChainFailed:
1232f1d1104c736f79145209c76a147c1334f8dbb6a9Gilad Arnold            self._dump_update_engine_log(test_platform)
1233ed8c1858bc32b066e4fbe4100a9ad49bdf1adc61Chris Sosa            raise
1234f789cf3a52c720344062f0a6c782bb758f08b189Don Garrett
1235a0dee8465654b79d97cbc1cfca82c42fc95bd8b5David Haddock        test_platform.check_device_after_update()
1236