• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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"""Utility to deploy and run result utils on a DUT.
6
7This module is the one imported by other Autotest code and run result
8throttling. Other modules in result_tools are designed to be copied to DUT and
9executed with command line. That's why other modules (except view.py and
10unittests) don't import the common module.
11"""
12
13import logging
14import os
15
16import common
17from autotest_lib.client.common_lib import error
18from autotest_lib.client.common_lib import global_config
19from autotest_lib.client.common_lib import utils as client_utils
20
21try:
22    from chromite.lib import metrics
23except ImportError:
24    metrics = client_utils.metrics_mock
25
26
27CONFIG = global_config.global_config
28ENABLE_RESULT_THROTTLING = CONFIG.get_config_value(
29        'AUTOSERV', 'enable_result_throttling', type=bool, default=False)
30
31_THROTTLE_OPTION_FMT = '-m %s'
32_BUILD_DIR_SUMMARY_CMD = '%s/result_tools/utils.py -p %s %s'
33_BUILD_DIR_SUMMARY_TIMEOUT = 120
34_FIND_DIR_SUMMARY_TIMEOUT = 10
35_CLEANUP_DIR_SUMMARY_CMD = '%s/result_tools/utils.py -p %s -d'
36_CLEANUP_DIR_SUMMARY_TIMEOUT = 10
37
38# Default autotest directory on host
39DEFAULT_AUTOTEST_DIR = '/usr/local/autotest'
40
41# File patterns to be excluded from deploying to the dut.
42_EXCLUDES = ['*.pyc', '*unittest.py', 'common.py', '__init__.py', 'runner.py',
43             'view.py']
44
45# A set of hostnames that have result tools already deployed.
46_deployed_duts = set()
47
48def _deploy_result_tools(host):
49    """Send result tools to the dut.
50
51    @param host: Host to run the result tools.
52    """
53    logging.debug('Deploy result utilities to %s', host.hostname)
54    with metrics.SecondsTimer(
55            'chromeos/autotest/job/send_result_tools_duration',
56            fields={'dut_host_name': host.hostname}) as fields:
57        try:
58            result_tools_dir = os.path.dirname(__file__)
59            host.send_file(result_tools_dir, DEFAULT_AUTOTEST_DIR,
60                           excludes = _EXCLUDES)
61            fields['success'] = True
62        except error.AutotestHostRunError:
63            logging.debug('Failed to deploy result tools using `excludes`. Try '
64                          'again without `excludes`.')
65            host.send_file(result_tools_dir, DEFAULT_AUTOTEST_DIR)
66            fields['success'] = False
67        _deployed_duts.add(host.hostname)
68
69
70def run_on_client(host, client_results_dir, cleanup_only=False):
71    """Run result utils on the given host.
72
73    @param host: Host to run the result utils.
74    @param client_results_dir: Path to the results directory on the client.
75    @param cleanup_only: True to delete all existing directory summary files in
76            the given directory.
77    @return: True: If the command runs on client without error.
78             False: If the command failed with error in result throttling.
79    """
80    success = False
81    with metrics.SecondsTimer(
82            'chromeos/autotest/job/dir_summary_collection_duration',
83            fields={'dut_host_name': host.hostname}) as fields:
84        try:
85            if host.hostname not in _deployed_duts:
86                _deploy_result_tools(host)
87            else:
88                logging.debug('result tools are already deployed to %s.',
89                              host.hostname)
90
91            if cleanup_only:
92                logging.debug('Cleaning up directory summary in %s',
93                              client_results_dir)
94                cmd = (_CLEANUP_DIR_SUMMARY_CMD %
95                       (DEFAULT_AUTOTEST_DIR, client_results_dir))
96                host.run(cmd, ignore_status=False,
97                         timeout=_CLEANUP_DIR_SUMMARY_TIMEOUT)
98            else:
99                logging.debug('Getting directory summary for %s',
100                              client_results_dir)
101                throttle_option = ''
102                if ENABLE_RESULT_THROTTLING:
103                    try:
104                        throttle_option = (_THROTTLE_OPTION_FMT %
105                                           host.job.max_result_size_KB)
106                    except AttributeError:
107                        # In case host job is not set, skip throttling.
108                        logging.warn('host object does not have job attribute, '
109                                     'skipping result throttling.')
110                cmd = (_BUILD_DIR_SUMMARY_CMD %
111                       (DEFAULT_AUTOTEST_DIR, client_results_dir,
112                        throttle_option))
113                host.run(cmd, ignore_status=False,
114                         timeout=_BUILD_DIR_SUMMARY_TIMEOUT)
115                success = True
116            fields['success'] = True
117        except error.AutoservRunError:
118            action = 'cleanup' if cleanup_only else 'create'
119            logging.exception(
120                    'Non-critical failure: Failed to %s directory summary for '
121                    '%s.', action, client_results_dir)
122            fields['success'] = False
123
124    return success
125
126
127def collect_last_summary(host, source_path, dest_path,
128                         skip_summary_collection=False):
129    """Collect the last directory summary next to the given file path.
130
131    If the given source_path is a directory, return without collecting any
132    result summary file, as the summary file should have been collected with the
133    directory.
134
135    @param host: The RemoteHost to collect logs from.
136    @param source_path: The remote path to collect the directory summary file
137            from. If the source_path is a file
138    @param dest_path: A path to write the source_path into. The summary file
139            will be saved to the same folder.
140    @param skip_summary_collection: True to skip summary file collection, only
141            to delete the last summary. This is used in case when result
142            collection in the dut failed. Default is set to False.
143    """
144    if not os.path.exists(dest_path):
145        logging.debug('Source path %s does not exist, no directory summary '
146                      'will be collected', dest_path)
147        return
148
149    # Test if source_path is a file.
150    try:
151        host.run('test -f %s' % source_path, timeout=_FIND_DIR_SUMMARY_TIMEOUT)
152        is_source_file = True
153    except error.AutoservRunError:
154        is_source_file = False
155        # No need to collect summary files if the source path is a directory,
156        # as the summary files should have been copied over with the directory.
157        # However, the last summary should be cleaned up so it won't affect
158        # later tests.
159        skip_summary_collection = True
160
161    source_dir = os.path.dirname(source_path) if is_source_file else source_path
162    dest_dir = dest_path if os.path.isdir(dest_path) else dest_path
163
164    # Get the latest directory summary file.
165    try:
166        summary_pattern = os.path.join(source_dir, 'dir_summary_*.json')
167        summary_file = host.run(
168                'ls -t %s | head -1' % summary_pattern,
169                timeout=_FIND_DIR_SUMMARY_TIMEOUT).stdout.strip()
170    except error.AutoservRunError:
171        logging.exception(
172                'Non-critical failure: Failed to locate the latest directory '
173                'summary for %s', source_dir)
174        return
175
176    try:
177        if not skip_summary_collection:
178            host.get_file(
179                    summary_file,
180                    os.path.join(dest_dir, os.path.basename(summary_file)),
181                    preserve_perm=False)
182    finally:
183        # Remove the collected summary file so it won't affect later tests.
184        try:
185            host.run('rm %s' % summary_file,
186                     timeout=_FIND_DIR_SUMMARY_TIMEOUT).stdout.strip()
187        except error.AutoservRunError:
188            logging.exception(
189                    'Non-critical failure: Failed to delete the latest '
190                    'directory summary: %s', summary_file)
191