xref: /aosp_15_r20/external/autotest/client/cros/update_engine/update_engine_test.py (revision 9c5db1993ded3edbeafc8092d69fe5de2ee02df7)
1# Lint as: python2, python3
2# Copyright 2018 The Chromium OS Authors. All rights reserved.
3# Use of this source code is governed by a BSD-style license that can be
4# found in the LICENSE file.
5
6import logging
7import shutil
8import time
9import six.moves.urllib.parse
10
11from autotest_lib.client.bin import test, utils
12from autotest_lib.client.common_lib import error
13from autotest_lib.client.cros.update_engine import dlc_util
14from autotest_lib.client.cros.update_engine import update_engine_util
15
16class UpdateEngineTest(test.test, update_engine_util.UpdateEngineUtil):
17    """Base class for update engine client tests."""
18
19    _NETWORK_INTERFACES = ['eth0', 'eth1', 'eth2']
20
21
22    def initialize(self):
23        """Initialize for this test."""
24        self._set_util_functions(utils.run, shutil.copy)
25        self._internet_was_disabled = False
26
27        # Utilities for DLC management
28        self._dlc_util = dlc_util.DLCUtil(self._run)
29
30
31    def cleanup(self):
32        """Cleanup for this test."""
33        # Make sure to grab the update engine log for every test run.
34        shutil.copy(self._UPDATE_ENGINE_LOG, self.resultsdir)
35
36        # Ensure ethernet adapters are back on
37        self._enable_internet()
38
39
40    def _enable_internet(self, ping_server='google.com'):
41        """
42        Re-enables the internet connection.
43
44        @param ping_server: The server to ping to check we are online.
45
46        """
47        if not self._internet_was_disabled:
48            return
49
50        self._internet_was_disabled = False
51        logging.debug('Before reconnect: %s', utils.run(['ifconfig']))
52        for eth in self._NETWORK_INTERFACES:
53            utils.run(['ifconfig', eth, 'up'], ignore_status=True)
54        utils.start_service('recover_duts', ignore_status=True)
55
56        # Print ifconfig to help debug DUTs that stay offline.
57        logging.debug('After reconnect: %s', utils.run(['ifconfig']))
58
59        # We can't return right after reconnecting the network or the server
60        # test may not receive the message. So we wait a bit longer for the
61        # DUT to be reconnected.
62        utils.poll_for_condition(lambda: utils.ping(ping_server,
63                                                    tries=3, timeout=10) == 0,
64                                 timeout=120,
65                                 sleep_interval=1,
66                                 exception=error.TestFail(
67                                     'Ping failed after reconnecting network'))
68
69
70    def _disable_internet(self, ping_server='google.com'):
71        """Disable the internet connection"""
72        self._internet_was_disabled = True
73        try:
74            logging.debug('Before disconnect: %s', utils.run(['ifconfig']))
75            # DUTs in the lab have a service called recover_duts that is used to
76            # check that the DUT is online and if it is not it will bring it
77            # back online. We will need to stop this service for the length
78            # of this test.
79            utils.stop_service('recover_duts', ignore_status=True)
80            for eth in self._NETWORK_INTERFACES:
81                result = utils.run(['ifconfig', eth, 'down'],
82                                   ignore_status=True)
83                logging.debug(result)
84
85            # Print ifconfig to help debug DUTs that stay online.
86            logging.debug('After disconnect: %s', utils.run('ifconfig'))
87
88            # Make sure we are offline
89            utils.poll_for_condition(lambda: utils.ping(ping_server,
90                                                        deadline=5,
91                                                        timeout=5) != 0,
92                                     timeout=60,
93                                     sleep_interval=1,
94                                     desc='Ping failure while offline.')
95        except (error.CmdError, utils.TimeoutError):
96            logging.exception('Failed to disconnect one or more interfaces.')
97            logging.debug(utils.run(['ifconfig'], ignore_status=True))
98            raise error.TestFail('Disabling the internet connection failed.')
99
100
101    def _disconnect_reconnect_network_test(self,
102                                           time_without_network=25,
103                                           accepted_movement=0.015,
104                                           ping_server='google.com'):
105        """
106        Disconnects the network for a period of time, verifies that the update
107        pauses, reconnects the network, and ensures that the update picks up
108        from where it left off. This will be used as a part of
109        autoupdate_ForcedOOBEUpdate.interrupt and autoupdate_Interruptions.
110
111        @param time_without_network: Duration of the network disconnection in
112                                     seconds.
113        @param accepted_movement: Acceptable movement of update_engine
114                                  progress after the network is disabled.
115                                  Sometimes when network is disabled
116                                  update_engine progress will move a little,
117                                  which can cause false positives.
118        @param ping_server: The server to ping to check we are online.
119
120        """
121        logging.info('Starting network interruption check.')
122        if self._is_update_finished_downloading():
123            raise error.TestFail('The update has already finished before we '
124                                 'can disconnect network.')
125        self._disable_internet()
126
127        # Check that we are offline.
128        result = utils.ping(ping_server, deadline=5, timeout=5)
129        if result != 2:
130            raise error.TestFail('Ping succeeded even though we were offline.')
131
132        # We are seeing update_engine progress move a very tiny amount
133        # after disconnecting network so wait for it to stop moving.
134        utils.poll_for_condition(lambda: self._has_progress_stopped,
135                                 desc='Waiting for update progress to stop.')
136
137        # Get the update progress as the network is down
138        progress_before = float(self._get_update_engine_status()[
139            self._PROGRESS])
140
141        seconds = 1
142        while seconds < time_without_network:
143            logging.info(self._get_update_engine_status())
144            time.sleep(1)
145            seconds += 1
146
147        progress_after = float(self._get_update_engine_status()[
148            self._PROGRESS])
149
150        if progress_before != progress_after:
151            if progress_before < progress_after:
152                if progress_after - progress_before > accepted_movement:
153                    raise error.TestFail('The update continued while the '
154                                         'network was supposedly disabled. '
155                                         'Before: %f, After: %f' % (
156                                         progress_before, progress_after))
157                else:
158                    logging.warning('The update progress moved slightly while '
159                                    'network was off.')
160            elif self._is_update_finished_downloading():
161                raise error.TestFail('The update finished while the network '
162                                     'was disabled. Before: %f, After: %f' %
163                                     (progress_before, progress_after))
164            else:
165                raise error.TestFail('The update appears to have restarted. '
166                                     'Before: %f, After: %f' % (progress_before,
167                                                                progress_after))
168
169        self._enable_internet()
170