# Copyright 2018 The Chromium OS Authors. All rights reserved. # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. import logging import shutil import time import urlparse from autotest_lib.client.bin import test, utils from autotest_lib.client.common_lib import error from autotest_lib.client.cros.update_engine import dlc_util from autotest_lib.client.cros.update_engine import update_engine_util class UpdateEngineTest(test.test, update_engine_util.UpdateEngineUtil): """Base class for update engine client tests.""" _NETWORK_INTERFACES = ['eth0', 'eth1', 'eth2'] def initialize(self): """Initialize for this test.""" self._set_util_functions(utils.run, shutil.copy) self._internet_was_disabled = False # Utilities for DLC management self._dlc_util = dlc_util.DLCUtil(self._run) def cleanup(self): """Cleanup for this test.""" # Make sure to grab the update engine log for every test run. shutil.copy(self._UPDATE_ENGINE_LOG, self.resultsdir) # Ensure ethernet adapters are back on self._enable_internet() def _enable_internet(self, ping_server='google.com'): """ Re-enables the internet connection. @param ping_server: The server to ping to check we are online. """ if not self._internet_was_disabled: return self._internet_was_disabled = False logging.debug('Before reconnect: %s', utils.run(['ifconfig'])) for eth in self._NETWORK_INTERFACES: utils.run(['ifconfig', eth, 'up'], ignore_status=True) utils.start_service('recover_duts', ignore_status=True) # Print ifconfig to help debug DUTs that stay offline. logging.debug('After reconnect: %s', utils.run(['ifconfig'])) # We can't return right after reconnecting the network or the server # test may not receive the message. So we wait a bit longer for the # DUT to be reconnected. utils.poll_for_condition(lambda: utils.ping(ping_server, tries=3, timeout=10) == 0, timeout=120, sleep_interval=1, exception=error.TestFail( 'Ping failed after reconnecting network')) def _disable_internet(self, ping_server='google.com'): """Disable the internet connection""" self._internet_was_disabled = True try: logging.debug('Before disconnect: %s', utils.run(['ifconfig'])) # DUTs in the lab have a service called recover_duts that is used to # check that the DUT is online and if it is not it will bring it # back online. We will need to stop this service for the length # of this test. utils.stop_service('recover_duts', ignore_status=True) for eth in self._NETWORK_INTERFACES: result = utils.run(['ifconfig', eth, 'down'], ignore_status=True) logging.debug(result) # Print ifconfig to help debug DUTs that stay online. logging.debug('After disconnect: %s', utils.run('ifconfig')) # Make sure we are offline utils.poll_for_condition(lambda: utils.ping(ping_server, deadline=5, timeout=5) != 0, timeout=60, sleep_interval=1, desc='Ping failure while offline.') except (error.CmdError, utils.TimeoutError): logging.exception('Failed to disconnect one or more interfaces.') logging.debug(utils.run(['ifconfig'], ignore_status=True)) raise error.TestFail('Disabling the internet connection failed.') def _disconnect_reconnect_network_test(self, update_url, time_without_network=120, accepted_movement=0.015): """ Disconnects the network for a period of time, verifies that the update pauses, reconnects the network, and ensures that the update picks up from where it left off. This will be used as a part of autoupdate_ForcedOOBEUpdate.interrupt and autoupdate_Interruptions. @param update_url: The update url used by the test. We will ping it to check whether we are online/offline. @param time_without_network: Duration of the network disconnection in seconds. @param accepted_movement: Acceptable movement of update_engine progress after the network is disabled. Sometimes when network is disabled update_engine progress will move a little, which can cause false positives. """ logging.info('Starting network interruption check.') if self._is_update_finished_downloading(): raise error.TestFail('The update has already finished before we ' 'can disconnect network.') self._update_server = urlparse.urlparse(update_url).hostname self._disable_internet() # Check that we are offline. result = utils.ping(self._update_server, deadline=5, timeout=5) if result != 2: raise error.TestFail('Ping succeeded even though we were offline.') # We are seeing update_engine progress move a very tiny amount # after disconnecting network so wait for it to stop moving. utils.poll_for_condition(lambda: self._has_progress_stopped, desc='Waiting for update progress to stop.') # Get the update progress as the network is down progress_before = float(self._get_update_engine_status()[ self._PROGRESS]) seconds = 1 while seconds < time_without_network: logging.info(self._get_update_engine_status()) time.sleep(1) seconds += 1 progress_after = float(self._get_update_engine_status()[ self._PROGRESS]) if progress_before != progress_after: if progress_before < progress_after: if progress_after - progress_before > accepted_movement: raise error.TestFail('The update continued while the ' 'network was supposedly disabled. ' 'Before: %f, After: %f' % ( progress_before, progress_after)) else: logging.warning('The update progress moved slightly while ' 'network was off.') elif self._is_update_finished_downloading(): raise error.TestFail('The update finished while the network ' 'was disabled. Before: %f, After: %f' % (progress_before, progress_after)) else: raise error.TestFail('The update appears to have restarted. ' 'Before: %f, After: %f' % (progress_before, progress_after)) self._enable_internet()