Home | History | Annotate | Download | only in cmd
      1 # Copyright 2017 The Chromium 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 """Run a job against Autotest.
      6 
      7 See http://goto.google.com/monitor_db_per_job_refactor
      8 
      9 See also lucifer_run_job in
     10 https://chromium.googlesource.com/chromiumos/infra/lucifer
     11 
     12 job_reporter is a thin wrapper around lucifer_run_job and only updates the
     13 Autotest database according to status events.
     14 """
     15 
     16 from __future__ import absolute_import
     17 from __future__ import division
     18 from __future__ import print_function
     19 
     20 import atexit
     21 import argparse
     22 import logging
     23 import os
     24 import sys
     25 
     26 from lucifer import autotest
     27 from lucifer import eventlib
     28 from lucifer import handlers
     29 from lucifer import jobx
     30 from lucifer import leasing
     31 from lucifer import loglib
     32 
     33 logger = logging.getLogger(__name__)
     34 
     35 
     36 def main(args):
     37     """Main function
     38 
     39     @param args: list of command line args
     40     """
     41     args = _parse_args_and_configure_logging(args)
     42     logger.info('Starting with args: %r', args)
     43     with leasing.obtain_lease(_lease_path(args.jobdir, args.job_id)):
     44         autotest.monkeypatch()
     45         ret = _main(args)
     46     logger.info('Exiting normally with: %r', ret)
     47     return ret
     48 
     49 
     50 def _parse_args_and_configure_logging(args):
     51     parser = argparse.ArgumentParser(prog='job_reporter', description=__doc__)
     52     loglib.add_logging_options(parser)
     53 
     54     # General configuration
     55     parser.add_argument('--jobdir', default='/usr/local/autotest/leases',
     56                         help='Path to job leases directory.')
     57     parser.add_argument('--run-job-path', default='/usr/bin/lucifer_run_job',
     58                         help='Path to lucifer_run_job binary')
     59     parser.add_argument('--watcher-path', default='/usr/bin/lucifer_watcher',
     60                         help='Path to lucifer_watcher binary')
     61 
     62     # Job specific
     63     parser.add_argument('--job-id', type=int, required=True,
     64                         help='Autotest Job ID')
     65     parser.add_argument('--lucifer-level', required=True,
     66                         help='Lucifer level')
     67     parser.add_argument('--autoserv-exit', type=int, default=None, help='''
     68 autoserv exit status.  If this is passed, then autoserv will not be run
     69 as the caller has presumably already run it.
     70 ''')
     71     parser.add_argument('--need-gather', action='store_true',
     72                         help='Whether to gather logs'
     73                         ' (only with --lucifer-level GATHERING)')
     74     parser.add_argument('--num-tests-failed', type=int, default=-1,
     75                         help='Number of tests failed'
     76                         ' (only with --need-gather)')
     77     parser.add_argument('--results-dir', required=True,
     78                         help='Path to job leases directory.')
     79     args = parser.parse_args(args)
     80     loglib.configure_logging_with_args(parser, args)
     81     return args
     82 
     83 
     84 def _main(args):
     85     """Main program body, running under a lease file.
     86 
     87     @param args: Namespace object containing parsed arguments
     88     """
     89     ts_mon_config = autotest.chromite_load('ts_mon_config')
     90     metrics = autotest.chromite_load('metrics')
     91     with ts_mon_config.SetupTsMonGlobalState(
     92             'job_reporter', short_lived=True):
     93         atexit.register(metrics.Flush)
     94         return _run_autotest_job(args)
     95 
     96 
     97 def _run_autotest_job(args):
     98     """Run a job as seen from Autotest.
     99 
    100     This include some Autotest setup and cleanup around lucifer starting
    101     proper.
    102     """
    103     handler = _make_handler(args)
    104     ret = _run_lucifer_job(handler, args)
    105     if handler.completed:
    106         _mark_handoff_completed(args.job_id)
    107     return ret
    108 
    109 
    110 def _make_handler(args):
    111     """Make event handler for lucifer_run_job."""
    112     models = autotest.load('frontend.afe.models')
    113     if args.autoserv_exit is None:
    114         # TODO(crbug.com/748234): autoserv not implemented yet.
    115         raise NotImplementedError('not implemented yet (crbug.com/748234)')
    116     job = models.Job.objects.get(id=args.job_id)
    117     return handlers.EventHandler(
    118             metrics=handlers.Metrics(),
    119             job=job,
    120             autoserv_exit=args.autoserv_exit,
    121     )
    122 
    123 
    124 def _run_lucifer_job(event_handler, args):
    125     """Run lucifer_run_job.
    126 
    127     Issued events will be handled by event_handler.
    128 
    129     @param event_handler: callable that takes an Event
    130     @param args: parsed arguments
    131     @returns: exit status of lucifer_run_job
    132     """
    133     models = autotest.load('frontend.afe.models')
    134     command_args = [args.run_job_path]
    135     job = models.Job.objects.get(id=args.job_id)
    136     command_args.extend([
    137             '-autotestdir', autotest.AUTOTEST_DIR,
    138             '-watcherpath', args.watcher_path,
    139 
    140             '-abortsock', _abort_sock_path(args.jobdir, args.job_id),
    141             '-hosts', ','.join(jobx.hostnames(job)),
    142 
    143             '-x-level', args.lucifer_level,
    144             '-x-resultsdir', args.results_dir,
    145             '-x-autoserv-exit', str(args.autoserv_exit),
    146     ])
    147     if args.need_gather:
    148         command_args.extend([
    149                 '-x-need-gather',
    150                 '-x-num-tests-failed', str(args.num_tests_failed),
    151         ])
    152     return eventlib.run_event_command(
    153             event_handler=event_handler, args=command_args)
    154 
    155 
    156 def _mark_handoff_completed(job_id):
    157     models = autotest.load('frontend.afe.models')
    158     handoff = models.JobHandoff.objects.get(job_id=job_id)
    159     handoff.completed = True
    160     handoff.save()
    161 
    162 
    163 def _abort_sock_path(jobdir, job_id):
    164     return _lease_path(jobdir, job_id) + '.sock'
    165 
    166 
    167 def _lease_path(jobdir, job_id):
    168     return os.path.join(jobdir, str(job_id))
    169 
    170 
    171 if __name__ == '__main__':
    172     sys.exit(main(sys.argv[1:]))
    173