1# Copyright 2018 The Chromium OS Authors. All rights reserved. 2# Use of this source code is governed by a BSD-style license that can be 3# found in the LICENSE file. 4 5import logging 6import shutil 7import time 8import urlparse 9 10from autotest_lib.client.bin import test, utils 11from autotest_lib.client.common_lib import error 12from autotest_lib.client.cros.update_engine import dlc_util 13from autotest_lib.client.cros.update_engine import update_engine_util 14 15class UpdateEngineTest(test.test, update_engine_util.UpdateEngineUtil): 16 """Base class for update engine client tests.""" 17 18 _NETWORK_INTERFACES = ['eth0', 'eth1', 'eth2'] 19 20 21 def initialize(self): 22 """Initialize for this test.""" 23 self._set_util_functions(utils.run, shutil.copy) 24 self._internet_was_disabled = False 25 26 # Utilities for DLC management 27 self._dlc_util = dlc_util.DLCUtil(self._run) 28 29 30 def cleanup(self): 31 """Cleanup for this test.""" 32 # Make sure to grab the update engine log for every test run. 33 shutil.copy(self._UPDATE_ENGINE_LOG, self.resultsdir) 34 35 # Ensure ethernet adapters are back on 36 self._enable_internet() 37 38 39 def _enable_internet(self, ping_server='google.com'): 40 """ 41 Re-enables the internet connection. 42 43 @param ping_server: The server to ping to check we are online. 44 45 """ 46 if not self._internet_was_disabled: 47 return 48 49 self._internet_was_disabled = False 50 logging.debug('Before reconnect: %s', utils.run(['ifconfig'])) 51 for eth in self._NETWORK_INTERFACES: 52 utils.run(['ifconfig', eth, 'up'], ignore_status=True) 53 utils.start_service('recover_duts', ignore_status=True) 54 55 # Print ifconfig to help debug DUTs that stay offline. 56 logging.debug('After reconnect: %s', utils.run(['ifconfig'])) 57 58 # We can't return right after reconnecting the network or the server 59 # test may not receive the message. So we wait a bit longer for the 60 # DUT to be reconnected. 61 utils.poll_for_condition(lambda: utils.ping(ping_server, 62 tries=3, timeout=10) == 0, 63 timeout=120, 64 sleep_interval=1, 65 exception=error.TestFail( 66 'Ping failed after reconnecting network')) 67 68 69 def _disable_internet(self, ping_server='google.com'): 70 """Disable the internet connection""" 71 self._internet_was_disabled = True 72 try: 73 logging.debug('Before disconnect: %s', utils.run(['ifconfig'])) 74 # DUTs in the lab have a service called recover_duts that is used to 75 # check that the DUT is online and if it is not it will bring it 76 # back online. We will need to stop this service for the length 77 # of this test. 78 utils.stop_service('recover_duts', ignore_status=True) 79 for eth in self._NETWORK_INTERFACES: 80 result = utils.run(['ifconfig', eth, 'down'], 81 ignore_status=True) 82 logging.debug(result) 83 84 # Print ifconfig to help debug DUTs that stay online. 85 logging.debug('After disconnect: %s', utils.run('ifconfig')) 86 87 # Make sure we are offline 88 utils.poll_for_condition(lambda: utils.ping(ping_server, 89 deadline=5, 90 timeout=5) != 0, 91 timeout=60, 92 sleep_interval=1, 93 desc='Ping failure while offline.') 94 except (error.CmdError, utils.TimeoutError): 95 logging.exception('Failed to disconnect one or more interfaces.') 96 logging.debug(utils.run(['ifconfig'], ignore_status=True)) 97 raise error.TestFail('Disabling the internet connection failed.') 98 99 100 def _disconnect_reconnect_network_test(self, update_url, 101 time_without_network=120, 102 accepted_movement=0.015): 103 """ 104 Disconnects the network for a period of time, verifies that the update 105 pauses, reconnects the network, and ensures that the update picks up 106 from where it left off. This will be used as a part of 107 autoupdate_ForcedOOBEUpdate.interrupt and autoupdate_Interruptions. 108 109 @param update_url: The update url used by the test. We will ping it to 110 check whether we are online/offline. 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 119 """ 120 logging.info('Starting network interruption check.') 121 if self._is_update_finished_downloading(): 122 raise error.TestFail('The update has already finished before we ' 123 'can disconnect network.') 124 self._update_server = urlparse.urlparse(update_url).hostname 125 self._disable_internet() 126 127 # Check that we are offline. 128 result = utils.ping(self._update_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