# Copyright 2017 The Chromium OS Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""Help functions used by different throttlers."""
import os
import re
import utils_lib
# A list of file names that should not be throttled, that is, not modified by
# deletion, trimming or compression.
NON_THROTTLEABLE_FILE_NAMES = set([
'.autoserv_execute',
'.parse.lock',
'.parse.log',
'.parser_execute',
'control',
'control.srv',
'host_keyvals',
'job_report.html',
'keyval',
'profiling',
'result_summary.html',
'sponge_invocation.xml',
'status',
'status.log',
# ACTS related files:
'test_run_details.txt',
'test_run_error.txt',
'test_run_info.txt',
'test_run_summary.json',
])
# A list of file name patterns that should not be throttled, that is, not
# modified by deletion, deduping, trimming or compression.
NON_THROTTLEABLE_FILE_PATTERNS = [
'.*/BUILD_INFO-.*', # ACTS test result files.
'.*/AndroidDevice.*', # ACTS test result files.
]
# Regex of result files sorted based on priority. Files can be throttled first
# have higher priority.
RESULT_THROTTLE_PRIORITY = [
'(.*/)?sysinfo/var/log/.*',
'(.*/)?sysinfo/var/log_diff/.*',
'(.*/)?sysinfo/.*',
# The last one matches any file.
'.*',
]
# Regex of file names for Autotest debug logs. These files should be preserved
# without throttling if possible.
AUTOTEST_LOG_PATTERN ='.*\.(DEBUG|ERROR|INFO|WARNING)$'
def _list_files(files, all_files=None):
"""Get all files in the given directories.
@param files: A list of ResultInfo objects for files in a directory.
@param all_files: A list of ResultInfo objects collected so far.
@return: A list of all collected ResultInfo objects.
"""
if all_files is None:
all_files = []
for info in files:
if info.is_dir:
_list_files(info.files, all_files)
else:
all_files.append(info)
return all_files
def sort_result_files(summary):
"""Sort result infos based on priority.
@param summary: A ResultInfo object containing result summary.
@return: A tuple of (sorted_files, grouped_files)
sorted_files: A list of ResultInfo, sorted based on file size and
priority based on RESULT_THROTTLE_PRIORITY.
grouped_files: A dictionary of ResultInfo grouped by each item in
RESULT_THROTTLE_PRIORITY.
"""
all_files = _list_files(summary.files)
# Scan all file paths and group them based on the throttle priority.
grouped_files = {pattern: [] for pattern in RESULT_THROTTLE_PRIORITY}
for info in all_files:
for pattern in RESULT_THROTTLE_PRIORITY:
if re.match(pattern, info.path):
grouped_files[pattern].append(info)
break
sorted_files = []
for pattern in RESULT_THROTTLE_PRIORITY:
# Sort the files in each group by file size, largest first.
infos = grouped_files[pattern]
infos.sort(key=lambda info: -info.trimmed_size)
sorted_files.extend(infos)
return sorted_files, grouped_files
def get_throttleable_files(file_infos, extra_patterns=[]):
"""Filter the files can be throttled.
@param file_infos: A list of ResultInfo objects.
@param extra_patterns: Extra patterns of file path that should not be
throttled.
@yield: ResultInfo objects that can be throttled.
"""
for info in file_infos:
# Skip the files being deleted in earlier throttling.
if info.trimmed_size == 0:
continue
if info.name in NON_THROTTLEABLE_FILE_NAMES:
continue
pattern_matched = False
for pattern in extra_patterns + NON_THROTTLEABLE_FILE_PATTERNS:
if re.match(pattern, info.path):
pattern_matched = True
break
if not pattern_matched:
yield info
def check_throttle_limit(summary, max_result_size_KB):
"""Check if throttling is enough already.
@param summary: A ResultInfo object containing result summary.
@param max_result_size_KB: Maximum test result size in KB.
@return: True if the result directory has been trimmed to be smaller than
max_result_size_KB.
"""
if (summary.trimmed_size <= max_result_size_KB * 1024):
utils_lib.LOG('Maximum result size is reached (current result'
'size is %s (limit is %s).' %
(utils_lib.get_size_string(summary.trimmed_size),
utils_lib.get_size_string(max_result_size_KB * 1024)))
return True
else:
return False
def try_delete_file_on_disk(path):
"""Try to delete the give file on disk.
@param path: Path to the file.
@returns: True if the file is deleted, False otherwise.
"""
try:
utils_lib.LOG('Deleting file %s.' % path)
os.remove(path)
return True
except OSError as e:
utils_lib.LOG('Failed to delete file %s, Error: %s' % (path, e))