blob: 8001ca7178242a20bcf254cbfb43296e3efc14dc [file] [log] [blame]
# Copyright 2017 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""Run a job against Autotest.
See http://goto.google.com/monitor_db_per_job_refactor
See also https://chromium.googlesource.com/chromiumos/infra/lucifer
job_reporter is a thin wrapper around lucifer and only updates the
Autotest database according to status events.
"""
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
import atexit
import argparse
import logging
import os
import sys
from lucifer import autotest
from lucifer import eventlib
from lucifer import handlers
from lucifer import jobx
from lucifer import leasing
from lucifer import loglib
logger = logging.getLogger(__name__)
def main(argv):
"""Main function
@param argv: command line args
"""
print('job_reporter: Running with argv: %r' % argv, file=sys.stderr)
args = _parse_args_and_configure_logging(argv[1:])
logger.info('Running with parsed args: %r', args)
with leasing.obtain_lease(_lease_path(args.jobdir, args.job_id)):
autotest.monkeypatch()
ret = _main(args)
logger.info('Exiting normally with: %r', ret)
return ret
def _parse_args_and_configure_logging(args):
parser = argparse.ArgumentParser(prog='job_reporter', description=__doc__)
loglib.add_logging_options(parser)
# General configuration
parser.add_argument('--jobdir', default='/usr/local/autotest/leases',
help='Path to job leases directory.')
parser.add_argument('--lucifer-path', default='/usr/bin/lucifer',
help='Path to lucifer binary')
# Job specific
# General
parser.add_argument('--lucifer-level', required=True,
help='Lucifer level', choices=['STARTING'])
parser.add_argument('--job-id', type=int, required=True,
help='Autotest Job ID')
parser.add_argument('--results-dir', required=True,
help='Path to job results directory.')
# STARTING flags
parser.add_argument('--execution-tag', default=None,
help='Autotest execution tag.')
parser.add_argument('--parsing-only', action='store_true',
help='Whether to only do parsing'
' (only with --lucifer-level STARTING)')
args = parser.parse_args(args)
loglib.configure_logging_with_args(parser, args)
return args
def _main(args):
"""Main program body, running under a lease file.
@param args: Namespace object containing parsed arguments
"""
ts_mon_config = autotest.chromite_load('ts_mon_config')
metrics = autotest.chromite_load('metrics')
with ts_mon_config.SetupTsMonGlobalState(
'job_reporter', short_lived=True):
atexit.register(metrics.Flush)
return _run_autotest_job(args)
def _run_autotest_job(args):
"""Run a job as seen from Autotest.
This include some Autotest setup and cleanup around lucifer starting
proper.
"""
models = autotest.load('frontend.afe.models')
job = models.Job.objects.get(id=args.job_id)
_prepare_autotest_job_files(args, job)
handler = _make_handler(args, job)
ret = _run_lucifer_job(handler, args, job)
if handler.completed:
_mark_handoff_completed(args.job_id)
return ret
def _prepare_autotest_job_files(args, job):
jobx.prepare_control_file(job, args.results_dir)
jobx.prepare_keyvals_files(job, args.results_dir)
def _make_handler(args, job):
"""Make event handler for lucifer."""
return handlers.EventHandler(
metrics=handlers.Metrics(),
job=job,
autoserv_exit=None,
results_dir=args.results_dir,
)
def _run_lucifer_job(event_handler, args, job):
"""Run lucifer test.
Issued events will be handled by event_handler.
@param event_handler: callable that takes an Event
@param args: parsed arguments
@returns: exit status of lucifer
"""
command_args = [args.lucifer_path]
command_args.extend([
'test',
'-autotestdir', '/usr/local/autotest',
'-abortsock', _abort_sock_path(args.jobdir, args.job_id),
'-hosts', ','.join(jobx.hostnames(job)),
'-x-level', args.lucifer_level,
'-resultsdir', args.results_dir,
])
_add_level_specific_args(command_args, args, job)
return eventlib.run_event_command(
event_handler=event_handler, args=command_args)
def _add_level_specific_args(command_args, args, job):
"""Add level specific arguments for lucifer test.
command_args is modified in place.
"""
if args.lucifer_level == 'STARTING':
_add_starting_args(command_args, args, job)
else:
raise Exception('Invalid lucifer level %s' % args.lucifer_level)
def _add_starting_args(command_args, args, job):
"""Add STARTING level arguments for lucifer test.
command_args is modified in place.
"""
RebootAfter = autotest.load('frontend.afe.model_attributes').RebootAfter
command_args.extend([
'-x-control-file', jobx.control_file_path(args.results_dir),
])
if args.execution_tag is not None:
command_args.extend(['-x-execution-tag', args.execution_tag])
command_args.extend(['-x-job-owner', job.owner])
command_args.extend(['-x-job-name', job.name])
command_args.extend(
['-x-reboot-after',
RebootAfter.get_string(job.reboot_after).lower()])
if args.parsing_only:
command_args.append('-x-parse-only')
if job.run_reset:
command_args.append('-x-run-reset')
if jobx.is_client_job(job):
command_args.append('-x-client-test')
if jobx.needs_ssp(job):
command_args.append('-x-require-ssp')
test_source_build = job.keyval_dict().get('test_source_build', None)
if test_source_build:
command_args.extend(['-x-test-source-build', test_source_build])
if job.parent_job_id:
command_args.extend(['-x-parent-job-id', str(job.parent_job_id)])
def _mark_handoff_completed(job_id):
models = autotest.load('frontend.afe.models')
handoff = models.JobHandoff.objects.get(job_id=job_id)
handoff.completed = True
handoff.save()
def _abort_sock_path(jobdir, job_id):
return _lease_path(jobdir, job_id) + '.sock'
def _lease_path(jobdir, job_id):
return os.path.join(jobdir, str(job_id))
if __name__ == '__main__':
sys.exit(main(sys.argv))