blob: 142e8b7cf4e549cc146af302ce60c564d13fb198 [file] [log] [blame]
Mike Frysinger0e2cb7a2019-08-20 17:04:52 -04001#!/usr/bin/python2 -u
mbligh1ffd5dc2008-11-25 13:24:05 +00002# Copyright 2007-2008 Martin J. Bligh <mbligh@google.com>, Google Inc.
mbligh82648e52008-11-20 16:54:25 +00003# Released under the GPL v2
mblighdcd57a82007-07-11 23:06:47 +00004
mblighc8949b82007-07-23 16:33:58 +00005"""
Aviv Keshetde6bb192013-01-30 16:17:22 -08006Run a control file through the server side engine
mblighdcd57a82007-07-11 23:06:47 +00007"""
mbligh1ffd5dc2008-11-25 13:24:05 +00008
Fang Deng042c1472014-10-23 13:56:41 -07009import datetime
Paul Hobbs20cc72a2016-08-30 16:57:05 -070010import contextlib
Fang Deng042c1472014-10-23 13:56:41 -070011import getpass
12import logging
13import os
14import re
Prathmesh Prabhu46047362018-03-16 10:33:19 -070015import shutil
Fang Deng042c1472014-10-23 13:56:41 -070016import signal
Dan Shicf4d2032015-03-12 15:04:21 -070017import socket
Fang Deng042c1472014-10-23 13:56:41 -070018import sys
19import traceback
20import time
21import urllib2
mbligh1ffd5dc2008-11-25 13:24:05 +000022
mblighf5427bb2008-04-09 15:55:57 +000023import common
Dan Shi4f8c0242017-07-07 15:34:49 -070024from autotest_lib.client.bin.result_tools import utils as result_utils
25from autotest_lib.client.bin.result_tools import view as result_view
Dan Shia1ecd5c2013-06-06 11:21:31 -070026from autotest_lib.client.common_lib import control_data
Prathmesh Prabhu9a631082018-05-11 17:30:09 -070027from autotest_lib.client.common_lib import enum
Dan Shi32649b82015-08-29 20:53:36 -070028from autotest_lib.client.common_lib import error
Dan Shia1ecd5c2013-06-06 11:21:31 -070029from autotest_lib.client.common_lib import global_config
Prathmesh Prabhu9a631082018-05-11 17:30:09 -070030from autotest_lib.client.common_lib import host_queue_entry_states
31from autotest_lib.client.common_lib import host_states
Allen Lif146e872017-08-15 18:24:31 -070032from autotest_lib.server import results_mocker
Prathmesh Prabhu46047362018-03-16 10:33:19 -070033from autotest_lib.server.cros.dynamic_suite import suite
Prathmesh Prabhua5eecda2016-11-23 16:48:40 -080034
Dan Shi5e2efb72017-02-07 11:40:23 -080035try:
36 from chromite.lib import metrics
Paul Hobbse9fd5572017-08-22 02:48:25 -070037 from chromite.lib import cloud_trace
Dan Shi5e2efb72017-02-07 11:40:23 -080038except ImportError:
Prathmesh Prabhud16c8012017-08-28 11:42:46 -070039 from autotest_lib.client.common_lib import utils as common_utils
40 metrics = common_utils.metrics_mock
Paul Hobbse9fd5572017-08-22 02:48:25 -070041 import mock
42 cloud_trace = mock.MagicMock()
Prathmesh Prabhua5eecda2016-11-23 16:48:40 -080043
Dan Shia06f3e22015-09-03 16:15:15 -070044_CONFIG = global_config.global_config
45
Jakob Jueliche497b552014-09-23 19:11:59 -070046# Number of seconds to wait before returning if testing mode is enabled
Prashanth B6285f6a2014-05-08 18:01:27 -070047TESTING_MODE_SLEEP_SECS = 1
Jakob Jueliche497b552014-09-23 19:11:59 -070048
mbligh9ff89cd2009-09-03 20:28:17 +000049
Kevin Cheng9b6930f2016-07-20 14:57:15 -070050from autotest_lib.server import frontend
showard75cdfee2009-06-10 17:40:41 +000051from autotest_lib.server import server_logging_config
showard043c62a2009-06-10 19:48:57 +000052from autotest_lib.server import server_job, utils, autoserv_parser, autotest
Dan Shia1ecd5c2013-06-06 11:21:31 -070053from autotest_lib.server import utils as server_utils
Paul Hobbs20cc72a2016-08-30 16:57:05 -070054from autotest_lib.server import site_utils
Kevin Chengadc99f92016-07-20 08:21:58 -070055from autotest_lib.server.cros.dynamic_suite import frontend_wrappers
Dan Shicf4d2032015-03-12 15:04:21 -070056from autotest_lib.site_utils import job_directories
Dan Shicf4d2032015-03-12 15:04:21 -070057from autotest_lib.site_utils import lxc
Ben Kwa966db082017-06-05 14:17:23 -070058from autotest_lib.site_utils.lxc import utils as lxc_utils
showard75cdfee2009-06-10 17:40:41 +000059from autotest_lib.client.common_lib import pidfile, logging_manager
mbligh92c0fc22008-11-20 16:52:23 +000060
Paul Hobbs20cc72a2016-08-30 16:57:05 -070061
Dan Shicf4d2032015-03-12 15:04:21 -070062# Control segment to stage server-side package.
63STAGE_SERVER_SIDE_PACKAGE_CONTROL_FILE = server_job._control_segment_path(
64 'stage_server_side_package')
65
Dan Shia06f3e22015-09-03 16:15:15 -070066# Command line to start servod in a moblab.
67START_SERVOD_CMD = 'sudo start servod BOARD=%s PORT=%s'
68STOP_SERVOD_CMD = 'sudo stop servod'
69
Prathmesh Prabhu46047362018-03-16 10:33:19 -070070_AUTOTEST_ROOT = os.path.realpath(os.path.join(os.path.dirname(__file__), '..'))
71_CONTROL_FILE_FROM_CONTROL_NAME = 'control.from_control_name'
72
Prathmesh Prabhu7b05ecc2018-08-13 14:44:14 -070073_LXC_JOB_FOLDER = 'lxc_job_folder'
74
Alex Millerf1af17e2013-01-09 22:50:32 -080075def log_alarm(signum, frame):
76 logging.error("Received SIGALARM. Ignoring and continuing on.")
Alex Miller0528d6f2013-01-11 10:49:48 -080077 sys.exit(1)
Alex Millerf1af17e2013-01-09 22:50:32 -080078
Dan Shicf4d2032015-03-12 15:04:21 -070079
80def _get_machines(parser):
81 """Get a list of machine names from command line arg -m or a file.
82
83 @param parser: Parser for the command line arguments.
84
85 @return: A list of machine names from command line arg -m or the
86 machines file specified in the command line arg -M.
87 """
88 if parser.options.machines:
89 machines = parser.options.machines.replace(',', ' ').strip().split()
90 else:
91 machines = []
92 machines_file = parser.options.machines_file
93 if machines_file:
94 machines = []
95 for m in open(machines_file, 'r').readlines():
96 # remove comments, spaces
97 m = re.sub('#.*', '', m).strip()
98 if m:
99 machines.append(m)
100 logging.debug('Read list of machines from file: %s', machines_file)
101 logging.debug('Machines: %s', ','.join(machines))
102
103 if machines:
104 for machine in machines:
105 if not machine or re.search('\s', machine):
106 parser.parser.error("Invalid machine: %s" % str(machine))
107 machines = list(set(machines))
108 machines.sort()
109 return machines
110
111
Prathmesh Prabhu588007d2017-06-15 00:31:31 -0700112def _stage_ssp(parser, resultsdir):
Dan Shicf4d2032015-03-12 15:04:21 -0700113 """Stage server-side package.
114
115 This function calls a control segment to stage server-side package based on
116 the job and autoserv command line option. The detail implementation could
117 be different for each host type. Currently, only CrosHost has
118 stage_server_side_package function defined.
119 The script returns None if no server-side package is available. However,
120 it may raise exception if it failed for reasons other than artifact (the
121 server-side package) not found.
122
123 @param parser: Command line arguments parser passed in the autoserv process.
Prathmesh Prabhu588007d2017-06-15 00:31:31 -0700124 @param resultsdir: Folder to store results. This could be different from
125 parser.options.results: parser.options.results can be set to None
126 for results to be stored in a temp folder. resultsdir can be None
127 for autoserv run requires no logging.
Dan Shicf4d2032015-03-12 15:04:21 -0700128
Prathmesh Prabhu80f9cb82018-08-11 18:32:08 -0700129 @return: url to the autotest server-side package. None in case of errors.
Dan Shicf4d2032015-03-12 15:04:21 -0700130 """
Kevin Chengadc99f92016-07-20 08:21:58 -0700131 machines_list = _get_machines(parser)
Prathmesh Prabhu7fc39c52018-03-21 14:08:30 -0700132 machines_list = server_job.get_machine_dicts(
133 machine_names=machines_list,
134 store_dir=os.path.join(resultsdir, parser.options.host_info_subdir),
135 in_lab=parser.options.lab,
136 use_shadow_store=not parser.options.local_only_host_info,
137 host_attributes=parser.options.host_attributes,
138 )
Kevin Chengadc99f92016-07-20 08:21:58 -0700139
Kevin Chengadc99f92016-07-20 08:21:58 -0700140 namespace = {'machines': machines_list,
Jacob Kopczynski9a3a0c32018-07-18 18:36:20 -0700141 'isolate_hash': parser.options.isolate,
Richard Barnette71854c72018-03-30 14:22:09 -0700142 'image': parser.options.test_source_build}
Dan Shicf4d2032015-03-12 15:04:21 -0700143 script_locals = {}
144 execfile(STAGE_SERVER_SIDE_PACKAGE_CONTROL_FILE, namespace, script_locals)
Prathmesh Prabhu80f9cb82018-08-11 18:32:08 -0700145 ssp_url = script_locals['ssp_url']
146 if not ssp_url:
147 logging.error('Failed to stage SSP package: %s',
148 script_locals['error_msg'])
149 logging.error('This job will fail later, when attempting to run with'
150 ' SSP')
151 return ssp_url
Dan Shicf4d2032015-03-12 15:04:21 -0700152
153
Ben Kwabedacad2017-08-28 12:20:38 -0700154def _run_with_ssp(job, container_id, job_id, results, parser, ssp_url,
Prathmesh Prabhu7b05ecc2018-08-13 14:44:14 -0700155 machines):
Dan Shicf4d2032015-03-12 15:04:21 -0700156 """Run the server job with server-side packaging.
157
Dan Shi37befda2015-12-07 13:16:56 -0800158 @param job: The server job object.
Ben Kwabedacad2017-08-28 12:20:38 -0700159 @param container_id: ID of the container to run the test.
Dan Shicf4d2032015-03-12 15:04:21 -0700160 @param job_id: ID of the test job.
161 @param results: Folder to store results. This could be different from
162 parser.options.results:
163 parser.options.results can be set to None for results to be
164 stored in a temp folder.
165 results can be None for autoserv run requires no logging.
166 @param parser: Command line parser that contains the options.
167 @param ssp_url: url of the staged server-side package.
Dan Shi3be35af2016-08-25 23:22:40 -0700168 @param machines: A list of machines to run the test.
Dan Shicf4d2032015-03-12 15:04:21 -0700169 """
Prathmesh Prabhu80f9cb82018-08-11 18:32:08 -0700170 if not ssp_url:
171 job.record('FAIL', None, None,
172 'Failed to stage server-side package')
173 raise error.AutoservError('Failed to stage server-side package')
174
Dan Shicf4d2032015-03-12 15:04:21 -0700175 bucket = lxc.ContainerBucket()
176 control = (parser.args[0] if len(parser.args) > 0 and parser.args[0] != ''
177 else None)
Dan Shi37befda2015-12-07 13:16:56 -0800178 try:
Dan Shi3be35af2016-08-25 23:22:40 -0700179 dut_name = machines[0] if len(machines) >= 1 else None
Ben Kwabedacad2017-08-28 12:20:38 -0700180 test_container = bucket.setup_test(container_id, job_id, ssp_url,
Dan Shiafa63872016-02-23 15:32:31 -0800181 results, control=control,
Prathmesh Prabhu7b05ecc2018-08-13 14:44:14 -0700182 job_folder=_LXC_JOB_FOLDER,
Jacob Kopczynski22bb5962018-07-24 11:56:02 -0700183 dut_name=dut_name,
184 isolate_hash=parser.options.isolate)
Dan Shi37befda2015-12-07 13:16:56 -0800185 except Exception as e:
186 job.record('FAIL', None, None,
187 'Failed to setup container for test: %s. Check logs in '
188 'ssp_logs folder for more details.' % e)
189 raise
190
Dan Shicf4d2032015-03-12 15:04:21 -0700191 args = sys.argv[:]
192 args.remove('--require-ssp')
Dan Shi77b79a62015-07-29 16:22:05 -0700193 # --parent_job_id is only useful in autoserv running in host, not in
194 # container. Include this argument will cause test to fail for builds before
195 # CL 286265 was merged.
196 if '--parent_job_id' in args:
197 index = args.index('--parent_job_id')
198 args.remove('--parent_job_id')
199 # Remove the actual parent job id in command line arg.
200 del args[index]
Dan Shicf4d2032015-03-12 15:04:21 -0700201
202 # A dictionary of paths to replace in the command line. Key is the path to
203 # be replaced with the one in value.
204 paths_to_replace = {}
205 # Replace the control file path with the one in container.
206 if control:
207 container_control_filename = os.path.join(
208 lxc.CONTROL_TEMP_PATH, os.path.basename(control))
209 paths_to_replace[control] = container_control_filename
210 # Update result directory with the one in container.
Prathmesh Prabhu7b05ecc2018-08-13 14:44:14 -0700211 container_result_dir = os.path.join(lxc.RESULT_DIR_FMT % _LXC_JOB_FOLDER)
Dan Shicf4d2032015-03-12 15:04:21 -0700212 if parser.options.results:
Dan Shicf4d2032015-03-12 15:04:21 -0700213 paths_to_replace[parser.options.results] = container_result_dir
Dan Shicf4d2032015-03-12 15:04:21 -0700214 args = [paths_to_replace.get(arg, arg) for arg in args]
215
216 # Apply --use-existing-results, results directory is aready created and
217 # mounted in container. Apply this arg to avoid exception being raised.
218 if not '--use-existing-results' in args:
219 args.append('--use-existing-results')
220
221 # Make sure autoserv running in container using a different pid file.
222 if not '--pidfile-label' in args:
223 args.extend(['--pidfile-label', 'container_autoserv'])
224
Dan Shid1f51232015-04-18 00:29:14 -0700225 cmd_line = ' '.join(["'%s'" % arg if ' ' in arg else arg for arg in args])
Dan Shicf4d2032015-03-12 15:04:21 -0700226 logging.info('Run command in container: %s', cmd_line)
Dan Shi37bee222015-04-13 15:46:47 -0700227 success = False
Dan Shicf4d2032015-03-12 15:04:21 -0700228 try:
229 test_container.attach_run(cmd_line)
Dan Shi37bee222015-04-13 15:46:47 -0700230 success = True
Dan Shi9d3454e2015-12-08 09:16:08 -0800231 except Exception as e:
232 # If the test run inside container fails without generating any log,
233 # write a message to status.log to help troubleshooting.
234 debug_files = os.listdir(os.path.join(results, 'debug'))
235 if not debug_files:
236 job.record('FAIL', None, None,
237 'Failed to run test inside the container: %s. Check '
238 'logs in ssp_logs folder for more details.' % e)
239 raise
Dan Shicf4d2032015-03-12 15:04:21 -0700240 finally:
Prathmesh Prabhua5eecda2016-11-23 16:48:40 -0800241 metrics.Counter(
242 'chromeos/autotest/experimental/execute_job_in_ssp').increment(
243 fields={'success': success})
Dan Shicf4d2032015-03-12 15:04:21 -0700244 test_container.destroy()
245
246
Dan Shi3f1b8a52015-04-21 11:11:06 -0700247def correct_results_folder_permission(results):
248 """Make sure the results folder has the right permission settings.
249
250 For tests running with server-side packaging, the results folder has the
251 owner of root. This must be changed to the user running the autoserv
252 process, so parsing job can access the results folder.
253 TODO(dshi): crbug.com/459344 Remove this function when test container can be
254 unprivileged container.
255
256 @param results: Path to the results folder.
257
258 """
259 if not results:
260 return
261
Aviv Keshetc03de792017-07-18 14:24:31 -0700262 utils.run('sudo -n chown -R %s "%s"' % (os.getuid(), results))
263 utils.run('sudo -n chgrp -R %s "%s"' % (os.getgid(), results))
Dan Shi3f1b8a52015-04-21 11:11:06 -0700264
265
Dan Shia06f3e22015-09-03 16:15:15 -0700266def _start_servod(machine):
267 """Try to start servod in moblab if it's not already running or running with
268 different board or port.
269
270 @param machine: Name of the dut used for test.
271 """
272 if not utils.is_moblab():
273 return
274
Dan Shi1cded882015-09-23 16:52:26 -0700275 logging.debug('Trying to start servod.')
Dan Shia06f3e22015-09-03 16:15:15 -0700276 try:
Kevin Cheng9b6930f2016-07-20 14:57:15 -0700277 afe = frontend.AFE()
Dan Shia06f3e22015-09-03 16:15:15 -0700278 board = server_utils.get_board_from_afe(machine, afe)
279 hosts = afe.get_hosts(hostname=machine)
280 servo_host = hosts[0].attributes.get('servo_host', None)
281 servo_port = hosts[0].attributes.get('servo_port', 9999)
282 if not servo_host in ['localhost', '127.0.0.1']:
Dan Shi1cded882015-09-23 16:52:26 -0700283 logging.warn('Starting servod is aborted. The dut\'s servo_host '
284 'attribute is not set to localhost.')
Dan Shia06f3e22015-09-03 16:15:15 -0700285 return
286 except (urllib2.HTTPError, urllib2.URLError):
287 # Ignore error if RPC failed to get board
288 logging.error('Failed to get board name from AFE. Start servod is '
289 'aborted')
290 return
291
292 try:
293 pid = utils.run('pgrep servod').stdout
294 cmd_line = utils.run('ps -fp %s' % pid).stdout
295 if ('--board %s' % board in cmd_line and
296 '--port %s' % servo_port in cmd_line):
297 logging.debug('Servod is already running with given board and port.'
298 ' There is no need to restart servod.')
299 return
300 logging.debug('Servod is running with different board or port. '
301 'Stopping existing servod.')
302 utils.run('sudo stop servod')
303 except error.CmdError:
304 # servod is not running.
305 pass
306
307 try:
308 utils.run(START_SERVOD_CMD % (board, servo_port))
309 logging.debug('Servod is started')
310 except error.CmdError as e:
311 logging.error('Servod failed to be started, error: %s', e)
312
313
Prathmesh Prabhu46047362018-03-16 10:33:19 -0700314def _control_path_on_disk(control_name):
315 """Find the control file corresponding to the given control name, on disk.
316
317 @param control_name: NAME attribute of the control file to fetch.
318 @return: Path to the control file.
319 """
320 cf_getter = suite.create_fs_getter(_AUTOTEST_ROOT)
321 control_name_predicate = suite.test_name_matches_pattern_predicate(
322 '^%s$' % control_name)
323 tests = suite.find_and_parse_tests(cf_getter, control_name_predicate)
324 if not tests:
325 raise error.AutoservError(
326 'Failed to find any control files with NAME %s' % control_name)
327 if len(tests) > 1:
328 logging.error('Found more than one control file with NAME %s: %s',
329 control_name, [t.path for t in tests])
330 raise error.AutoservError(
331 'Found more than one control file with NAME %s' % control_name)
332 return tests[0].path
333
334
Prathmesh Prabhu492ef462018-08-11 17:10:33 -0700335def _stage_control_file(control_name, results_dir):
336 """Stage the control file to execute from local autotest checkout.
Prathmesh Prabhu46047362018-03-16 10:33:19 -0700337
Prathmesh Prabhu492ef462018-08-11 17:10:33 -0700338 @param control_name: Name of the control file to stage.
Prathmesh Prabhu46047362018-03-16 10:33:19 -0700339 @param results_dir: Results directory to stage the control file into.
340 @return: Absolute path to the staged control file.
341 """
Prathmesh Prabhu492ef462018-08-11 17:10:33 -0700342 control_path = _control_path_on_disk(control_name)
Prathmesh Prabhu46047362018-03-16 10:33:19 -0700343 new_control = os.path.join(results_dir, _CONTROL_FILE_FROM_CONTROL_NAME)
344 shutil.copy2(control_path, new_control)
345 return new_control
346
347
Dan Shic68fefb2015-04-07 10:10:52 -0700348def run_autoserv(pid_file_manager, results, parser, ssp_url, use_ssp):
Dan Shicf4d2032015-03-12 15:04:21 -0700349 """Run server job with given options.
350
351 @param pid_file_manager: PidFileManager used to monitor the autoserv process
352 @param results: Folder to store results.
353 @param parser: Parser for the command line arguments.
354 @param ssp_url: Url to server-side package.
Dan Shic68fefb2015-04-07 10:10:52 -0700355 @param use_ssp: Set to True to run with server-side packaging.
Dan Shicf4d2032015-03-12 15:04:21 -0700356 """
jadmanski0afbb632008-06-06 21:10:57 +0000357 # send stdin to /dev/null
358 dev_null = os.open(os.devnull, os.O_RDONLY)
359 os.dup2(dev_null, sys.stdin.fileno())
360 os.close(dev_null)
mblighdbf37612007-11-24 19:38:11 +0000361
Dan Shie8aeb662016-06-30 11:22:03 -0700362 # Create separate process group if the process is not a process group
363 # leader. This allows autoserv process to keep running after the caller
364 # process (drone manager call) exits.
365 if os.getpid() != os.getpgid(0):
366 os.setsid()
mbligh1d42d4e2007-11-05 22:42:00 +0000367
Dan Shicf4d2032015-03-12 15:04:21 -0700368 # Container name is predefined so the container can be destroyed in
369 # handle_sigterm.
370 job_or_task_id = job_directories.get_job_id_or_task_id(
371 parser.options.results)
Ben Kwabedacad2017-08-28 12:20:38 -0700372 container_id = lxc.ContainerId(job_or_task_id, time.time(), os.getpid())
Dan Shicf4d2032015-03-12 15:04:21 -0700373
jadmanski0afbb632008-06-06 21:10:57 +0000374 # Implement SIGTERM handler
mblighc2299562009-07-02 19:00:36 +0000375 def handle_sigterm(signum, frame):
Simran Basi9d9b7292013-10-16 16:44:07 -0700376 logging.debug('Received SIGTERM')
mblighff7d61f2008-12-22 14:53:35 +0000377 if pid_file_manager:
378 pid_file_manager.close_file(1, signal.SIGTERM)
Simran Basi49e21e62013-10-17 12:40:33 -0700379 logging.debug('Finished writing to pid_file. Killing process.')
Dan Shi3f1b8a52015-04-21 11:11:06 -0700380
381 # Update results folder's file permission. This needs to be done ASAP
382 # before the parsing process tries to access the log.
383 if use_ssp and results:
384 correct_results_folder_permission(results)
385
Simran Basid6b83772014-01-06 16:31:30 -0800386 # This sleep allows the pending output to be logged before the kill
387 # signal is sent.
388 time.sleep(.1)
Dan Shic68fefb2015-04-07 10:10:52 -0700389 if use_ssp:
Dan Shicf4d2032015-03-12 15:04:21 -0700390 logging.debug('Destroy container %s before aborting the autoserv '
Ben Kwabedacad2017-08-28 12:20:38 -0700391 'process.', container_id)
Dan Shicf4d2032015-03-12 15:04:21 -0700392 try:
393 bucket = lxc.ContainerBucket()
Ben Kwabedacad2017-08-28 12:20:38 -0700394 container = bucket.get_container(container_id)
Dan Shicf4d2032015-03-12 15:04:21 -0700395 if container:
396 container.destroy()
Jacob Kopczynskib83de7a2019-05-13 15:37:10 -0700397 logging.debug("Container %s destroyed.", container_id)
Dan Shicf4d2032015-03-12 15:04:21 -0700398 else:
Ben Kwabedacad2017-08-28 12:20:38 -0700399 logging.debug('Container %s is not found.', container_id)
Jacob Kopczynskie89ae402019-11-21 17:29:15 -0800400 bucket.scrub_container_location(container_id)
Dan Shicf4d2032015-03-12 15:04:21 -0700401 except:
402 # Handle any exception so the autoserv process can be aborted.
Dan Shi65374e22016-09-15 16:14:05 -0700403 logging.exception('Failed to destroy container %s.',
Ben Kwabedacad2017-08-28 12:20:38 -0700404 container_id)
Dan Shie4a4f9f2015-07-20 09:00:25 -0700405 # Try to correct the result file permission again after the
406 # container is destroyed, as the container might have created some
407 # new files in the result folder.
408 if results:
409 correct_results_folder_permission(results)
Dan Shicf4d2032015-03-12 15:04:21 -0700410
jadmanski0afbb632008-06-06 21:10:57 +0000411 os.killpg(os.getpgrp(), signal.SIGKILL)
mblighfaf0cd42007-11-19 16:00:24 +0000412
jadmanski0afbb632008-06-06 21:10:57 +0000413 # Set signal handler
mblighc2299562009-07-02 19:00:36 +0000414 signal.signal(signal.SIGTERM, handle_sigterm)
mbligha46678d2008-05-01 20:00:01 +0000415
Simran Basid6b83772014-01-06 16:31:30 -0800416 # faulthandler is only needed to debug in the Lab and is not avaliable to
417 # be imported in the chroot as part of VMTest, so Try-Except it.
418 try:
419 import faulthandler
420 faulthandler.register(signal.SIGTERM, all_threads=True, chain=True)
421 logging.debug('faulthandler registered on SIGTERM.')
422 except ImportError:
Christopher Grant4beca022015-06-16 15:14:47 -0400423 sys.exc_clear()
Simran Basid6b83772014-01-06 16:31:30 -0800424
David Rochberg8a60d1e2011-02-01 14:22:07 -0500425 # Ignore SIGTTOU's generated by output from forked children.
426 signal.signal(signal.SIGTTOU, signal.SIG_IGN)
427
Alex Millerf1af17e2013-01-09 22:50:32 -0800428 # If we received a SIGALARM, let's be loud about it.
429 signal.signal(signal.SIGALRM, log_alarm)
430
mbligha5f5e542009-12-30 16:57:49 +0000431 # Server side tests that call shell scripts often depend on $USER being set
432 # but depending on how you launch your autotest scheduler it may not be set.
433 os.environ['USER'] = getpass.getuser()
434
mblighb2bea302008-07-24 20:25:57 +0000435 label = parser.options.label
mbligh374f3412009-05-13 21:29:45 +0000436 group_name = parser.options.group_name
mblighb2bea302008-07-24 20:25:57 +0000437 user = parser.options.user
438 client = parser.options.client
439 server = parser.options.server
mblighb2bea302008-07-24 20:25:57 +0000440 verify = parser.options.verify
441 repair = parser.options.repair
showard45ae8192008-11-05 19:32:53 +0000442 cleanup = parser.options.cleanup
Alex Millercb79ba72013-05-29 14:43:00 -0700443 provision = parser.options.provision
Dan Shi07e09af2013-04-12 09:31:29 -0700444 reset = parser.options.reset
Alex Miller667b5f22014-02-28 15:33:39 -0800445 job_labels = parser.options.job_labels
mblighb2bea302008-07-24 20:25:57 +0000446 no_tee = parser.options.no_tee
mblighe7d9c602009-07-02 19:02:33 +0000447 execution_tag = parser.options.execution_tag
jadmanski0afbb632008-06-06 21:10:57 +0000448 ssh_user = parser.options.ssh_user
449 ssh_port = parser.options.ssh_port
450 ssh_pass = parser.options.ssh_pass
jadmanskidef0c3c2009-03-25 20:07:10 +0000451 collect_crashinfo = parser.options.collect_crashinfo
mblighe0cbc912010-03-11 18:03:07 +0000452 control_filename = parser.options.control_filename
beepscb6f1e22013-06-28 19:14:10 -0700453 verify_job_repo_url = parser.options.verify_job_repo_url
Christopher Wileyf594c5e2013-07-03 18:25:30 -0700454 skip_crash_collection = parser.options.skip_crash_collection
Aviv Keshet18ee3142013-08-12 15:01:51 -0700455 ssh_verbosity = int(parser.options.ssh_verbosity)
Fang Deng6cc20de2013-09-06 15:47:32 -0700456 ssh_options = parser.options.ssh_options
Dan Shib669cbd2013-09-13 11:17:17 -0700457 no_use_packaging = parser.options.no_use_packaging
Simran Basi1bf60eb2015-12-01 16:39:29 -0800458 in_lab = bool(parser.options.lab)
mbligha46678d2008-05-01 20:00:01 +0000459
mblighb2bea302008-07-24 20:25:57 +0000460 # can't be both a client and a server side test
461 if client and server:
Eric Li861b2d52011-02-04 14:50:35 -0800462 parser.parser.error("Can not specify a test as both server and client!")
mblighb2bea302008-07-24 20:25:57 +0000463
Alex Millercb79ba72013-05-29 14:43:00 -0700464 if provision and client:
465 parser.parser.error("Cannot specify provisioning and client!")
466
467 is_special_task = (verify or repair or cleanup or collect_crashinfo or
Dan Shi07e09af2013-04-12 09:31:29 -0700468 provision or reset)
Allen Li5913d8c2018-09-25 17:56:31 -0700469 use_client_trampoline = False
Prathmesh Prabhu46047362018-03-16 10:33:19 -0700470 if parser.options.control_name:
Prathmesh Prabhu412d1512018-08-11 17:26:04 -0700471 if use_ssp:
472 # When use_ssp is True, autoserv will be re-executed inside a
473 # container preserving the --control-name argument. Control file
474 # will be staged inside the rexecuted autoserv.
475 control = None
476 else:
Allen Li5913d8c2018-09-25 17:56:31 -0700477 try:
478 control = _stage_control_file(parser.options.control_name,
479 results)
480 except error.AutoservError as e:
481 logging.info("Using client trampoline because of: %s", e)
482 control = parser.options.control_name
483 use_client_trampoline = True
484
Prathmesh Prabhu46047362018-03-16 10:33:19 -0700485 elif parser.args:
486 control = parser.args[0]
487 else:
Prathmesh Prabhu412d1512018-08-11 17:26:04 -0700488 if not is_special_task:
489 parser.parser.error("Missing argument: control file")
Prathmesh Prabhu46047362018-03-16 10:33:19 -0700490 control = None
491
Aviv Keshet18ee3142013-08-12 15:01:51 -0700492 if ssh_verbosity > 0:
493 # ssh_verbosity is an integer between 0 and 3, inclusive
494 ssh_verbosity_flag = '-' + 'v' * ssh_verbosity
Fang Dengd1c2b732013-08-20 12:59:46 -0700495 else:
496 ssh_verbosity_flag = ''
Aviv Keshet18ee3142013-08-12 15:01:51 -0700497
Dan Shicf4d2032015-03-12 15:04:21 -0700498 machines = _get_machines(parser)
mbligh374f3412009-05-13 21:29:45 +0000499 if group_name and len(machines) < 2:
Dan Shicf4d2032015-03-12 15:04:21 -0700500 parser.parser.error('-G %r may only be supplied with more than one '
501 'machine.' % group_name)
mbligh374f3412009-05-13 21:29:45 +0000502
Jacob Kopczynski26a020d2019-12-06 15:11:34 -0800503 logging.debug("Parser.args is %r", parser.args)
504 try:
505 logging.debug("Parser.options.args is %r", parser.options.args)
506 except AttributeError:
507 logging.debug("No Parser.options.args.")
508
Prathmesh Prabhu7fc39c52018-03-21 14:08:30 -0700509 job_kwargs = {
510 'control': control,
511 'args': parser.args[1:],
512 'resultdir': results,
513 'label': label,
514 'user': user,
515 'machines': machines,
516 'machine_dict_list': server_job.get_machine_dicts(
517 machine_names=machines,
518 store_dir=os.path.join(results,
519 parser.options.host_info_subdir),
520 in_lab=in_lab,
521 use_shadow_store=not parser.options.local_only_host_info,
522 host_attributes=parser.options.host_attributes,
523 ),
524 'client': client,
Prathmesh Prabhu7fc39c52018-03-21 14:08:30 -0700525 'ssh_user': ssh_user,
526 'ssh_port': ssh_port,
527 'ssh_pass': ssh_pass,
528 'ssh_verbosity_flag': ssh_verbosity_flag,
529 'ssh_options': ssh_options,
Prathmesh Prabhu7fc39c52018-03-21 14:08:30 -0700530 'group_name': group_name,
531 'tag': execution_tag,
532 'disable_sysinfo': parser.options.disable_sysinfo,
533 'in_lab': in_lab,
Allen Li5913d8c2018-09-25 17:56:31 -0700534 'use_client_trampoline': use_client_trampoline,
Prathmesh Prabhu7fc39c52018-03-21 14:08:30 -0700535 }
Dan Shi70647ca2015-07-16 22:52:35 -0700536 if parser.options.parent_job_id:
Prathmesh Prabhu7fc39c52018-03-21 14:08:30 -0700537 job_kwargs['parent_job_id'] = int(parser.options.parent_job_id)
mblighe0cbc912010-03-11 18:03:07 +0000538 if control_filename:
Prathmesh Prabhu7fc39c52018-03-21 14:08:30 -0700539 job_kwargs['control_filename'] = control_filename
540 job = server_job.server_job(**job_kwargs)
Dan Shicf4d2032015-03-12 15:04:21 -0700541
showard75cdfee2009-06-10 17:40:41 +0000542 job.logging.start_logging()
mbligha46678d2008-05-01 20:00:01 +0000543
mbligh161fe6f2008-06-19 16:26:04 +0000544 # perform checks
545 job.precheck()
546
jadmanski0afbb632008-06-06 21:10:57 +0000547 # run the job
548 exit_code = 0
Dan Shic1b8bdd2015-09-14 23:11:24 -0700549 auto_start_servod = _CONFIG.get_config_value(
550 'AUTOSERV', 'auto_start_servod', type=bool, default=False)
Paul Hobbs20cc72a2016-08-30 16:57:05 -0700551
Prathmesh Prabhu27bba962017-01-24 15:13:07 -0800552 site_utils.SetupTsMonGlobalState('autoserv', indirect=False,
Aviv Keshetaece2042018-07-17 16:27:22 -0700553 short_lived=True)
jadmanski0afbb632008-06-06 21:10:57 +0000554 try:
Prathmesh Prabhu27bba962017-01-24 15:13:07 -0800555 try:
556 if repair:
557 if auto_start_servod and len(machines) == 1:
558 _start_servod(machines[0])
559 job.repair(job_labels)
560 elif verify:
561 job.verify(job_labels)
562 elif provision:
563 job.provision(job_labels)
564 elif reset:
565 job.reset(job_labels)
566 elif cleanup:
567 job.cleanup(job_labels)
568 else:
569 if auto_start_servod and len(machines) == 1:
570 _start_servod(machines[0])
571 if use_ssp:
572 try:
Ben Kwabedacad2017-08-28 12:20:38 -0700573 _run_with_ssp(job, container_id, job_or_task_id,
Prathmesh Prabhu7b05ecc2018-08-13 14:44:14 -0700574 results, parser, ssp_url, machines)
Prathmesh Prabhu27bba962017-01-24 15:13:07 -0800575 finally:
576 # Update the ownership of files in result folder.
577 correct_results_folder_permission(results)
Dan Shicf4d2032015-03-12 15:04:21 -0700578 else:
Prathmesh Prabhu27bba962017-01-24 15:13:07 -0800579 if collect_crashinfo:
580 # Update the ownership of files in result folder. If the
581 # job to collect crashinfo was running inside container
582 # (SSP) and crashed before correcting folder permission,
583 # the result folder might have wrong permission setting.
Dan Shiafa63872016-02-23 15:32:31 -0800584 try:
585 correct_results_folder_permission(results)
Prathmesh Prabhu27bba962017-01-24 15:13:07 -0800586 except:
587 # Ignore any error as the user may not have root
588 # permission to run sudo command.
589 pass
Aviv Keshet92bf7b62017-02-13 15:34:03 -0800590 metric_name = ('chromeos/autotest/experimental/'
591 'autoserv_job_run_duration')
592 f = {'in_container': utils.is_in_container(),
593 'success': False}
594 with metrics.SecondsTimer(metric_name, fields=f) as c:
Richard Barnette71854c72018-03-30 14:22:09 -0700595 job.run(verify_job_repo_url=verify_job_repo_url,
Aviv Keshet92bf7b62017-02-13 15:34:03 -0800596 only_collect_crashinfo=collect_crashinfo,
597 skip_crash_collection=skip_crash_collection,
598 job_labels=job_labels,
599 use_packaging=(not no_use_packaging))
600 c['success'] = True
601
Prathmesh Prabhu27bba962017-01-24 15:13:07 -0800602 finally:
Hidehiko Abe06893302017-06-24 07:32:38 +0900603 job.close()
Dan Shiffd5b822017-07-14 11:16:23 -0700604 # Special task doesn't run parse, so result summary needs to be
605 # built here.
606 if results and (repair or verify or reset or cleanup or provision):
Dan Shi4f8c0242017-07-07 15:34:49 -0700607 # Throttle the result on the server side.
608 try:
609 result_utils.execute(
610 results, control_data.DEFAULT_MAX_RESULT_SIZE_KB)
611 except:
612 logging.exception(
613 'Non-critical failure: Failed to throttle results '
614 'in directory %s.', results)
615 # Build result view and report metrics for result sizes.
Dan Shiffd5b822017-07-14 11:16:23 -0700616 site_utils.collect_result_sizes(results)
jadmanski0afbb632008-06-06 21:10:57 +0000617 except:
jadmanski27b37ea2008-10-29 23:54:31 +0000618 exit_code = 1
jadmanski0afbb632008-06-06 21:10:57 +0000619 traceback.print_exc()
Prathmesh Prabhu27bba962017-01-24 15:13:07 -0800620 finally:
621 metrics.Flush()
mbligha46678d2008-05-01 20:00:01 +0000622
jadmanski27b37ea2008-10-29 23:54:31 +0000623 sys.exit(exit_code)
mbligha46678d2008-05-01 20:00:01 +0000624
625
Prathmesh Prabhu9a631082018-05-11 17:30:09 -0700626# Job breakdown statuses
627_hs = host_states.Status
628_qs = host_queue_entry_states.Status
629_status_list = [
630 _qs.QUEUED, _qs.RESETTING, _qs.VERIFYING,
631 _qs.PROVISIONING, _hs.REPAIRING, _qs.CLEANING,
632 _qs.RUNNING, _qs.GATHERING, _qs.PARSING]
633_JOB_OVERHEAD_STATUS = enum.Enum(*_status_list, string_values=True)
Paul Hobbs68d98592017-08-22 02:22:49 -0700634
635
636def get_job_status(options):
637 """Returns the HQE Status for this run.
638
639 @param options: parser options.
640 """
Prathmesh Prabhu9a631082018-05-11 17:30:09 -0700641 s = _JOB_OVERHEAD_STATUS
Fang Deng042c1472014-10-23 13:56:41 -0700642 task_mapping = {
643 'reset': s.RESETTING, 'verify': s.VERIFYING,
644 'provision': s.PROVISIONING, 'repair': s.REPAIRING,
645 'cleanup': s.CLEANING, 'collect_crashinfo': s.GATHERING}
Paul Hobbs68d98592017-08-22 02:22:49 -0700646 match = [task for task in task_mapping if getattr(options, task, False)]
647 return task_mapping[match[0]] if match else s.RUNNING
Fang Deng042c1472014-10-23 13:56:41 -0700648
649
Prathmesh Prabhu83b598c2018-08-13 17:03:07 -0700650def _require_ssp_from_control(control_name):
651 """Read the value of REQUIRE_SSP from test control file.
652
653 This reads the control file from the prod checkout of autotest and uses that
654 to determine whether to even stage the SSP package on a devserver.
655
656 This means:
657 [1] Any change in REQUIRE_SSP directive in a test requires a prod-push to go
658 live.
659 [2] This function may find that the control file does not exist but the SSP
660 package may contain the test file. This function conservatively returns True
661 in that case.
662
663 This function is called very early in autoserv, before logging is setup.
664 """
665 if not control_name:
666 return True
Allen Lidc1b98d2018-09-12 13:56:06 -0700667 try:
668 path = _control_path_on_disk(control_name)
Allen Liaa241662018-09-24 13:47:04 -0700669 except error.AutoservError as e:
Allen Lidc1b98d2018-09-12 13:56:06 -0700670 sys.stderr.write("autoserv: Could not determine control file path,"
Allen Liaa241662018-09-24 13:47:04 -0700671 " assuming we need SSP: %s\n" % e)
Allen Lidc1b98d2018-09-12 13:56:06 -0700672 sys.stderr.flush()
673 return True
Prathmesh Prabhu83b598c2018-08-13 17:03:07 -0700674 if not os.path.isfile(path):
675 return True
676 control = control_data.parse_control(path)
677 # There must be explicit directive in the control file to disable SSP.
678 if not control or control.require_ssp is None:
679 return True
680 return control.require_ssp
681
682
mbligha46678d2008-05-01 20:00:01 +0000683def main():
Fang Deng042c1472014-10-23 13:56:41 -0700684 start_time = datetime.datetime.now()
jadmanski0afbb632008-06-06 21:10:57 +0000685 parser = autoserv_parser.autoserv_parser
mbligha5cb4062009-02-17 15:53:39 +0000686 parser.parse_args()
mbligha46678d2008-05-01 20:00:01 +0000687
jadmanski0afbb632008-06-06 21:10:57 +0000688 if len(sys.argv) == 1:
689 parser.parser.print_help()
690 sys.exit(1)
mbligha6f13082008-06-05 23:53:46 +0000691
showard75cdfee2009-06-10 17:40:41 +0000692 if parser.options.no_logging:
693 results = None
694 else:
695 results = parser.options.results
mbligh80e1eba2008-11-19 00:26:18 +0000696 if not results:
697 results = 'results.' + time.strftime('%Y-%m-%d-%H.%M.%S')
Dan Shi14de7622016-08-22 11:09:06 -0700698 results = os.path.abspath(results)
showard566d3c02010-01-12 18:57:01 +0000699 resultdir_exists = False
700 for filename in ('control.srv', 'status.log', '.autoserv_execute'):
701 if os.path.exists(os.path.join(results, filename)):
702 resultdir_exists = True
mbligh4608b002010-01-05 18:22:35 +0000703 if not parser.options.use_existing_results and resultdir_exists:
mbligh80e1eba2008-11-19 00:26:18 +0000704 error = "Error: results directory already exists: %s\n" % results
705 sys.stderr.write(error)
706 sys.exit(1)
mbligha788dc42009-03-26 21:10:16 +0000707
708 # Now that we certified that there's no leftover results dir from
709 # previous jobs, lets create the result dir since the logging system
710 # needs to create the log file in there.
711 if not os.path.isdir(results):
712 os.makedirs(results)
showard75cdfee2009-06-10 17:40:41 +0000713
Prathmesh Prabhu83b598c2018-08-13 17:03:07 -0700714 if parser.options.require_ssp:
715 # This is currently only used for skylab (i.e., when --control-name is
716 # used).
717 use_ssp = _require_ssp_from_control(parser.options.control_name)
718 else:
719 use_ssp = False
720
721
Dan Shic68fefb2015-04-07 10:10:52 -0700722 if use_ssp:
Dan Shie28de552015-05-06 16:51:58 -0700723 log_dir = os.path.join(results, 'ssp_logs') if results else None
Dan Shicf4d2032015-03-12 15:04:21 -0700724 if log_dir and not os.path.exists(log_dir):
725 os.makedirs(log_dir)
726 else:
727 log_dir = results
Dan Shi3f1b8a52015-04-21 11:11:06 -0700728
showard75cdfee2009-06-10 17:40:41 +0000729 logging_manager.configure_logging(
Dan Shicf4d2032015-03-12 15:04:21 -0700730 server_logging_config.ServerLoggingConfig(),
731 results_dir=log_dir,
showard10d84172009-06-18 23:16:50 +0000732 use_console=not parser.options.no_tee,
733 verbose=parser.options.verbose,
734 no_console_prefix=parser.options.no_console_prefix)
Dan Shicf4d2032015-03-12 15:04:21 -0700735
Prathmesh Prabhu80f9cb82018-08-11 18:32:08 -0700736 logging.debug('autoserv is running in drone %s.', socket.gethostname())
Allen Lic2aa8c32019-09-25 17:32:21 -0700737 logging.debug('autoserv environment: %r', os.environ)
Prathmesh Prabhu80f9cb82018-08-11 18:32:08 -0700738 logging.debug('autoserv command was: %s', ' '.join(sys.argv))
739 logging.debug('autoserv parsed options: %s', parser.options)
740
741 if use_ssp:
742 ssp_url = _stage_ssp(parser, results)
743 else:
744 ssp_url = None
Dan Shi0b754c52015-04-20 14:20:38 -0700745
showard75cdfee2009-06-10 17:40:41 +0000746 if results:
mbligha788dc42009-03-26 21:10:16 +0000747 logging.info("Results placed in %s" % results)
mbligh10717632008-11-19 00:21:57 +0000748
mbligh4608b002010-01-05 18:22:35 +0000749 # wait until now to perform this check, so it get properly logged
Dan Shicf4d2032015-03-12 15:04:21 -0700750 if (parser.options.use_existing_results and not resultdir_exists and
Dan Shiff78f112015-06-12 13:34:02 -0700751 not utils.is_in_container()):
mbligh4608b002010-01-05 18:22:35 +0000752 logging.error("No existing results directory found: %s", results)
753 sys.exit(1)
754
Dan Shicf4d2032015-03-12 15:04:21 -0700755 if parser.options.write_pidfile and results:
mbligh4608b002010-01-05 18:22:35 +0000756 pid_file_manager = pidfile.PidFileManager(parser.options.pidfile_label,
757 results)
jadmanskid5ab8c52008-12-03 16:27:07 +0000758 pid_file_manager.open_file()
mblighff7d61f2008-12-22 14:53:35 +0000759 else:
760 pid_file_manager = None
mbligha46678d2008-05-01 20:00:01 +0000761
Allen Lid5abdab2017-02-07 16:03:43 -0800762 autotest.Autotest.set_install_in_tmpdir(
jadmanskif22fea82008-11-26 20:57:07 +0000763 parser.options.install_in_tmpdir)
764
jadmanski0afbb632008-06-06 21:10:57 +0000765 exit_code = 0
Allen Lif146e872017-08-15 18:24:31 -0700766 # TODO(beeps): Extend this to cover different failure modes.
767 # Testing exceptions are matched against labels sent to autoserv. Eg,
768 # to allow only the hostless job to run, specify
769 # testing_exceptions: test_suite in the shadow_config. To allow both
770 # the hostless job and dummy_Pass to run, specify
771 # testing_exceptions: test_suite,dummy_Pass. You can figure out
772 # what label autoserv is invoked with by looking through the logs of a test
773 # for the autoserv command's -l option.
774 testing_exceptions = _CONFIG.get_config_value(
775 'AUTOSERV', 'testing_exceptions', type=list, default=[])
776 test_mode = _CONFIG.get_config_value(
777 'AUTOSERV', 'testing_mode', type=bool, default=False)
778 test_mode = (results_mocker and test_mode and not
779 any([ex in parser.options.label
780 for ex in testing_exceptions]))
781 is_task = (parser.options.verify or parser.options.repair or
782 parser.options.provision or parser.options.reset or
783 parser.options.cleanup or parser.options.collect_crashinfo)
Paul Hobbse9fd5572017-08-22 02:48:25 -0700784
785 trace_labels = {
786 'job_id': job_directories.get_job_id_or_task_id(
787 parser.options.results)
788 }
789 trace = cloud_trace.SpanStack(
790 labels=trace_labels,
791 global_context=parser.options.cloud_trace_context)
792 trace.enabled = parser.options.cloud_trace_context_enabled == 'True'
jadmanski0afbb632008-06-06 21:10:57 +0000793 try:
794 try:
Allen Lif146e872017-08-15 18:24:31 -0700795 if test_mode:
796 # The parser doesn't run on tasks anyway, so we can just return
797 # happy signals without faking results.
798 if not is_task:
799 machine = parser.options.results.split('/')[-1]
800
801 # TODO(beeps): The proper way to do this would be to
802 # refactor job creation so we can invoke job.record
803 # directly. To do that one needs to pipe the test_name
804 # through run_autoserv and bail just before invoking
805 # the server job. See the comment in
806 # puppylab/results_mocker for more context.
807 results_mocker.ResultsMocker(
808 'unknown-test', parser.options.results, machine
809 ).mock_results()
810 return
811 else:
Paul Hobbse9fd5572017-08-22 02:48:25 -0700812 with trace.Span(get_job_status(parser.options)):
813 run_autoserv(pid_file_manager, results, parser, ssp_url,
814 use_ssp)
Aviv Keshet5c40ec62013-08-20 12:11:12 -0700815 except SystemExit as e:
jadmanski0afbb632008-06-06 21:10:57 +0000816 exit_code = e.code
Aviv Keshet5c40ec62013-08-20 12:11:12 -0700817 if exit_code:
Aviv Keshet5ae0a002017-05-05 10:23:33 -0700818 logging.exception('Uncaught SystemExit with code %s', exit_code)
819 except Exception:
jadmanski0afbb632008-06-06 21:10:57 +0000820 # If we don't know what happened, we'll classify it as
821 # an 'abort' and return 1.
Aviv Keshet5ae0a002017-05-05 10:23:33 -0700822 logging.exception('Uncaught Exception, exit_code = 1.')
jadmanski0afbb632008-06-06 21:10:57 +0000823 exit_code = 1
824 finally:
mblighff7d61f2008-12-22 14:53:35 +0000825 if pid_file_manager:
826 pid_file_manager.close_file(exit_code)
jadmanski0afbb632008-06-06 21:10:57 +0000827 sys.exit(exit_code)
mblighfaf0cd42007-11-19 16:00:24 +0000828
mblighbb421852008-03-11 22:36:16 +0000829
mbligha46678d2008-05-01 20:00:01 +0000830if __name__ == '__main__':
jadmanski0afbb632008-06-06 21:10:57 +0000831 main()