Home | History | Annotate | Download | only in wb_kupdate
      1 import datetime, logging, os, time
      2 from autotest_lib.client.bin import test, utils
      3 from autotest_lib.client.common_lib import error
      4 
      5 class wb_kupdate(test.test):
      6     version = 1
      7 
      8 
      9     def _check_parameters(self, mount_point, write_size, file_count,
     10                           old_cleanup=False):
     11         """
     12         Check all test parameters.
     13 
     14         @param mount_point: the path to the desired mount_point.
     15         @param write_size: the size of data in MB to write.
     16         @param file_count: the number of files to write.
     17         @param old_cleanup: removes previous mount_point if it exists and is
     18                 not mounted. Default is False.
     19         """
     20         # Check mount_point.
     21         if not os.path.exists(mount_point):
     22             logging.info('%s does not exist. Creating directory.', mount_point)
     23         elif not os.path.ismount(mount_point) and old_cleanup:
     24             logging.info('Removing previous mount_point directory')
     25             os.rmdir(mount_point)
     26             logging.info('Creating new mount_point.')
     27         else:
     28             raise error.TestError('Mount point: %s already exists.' %
     29                                   mount_point)
     30 
     31         os.makedirs(mount_point)
     32         # Check write_size > 0.
     33         if not (write_size > 0):
     34             raise error.TestError('Write size should be a positive integer.')
     35 
     36         # Check file_count > 0.
     37         if not (file_count > 0) :
     38             raise error.TestError('File count shoulde be a positive integer.')
     39 
     40 
     41     def _reset_device(self):
     42         """
     43         Reset the test. Reinitialize sparse file.
     44         """
     45         # Umount device.
     46         logging.debug('Cleanup - unmounting loopback device.')
     47         utils.system('umount %s' % self.mount_point, ignore_status=True)
     48 
     49         # Remove sparse_file.
     50         logging.debug('Cleanup - removing sparse file.')
     51         os.remove(self.sparse_file)
     52 
     53         # Remove mount_point directory.
     54         logging.debug('Cleanup - removing the mount_point.')
     55         os.rmdir(self.mount_point)
     56 
     57 
     58     def _create_partition(self):
     59         """
     60         Create and initialize the sparse file.
     61         """
     62         # Recreate sparse_file.
     63         utils.system('dd if=/dev/zero of=%s bs=1M seek=1024 count=1' %
     64                       self.sparse_file)
     65 
     66         # Format sparse_file.
     67         utils.system('echo "y" |  mkfs -t %s %s' %
     68                      (self.file_system, self.sparse_file))
     69 
     70         # Mount sparse_file.
     71         utils.system('mount -o loop -t %s %s %s' %
     72                      (self.file_system, self.sparse_file, self.mount_point))
     73 
     74 
     75     def _needs_more_time(self, start_time, duration, _now=None):
     76         """
     77         Checks to see if the test has run its course.
     78 
     79         @param start_time: a datetime object specifying the start time of the
     80                 test.
     81         @param duration: test duration in minutes.
     82         @param _now: used mostly for testing - ensures that the function returns
     83                 pass/fail depnding on the value of _now.
     84 
     85         @return: True if the test still needs to run longer.
     86                  False if the test has run for 'duration' minutes.
     87         """
     88         if not _now:
     89             time_diff = datetime.datetime.now() - start_time
     90         else:
     91             time_diff = _now - start_time
     92         return time_diff <= datetime.timedelta(seconds=duration*60)
     93 
     94 
     95     def _write_data(self, destination, counter, write_size):
     96         """
     97         Writes data to the cache/memory.
     98 
     99         @param destination: the absolute path to where the data needs to be
    100         written.
    101         @param counter: the file counter.
    102         @param write_size: the size of data to be written.
    103 
    104         @return: the time when the write completed as a datetime object.
    105         """
    106         # Write data to disk.
    107         file_name = os.path.join(destination, 'test_file_%s' % counter)
    108         write_cmd = ('dd if=/dev/zero of=%s bs=1M count=%s' %
    109                      (file_name, write_size))
    110         utils.system(write_cmd)
    111 
    112         # Time the write operation.
    113         write_completion_time = datetime.datetime.now()
    114 
    115         # Return write completion time.
    116         return write_completion_time
    117 
    118 
    119     def _get_disk_usage(self, file_name):
    120         """
    121         Returns the disk usage of given file.
    122 
    123         @param file_name: the name of the file.
    124 
    125         @return: the disk usage as an integer.
    126         """
    127         # Check du stats.
    128         cmd = '%s %s' % (self._DU_CMD, file_name)
    129 
    130         # Expected value for  output = '1028\tfoo'
    131         output = utils.system_output(cmd)
    132 
    133         # Desired ouput = (1028, foo)
    134         output = output.split('\t')
    135 
    136         return int(output[0])
    137 
    138 
    139     def _wait_until_data_flushed(self, start_time, max_wait_time):
    140         """
    141         Check to see if the sparse file size increases.
    142 
    143         @param start_time: the time when data was actually written into the
    144                 cache.
    145         @param max_wait_time: the max amount of time to wait.
    146 
    147         @return: time waited as a datetime.timedelta object.
    148         """
    149         current_size = self._get_disk_usage(self.sparse_file)
    150         flushed_size = current_size
    151 
    152         logging.debug('current_size: %s' % current_size)
    153         logging.debug('flushed_size: %s' % flushed_size)
    154 
    155         # Keep checking until du value changes.
    156         while current_size == flushed_size:
    157             # Get flushed_size.
    158             flushed_size = self._get_disk_usage(self.sparse_file)
    159             logging.debug('flushed_size: %s' % flushed_size)
    160             time.sleep(1)
    161 
    162             # Check if data has been synced to disk.
    163             if not self._needs_more_time(start_time, max_wait_time):
    164                 raise error.TestError('Data not flushed. Waited for %s minutes '
    165                                       'for data to flush out.' % max_wait_time)
    166 
    167         # Return time waited.
    168         return datetime.datetime.now() - start_time
    169 
    170 
    171     def initialize(self):
    172         """
    173         Initialize all private and global member variables.
    174         """
    175         self._DU_CMD = 'du'
    176         self.partition = None
    177         self.mount_point = ''
    178         self.sparse_file = ''
    179         self.result_map = {}
    180         self.file_system = None
    181 
    182 
    183     def run_once(self, mount_point, file_count, write_size,
    184                  max_flush_time=1, file_system=None, remove_previous=False,
    185                  sparse_file=os.path.join(os.getcwd(),'sparse_file'),
    186                  old_cleanup=False):
    187         """
    188         Control execution of the test.
    189 
    190         @param mount_point: the absolute path to the mount point.
    191         @param file_count: the number of files to write.
    192         @param write_size: the size of each file in MB.
    193         @param max_flush_time: the maximum time to wait for the writeback to
    194                 flush dirty data to disk. Default = 1 minute.
    195         @param file_system: the new file system to be mounted, if any.
    196                 Default = None.
    197         @param remove_previous: boolean that allows the removal of previous
    198                 files before creating a new one. Default = False.
    199         @param sparse_file: the absolute path to the sparse file.
    200         @param old_cleanup: removes previous mount_point if it exists and is
    201                 not mounted. Default is False.
    202         """
    203         # Check validity of parameters.
    204         self._check_parameters(mount_point, write_size, file_count,
    205                                old_cleanup)
    206 
    207         # Initialize class variables.
    208         self.mount_point = mount_point
    209         self.sparse_file = sparse_file
    210         self.file_system = file_system
    211 
    212         # Initialize partition values.
    213         self._create_partition()
    214 
    215         # Flush read and write cache.
    216         utils.drop_caches()
    217 
    218         # Start iterations.
    219         logging.info('Starting test operations.')
    220         test_start_time = datetime.datetime.now()
    221         counter = 1
    222 
    223         # Run test until file_count files are successfully written to disk.
    224         while counter < file_count:
    225             logging.info('Iteration %s.', counter)
    226 
    227             # Write data to disk.
    228             write_completion_time = self._write_data(self.mount_point, counter,
    229                                                      write_size)
    230             logging.debug('Write time:%s',
    231                           write_completion_time.strftime("%H:%M:%S"))
    232 
    233             # Wait until data get synced to disk.
    234             time_taken = self._wait_until_data_flushed(write_completion_time,
    235                                                        max_flush_time)
    236 
    237             # Log time statistics.
    238             logging.info('Time taken to flush data: %s seconds.',
    239                          time_taken.seconds)
    240 
    241             # Check if there is a need to remove the previously written file.
    242             if remove_previous:
    243                 logging.debug('Removing previous file instance.')
    244                 os.remove(sparse_file)
    245             else:
    246                 logging.debug('Not removing previous file instance.')
    247 
    248             # Flush cache.
    249             logging.debug('Flush cache between iterations.')
    250             utils.drop_caches()
    251 
    252            # Update the result map.
    253             self.result_map[counter] = time_taken.seconds
    254 
    255             # Increment the counter.
    256             counter += 1
    257 
    258 
    259     def postprocess(self):
    260         """
    261         Cleanup routine.
    262         """
    263         # Write out keyval map.
    264         self.write_perf_keyval(self.result_map)
    265 
    266         # Cleanup device.
    267         self._reset_device()
    268 
    269         logging.info('Test operations completed.')
    270