xref: /aosp_15_r20/external/autotest/utils/tko_publish.py (revision 9c5db1993ded3edbeafc8092d69fe5de2ee02df7)
1*9c5db199SXin Li#!/usr/bin/python3
2*9c5db199SXin Li"""
3*9c5db199SXin LiThis script will scan an autotest server results directory for job result
4*9c5db199SXin Lidirectories that have completed and that have not yet been published on
5*9c5db199SXin Lia remote dashboard server matching given filtering options and for those it
6*9c5db199SXin Lifinds it will rsync them to the tko server and mark them as published (it uses
7*9c5db199SXin Lia <jobdir>/.tko_published flag file to determine if a jobdir results directory
8*9c5db199SXin Lihas been published yet).
9*9c5db199SXin Li"""
10*9c5db199SXin Li
11*9c5db199SXin Lifrom __future__ import absolute_import
12*9c5db199SXin Lifrom __future__ import division
13*9c5db199SXin Lifrom __future__ import print_function
14*9c5db199SXin Li
15*9c5db199SXin Liimport sys, os, re, optparse
16*9c5db199SXin Li
17*9c5db199SXin Liimport common
18*9c5db199SXin Lifrom autotest_lib.client.common_lib import utils
19*9c5db199SXin Lifrom autotest_lib.server import frontend
20*9c5db199SXin Li
21*9c5db199SXin Lioptions = optparse.Values()
22*9c5db199SXin Li
23*9c5db199SXin LiUSAGE="""tko-publish [options] <resultsdir> <rsync-destination-path>
24*9c5db199SXin Li
25*9c5db199SXin LiWhere:
26*9c5db199SXin Li<resultsdir>              A path to the directory having the job results
27*9c5db199SXin Li                          directories to publish.
28*9c5db199SXin Li
29*9c5db199SXin Li<rsync-destination-path>  A valid rsync destination path where to upload the
30*9c5db199SXin Li                          job result directories.
31*9c5db199SXin Li                          Example: [email protected]:/home/autotest/results"""
32*9c5db199SXin LiPUBLISH_FLAGFILE = '.tko_published'
33*9c5db199SXin LiRSYNC_COMMAND = 'rsync -aqz "%s" "%s"'
34*9c5db199SXin Li
35*9c5db199SXin Li
36*9c5db199SXin Lidef get_job_dirs(path):
37*9c5db199SXin Li    regex = re.compile('[1-9][0-9]*-')
38*9c5db199SXin Li    jobdirs = []
39*9c5db199SXin Li
40*9c5db199SXin Li    for dir in os.listdir(path):
41*9c5db199SXin Li        # skip directories not matching the job result dir pattern
42*9c5db199SXin Li        if not regex.match(dir):
43*9c5db199SXin Li            continue
44*9c5db199SXin Li
45*9c5db199SXin Li        dir = os.path.join(options.resultsdir, dir)
46*9c5db199SXin Li        if (os.path.isdir(dir)
47*9c5db199SXin Li                and not os.path.exists(os.path.join(dir, PUBLISH_FLAGFILE))):
48*9c5db199SXin Li            jobdirs.append(dir)
49*9c5db199SXin Li
50*9c5db199SXin Li    return jobdirs
51*9c5db199SXin Li
52*9c5db199SXin Li
53*9c5db199SXin Lidef publish_job(jobdir):
54*9c5db199SXin Li    cmd = RSYNC_COMMAND % (jobdir, options.dest)
55*9c5db199SXin Li    utils.system(cmd)
56*9c5db199SXin Li
57*9c5db199SXin Li    # mark the jobdir as published
58*9c5db199SXin Li    fd = open(os.path.join(jobdir, PUBLISH_FLAGFILE), 'w')
59*9c5db199SXin Li    fd.close()
60*9c5db199SXin Li    print('Published', jobdir)
61*9c5db199SXin Li
62*9c5db199SXin Li
63*9c5db199SXin Lidef main():
64*9c5db199SXin Li    jobdirs = get_job_dirs(options.resultsdir)
65*9c5db199SXin Li
66*9c5db199SXin Li    afe = frontend.AFE()
67*9c5db199SXin Li    # the way AFE API is right now is to give a whole list of jobs and can't
68*9c5db199SXin Li    # get specific jobs so minimize the queries caching the result
69*9c5db199SXin Li    finished_jobs = afe.get_jobs(finished=True)
70*9c5db199SXin Li
71*9c5db199SXin Li    if options.jobname_pattern:
72*9c5db199SXin Li        jobname_pattern = re.compile(options.jobname_pattern)
73*9c5db199SXin Li    else:
74*9c5db199SXin Li        jobname_pattern = None
75*9c5db199SXin Li
76*9c5db199SXin Li    # for each unpublished possible jobdir find it in the database and see
77*9c5db199SXin Li    # if it is completed
78*9c5db199SXin Li    for jobdir in jobdirs:
79*9c5db199SXin Li        job_id = int(os.path.basename(jobdir).split('-')[0])
80*9c5db199SXin Li        job = [job for job in finished_jobs if job.id == job_id]
81*9c5db199SXin Li
82*9c5db199SXin Li        if len(job) != 1:
83*9c5db199SXin Li            continue
84*9c5db199SXin Li
85*9c5db199SXin Li        if jobname_pattern:
86*9c5db199SXin Li            # does it match the jobname pattern?
87*9c5db199SXin Li            if not jobname_pattern.match(job[0].name):
88*9c5db199SXin Li                continue
89*9c5db199SXin Li
90*9c5db199SXin Li        # does it match the wanted job owner
91*9c5db199SXin Li        if options.job_owner and options.job_owner != job[0].owner:
92*9c5db199SXin Li            continue
93*9c5db199SXin Li
94*9c5db199SXin Li        publish_job(jobdir)
95*9c5db199SXin Li
96*9c5db199SXin Li
97*9c5db199SXin Liif __name__ == '__main__':
98*9c5db199SXin Li    parser = optparse.OptionParser(usage=USAGE)
99*9c5db199SXin Li    parser.add_option('--jobname-pattern', dest='jobname_pattern',
100*9c5db199SXin Li                      help='Regexp pattern to match against job names, by '
101*9c5db199SXin Li                      "default there won't be any matching done",
102*9c5db199SXin Li                      default=None)
103*9c5db199SXin Li    parser.add_option('--job-owner', dest='job_owner', default=None,
104*9c5db199SXin Li                      help='Job owner username to match against for the '
105*9c5db199SXin Li                      'published jobs, by default no matching is done.')
106*9c5db199SXin Li    options, args = parser.parse_args()
107*9c5db199SXin Li
108*9c5db199SXin Li    if len(args) < 2:
109*9c5db199SXin Li        print(USAGE)
110*9c5db199SXin Li        sys.exit(-1)
111*9c5db199SXin Li
112*9c5db199SXin Li    options.resultsdir = args[0]
113*9c5db199SXin Li    options.dest = args[1]
114*9c5db199SXin Li    main()
115