1# Copyright 2017 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"""This throttler tries to reduce result size by compress files to tgz file. 6""" 7 8import re 9import os 10import tarfile 11 12import throttler_lib 13import utils_lib 14 15 16# File with extensions that can not be zipped or compression won't reduce file 17# size further. 18UNZIPPABLE_EXTENSIONS = set([ 19 '.gz', 20 '.jpg', 21 '.png', 22 '.tgz', 23 '.xz', 24 '.zip', 25 ]) 26 27# Regex for files that should not be compressed. 28UNZIPPABLE_FILE_PATTERNS = [ 29 'BUILD_INFO-.*' # ACTS test result files. 30 ] 31 32# Default threshold of file size in byte for it to be qualified for compression. 33# Files smaller than the threshold will not be compressed. 34DEFAULT_FILE_SIZE_THRESHOLD_BYTE = 100 * 1024 35 36def _zip_file(file_info): 37 """Zip the file to reduce the file size. 38 39 @param file_info: A ResultInfo object containing summary for the file to be 40 shrunk. 41 """ 42 utils_lib.LOG('Compressing file %s' % file_info.path) 43 parent_result_info = file_info.parent_result_info 44 new_name = file_info.name + '.tgz' 45 new_path = os.path.join(os.path.dirname(file_info.path), new_name) 46 if os.path.exists(new_path): 47 utils_lib.LOG('File %s already exists, removing...' % new_path) 48 if not throttler_lib.try_delete_file_on_disk(new_path): 49 return 50 parent_result_info.remove_file(new_name) 51 with tarfile.open(new_path, 'w:gz') as tar: 52 tar.add(file_info.path, arcname=os.path.basename(file_info.path)) 53 stat = os.stat(file_info.path) 54 if not throttler_lib.try_delete_file_on_disk(file_info.path): 55 # Clean up the intermediate file. 56 throttler_lib.try_delete_file_on_disk(new_path) 57 utils_lib.LOG('Failed to compress %s' % file_info.path) 58 return 59 60 # Modify the new file's timestamp to the old one. 61 os.utime(new_path, (stat.st_atime, stat.st_mtime)) 62 # Get the original file size before compression. 63 original_size = file_info.original_size 64 parent_result_info.remove_file(file_info.name) 65 parent_result_info.add_file(new_name) 66 new_file_info = parent_result_info.get_file(new_name) 67 # Set the original size to be the size before compression. 68 new_file_info.original_size = original_size 69 # Set the trimmed size to be the physical file size of the compressed file. 70 new_file_info.trimmed_size = new_file_info.size 71 72 73def _get_zippable_files(file_infos, file_size_threshold_byte): 74 """Filter the files that can be throttled. 75 76 @param file_infos: A list of ResultInfo objects. 77 @param file_size_threshold_byte: Threshold of file size in byte for it to be 78 qualified for compression. 79 @yield: ResultInfo objects that can be shrunk. 80 """ 81 for info in file_infos: 82 ext = os.path.splitext(info.name)[1].lower() 83 if ext in UNZIPPABLE_EXTENSIONS: 84 continue 85 86 match_found = False 87 for pattern in UNZIPPABLE_FILE_PATTERNS: 88 if re.match(pattern, info.name): 89 match_found = True 90 break 91 if match_found: 92 continue 93 94 if info.trimmed_size <= file_size_threshold_byte: 95 continue 96 97 yield info 98 99 100def throttle(summary, max_result_size_KB, 101 file_size_threshold_byte=DEFAULT_FILE_SIZE_THRESHOLD_BYTE, 102 skip_autotest_log=False): 103 """Throttle the files in summary by compressing file. 104 105 Stop throttling until all files are processed or the result file size is 106 already reduced to be under the given max_result_size_KB. 107 108 @param summary: A ResultInfo object containing result summary. 109 @param max_result_size_KB: Maximum test result size in KB. 110 @param file_size_threshold_byte: Threshold of file size in byte for it to be 111 qualified for compression. 112 @param skip_autotest_log: True to skip shrink Autotest logs, default is 113 False. 114 """ 115 file_infos, _ = throttler_lib.sort_result_files(summary) 116 extra_patterns = ([throttler_lib.AUTOTEST_LOG_PATTERN] if skip_autotest_log 117 else []) 118 file_infos = throttler_lib.get_throttleable_files( 119 file_infos, extra_patterns) 120 file_infos = _get_zippable_files(file_infos, file_size_threshold_byte) 121 for info in file_infos: 122 _zip_file(info) 123 124 if throttler_lib.check_throttle_limit(summary, max_result_size_KB): 125 return 126