Home | History | Annotate | Download | only in network_DiskFull
      1 # Copyright (c) 2014 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 
      5 import logging
      6 import os
      7 import time
      8 
      9 from autotest_lib.client.common_lib import error
     10 from autotest_lib.server import autotest
     11 from autotest_lib.server import hosts
     12 from autotest_lib.server import test
     13 from autotest_lib.server.cros import remote_command
     14 
     15 class network_DiskFull(test.test):
     16     """Test networking daemons when /var is full."""
     17 
     18     version = 1
     19     CLIENT_TEST_LIST = [
     20         ('network_DhcpNegotiationSuccess', {}),
     21         ('network_DhcpRenew', {}),
     22         ('network_RestartShill', {
     23                 'tag': 'profile_exists',
     24                 'remove_profile': False}),
     25         ('network_RestartShill', {
     26                 'tag': 'profile_missing',
     27                 'remove_profile': True}),
     28         ]
     29     CLIENT_TMP_DIR = '/tmp'
     30     DISK_FILL_SCRIPT = 'hog_disk.sh'
     31     FILL_TIMEOUT_SECONDS = 5
     32     MAX_FREE_KB = 1024
     33     STATEFUL_PATH = '/var'
     34     TEST_TIMEOUT_SECONDS = 180
     35 
     36     def get_free_kilobytes(self, mount_point):
     37         """
     38         Get the size of free space on the filesystem mounted at |mount_point|,
     39         in kilobytes.
     40 
     41         @return Kilobytes free, as an integer.
     42         """
     43         # Filesystem              1024-blocks  Used Available Capacity Mount...
     44         # /dev/mapper/encstateful      290968 47492    243476      17% /var
     45         output = self._client.run('df -P %s' % mount_point).stdout
     46         lines = output.splitlines()
     47         if len(lines) != 2:
     48             raise error.TestFail('Unexpected df output: %s' % lines)
     49         _, _, _, free_kb, _, df_mount_point = lines[1].split(None, 5)
     50         if df_mount_point != mount_point:
     51             raise error.TestFail('Failed to find %s, got %s instead.' %
     52                                  (mount_point, df_mount_point))
     53         return int(free_kb)
     54 
     55 
     56     def wait_until_full(self, mount_point, max_free_kilobytes):
     57         """
     58         Wait until |mount_point| has no more than |max_free_kilobytes| free.
     59 
     60         @param mount_point The path at which the filesystem is mounted.
     61         @param max_free_kilobytes Maximum free space permitted, in kilobytes.
     62         @return True if the disk is full, else False
     63         """
     64         start_time = time.time()
     65         while time.time() - start_time < self.FILL_TIMEOUT_SECONDS:
     66             if (self.get_free_kilobytes(mount_point) <= max_free_kilobytes):
     67                 return True
     68             else:
     69                 time.sleep(1)
     70         return False
     71 
     72 
     73     def run_once(self, client_addr):
     74         """
     75         Test main loop.
     76 
     77         @param client_addr DUT hostname or IP address.
     78         """
     79         self._client = hosts.create_host(client_addr)
     80         client_autotest = autotest.Autotest(self._client)
     81 
     82         disk_filler_src = os.path.join(self.bindir, self.DISK_FILL_SCRIPT)
     83         disk_filler_dst = os.path.join(self.CLIENT_TMP_DIR,
     84                                        os.path.basename(self.DISK_FILL_SCRIPT))
     85         self._client.send_file(disk_filler_src, disk_filler_dst)
     86 
     87         disk_filler_command = '%s %s %d' % (
     88             disk_filler_dst, self.STATEFUL_PATH, self.TEST_TIMEOUT_SECONDS)
     89 
     90         with remote_command.Command(self._client, disk_filler_command) \
     91                 as disk_filler_process:
     92             if not self.wait_until_full(self.STATEFUL_PATH, self.MAX_FREE_KB):
     93                 logging.debug(disk_filler_process.result)
     94                 raise error.TestFail(
     95                     'did not fill %s within %d seconds' % (
     96                         self.STATEFUL_PATH, self.FILL_TIMEOUT_SECONDS))
     97 
     98             client_autotest.run_test('network_CheckCriticalProcesses',
     99                                      tag='before_client_tests')
    100             passed_with_failsafe = []
    101 
    102             for name, kwargs in self.CLIENT_TEST_LIST:
    103                 # Autotest goes to /mnt/stateful_partition/dev_image,
    104                 # while /var is on /mnt/stateful_partition/encrypted.
    105                 #
    106                 # These are separate partitions, so we can copy
    107                 # the tests onto the DUT even when /var is full.
    108                 client_autotest.run_test(name, **kwargs)
    109 
    110                 if 'tag' in kwargs:
    111                     full_test_name = '%s.%s' % (name, kwargs['tag'])
    112                 else:
    113                     full_test_name = name
    114 
    115                 # To avoid leaving the system in a bad state, the disk
    116                 # filler times out eventually. This means a test can
    117                 # "pass" due to the failsafe. Check if the failsafe
    118                 # kicked in, by checking if the disk is still full.
    119                 if (self.get_free_kilobytes(self.STATEFUL_PATH) >
    120                     self.MAX_FREE_KB):
    121                     passed_with_failsafe.append(full_test_name)
    122 
    123                 client_autotest.run_test('network_CheckCriticalProcesses',
    124                                          tag='after_%s' % full_test_name)
    125 
    126             if len(passed_with_failsafe):
    127                 raise error.TestFail(
    128                     '%d test(s) triggered the fail-safe: %s. '
    129                     'They may be incorrectly listed as passing.' % (
    130                         len(passed_with_failsafe),
    131                         ', '.join(passed_with_failsafe)))
    132