1import datetime, logging, os, time 2from autotest_lib.client.bin import test, utils 3from autotest_lib.client.common_lib import error 4 5class 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