import datetime, logging, os, time from autotest_lib.client.bin import test, utils from autotest_lib.client.common_lib import error class wb_kupdate(test.test): version = 1 def _check_parameters(self, mount_point, write_size, file_count, old_cleanup=False): """ Check all test parameters. @param mount_point: the path to the desired mount_point. @param write_size: the size of data in MB to write. @param file_count: the number of files to write. @param old_cleanup: removes previous mount_point if it exists and is not mounted. Default is False. """ # Check mount_point. if not os.path.exists(mount_point): logging.info('%s does not exist. Creating directory.', mount_point) elif not os.path.ismount(mount_point) and old_cleanup: logging.info('Removing previous mount_point directory') os.rmdir(mount_point) logging.info('Creating new mount_point.') else: raise error.TestError('Mount point: %s already exists.' % mount_point) os.makedirs(mount_point) # Check write_size > 0. if not (write_size > 0): raise error.TestError('Write size should be a positive integer.') # Check file_count > 0. if not (file_count > 0) : raise error.TestError('File count shoulde be a positive integer.') def _reset_device(self): """ Reset the test. Reinitialize sparse file. """ # Umount device. logging.debug('Cleanup - unmounting loopback device.') utils.system('umount %s' % self.mount_point, ignore_status=True) # Remove sparse_file. logging.debug('Cleanup - removing sparse file.') os.remove(self.sparse_file) # Remove mount_point directory. logging.debug('Cleanup - removing the mount_point.') os.rmdir(self.mount_point) def _create_partition(self): """ Create and initialize the sparse file. """ # Recreate sparse_file. utils.system('dd if=/dev/zero of=%s bs=1M seek=1024 count=1' % self.sparse_file) # Format sparse_file. utils.system('echo "y" | mkfs -t %s %s' % (self.file_system, self.sparse_file)) # Mount sparse_file. utils.system('mount -o loop -t %s %s %s' % (self.file_system, self.sparse_file, self.mount_point)) def _needs_more_time(self, start_time, duration, _now=None): """ Checks to see if the test has run its course. @param start_time: a datetime object specifying the start time of the test. @param duration: test duration in minutes. @param _now: used mostly for testing - ensures that the function returns pass/fail depnding on the value of _now. @return: True if the test still needs to run longer. False if the test has run for 'duration' minutes. """ if not _now: time_diff = datetime.datetime.now() - start_time else: time_diff = _now - start_time return time_diff <= datetime.timedelta(seconds=duration*60) def _write_data(self, destination, counter, write_size): """ Writes data to the cache/memory. @param destination: the absolute path to where the data needs to be written. @param counter: the file counter. @param write_size: the size of data to be written. @return: the time when the write completed as a datetime object. """ # Write data to disk. file_name = os.path.join(destination, 'test_file_%s' % counter) write_cmd = ('dd if=/dev/zero of=%s bs=1M count=%s' % (file_name, write_size)) utils.system(write_cmd) # Time the write operation. write_completion_time = datetime.datetime.now() # Return write completion time. return write_completion_time def _get_disk_usage(self, file_name): """ Returns the disk usage of given file. @param file_name: the name of the file. @return: the disk usage as an integer. """ # Check du stats. cmd = '%s %s' % (self._DU_CMD, file_name) # Expected value for output = '1028\tfoo' output = utils.system_output(cmd) # Desired ouput = (1028, foo) output = output.split('\t') return int(output[0]) def _wait_until_data_flushed(self, start_time, max_wait_time): """ Check to see if the sparse file size increases. @param start_time: the time when data was actually written into the cache. @param max_wait_time: the max amount of time to wait. @return: time waited as a datetime.timedelta object. """ current_size = self._get_disk_usage(self.sparse_file) flushed_size = current_size logging.debug('current_size: %s' % current_size) logging.debug('flushed_size: %s' % flushed_size) # Keep checking until du value changes. while current_size == flushed_size: # Get flushed_size. flushed_size = self._get_disk_usage(self.sparse_file) logging.debug('flushed_size: %s' % flushed_size) time.sleep(1) # Check if data has been synced to disk. if not self._needs_more_time(start_time, max_wait_time): raise error.TestError('Data not flushed. Waited for %s minutes ' 'for data to flush out.' % max_wait_time) # Return time waited. return datetime.datetime.now() - start_time def initialize(self): """ Initialize all private and global member variables. """ self._DU_CMD = 'du' self.partition = None self.mount_point = '' self.sparse_file = '' self.result_map = {} self.file_system = None def run_once(self, mount_point, file_count, write_size, max_flush_time=1, file_system=None, remove_previous=False, sparse_file=os.path.join(os.getcwd(),'sparse_file'), old_cleanup=False): """ Control execution of the test. @param mount_point: the absolute path to the mount point. @param file_count: the number of files to write. @param write_size: the size of each file in MB. @param max_flush_time: the maximum time to wait for the writeback to flush dirty data to disk. Default = 1 minute. @param file_system: the new file system to be mounted, if any. Default = None. @param remove_previous: boolean that allows the removal of previous files before creating a new one. Default = False. @param sparse_file: the absolute path to the sparse file. @param old_cleanup: removes previous mount_point if it exists and is not mounted. Default is False. """ # Check validity of parameters. self._check_parameters(mount_point, write_size, file_count, old_cleanup) # Initialize class variables. self.mount_point = mount_point self.sparse_file = sparse_file self.file_system = file_system # Initialize partition values. self._create_partition() # Flush read and write cache. utils.drop_caches() # Start iterations. logging.info('Starting test operations.') test_start_time = datetime.datetime.now() counter = 1 # Run test until file_count files are successfully written to disk. while counter < file_count: logging.info('Iteration %s.', counter) # Write data to disk. write_completion_time = self._write_data(self.mount_point, counter, write_size) logging.debug('Write time:%s', write_completion_time.strftime("%H:%M:%S")) # Wait until data get synced to disk. time_taken = self._wait_until_data_flushed(write_completion_time, max_flush_time) # Log time statistics. logging.info('Time taken to flush data: %s seconds.', time_taken.seconds) # Check if there is a need to remove the previously written file. if remove_previous: logging.debug('Removing previous file instance.') os.remove(sparse_file) else: logging.debug('Not removing previous file instance.') # Flush cache. logging.debug('Flush cache between iterations.') utils.drop_caches() # Update the result map. self.result_map[counter] = time_taken.seconds # Increment the counter. counter += 1 def postprocess(self): """ Cleanup routine. """ # Write out keyval map. self.write_perf_keyval(self.result_map) # Cleanup device. self._reset_device() logging.info('Test operations completed.')