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