aboutsummaryrefslogtreecommitdiffstats
path: root/testresults.py
blob: b23f0ef4d404ed2b622dcd6a0df3e5856930d5ed (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
# SPDX-License-Identifier: GPL-2.0-only
'''
Created on June 20, 2019

__author__ = "Tracy Graydon"
__copyright__ = "Copyright 2019, Intel Corp."
__credits__ = ["Tracy Graydon"]
__license__ = "GPL"
__version__ = "2.0"
__maintainer__ = "Tracy Graydon"
__email__ = "tracy.graydon@intel.com"
'''

''' This script is run as part of the release staging process. QA testresults are published
along with the respective release, as part of the release artefacts. This takes the build testresults,
merges them with any QA team testresults (currently just Intel/WindRiver QA) and generates a
master testreport with the combined results.
'''

import os
import os.path
import optparse
import sys
import subprocess
import glob
import shutil
import json
import collections
import git
from pprint import pprint
from shutil import rmtree, copytree, copyfile
from utils import where_am_i, split_thing
from rel_type import release_type

def get_repo(repo_url, repo_branch, single_branch=False):
    CWD = os.getcwd()
    repo_name = split_thing(repo_url, "/")[-1]
    repo_path = os.path.join(CWD, repo_name)
    if os.path.exists(repo_path):
        print("\nFound an existing %s repo. Nuking it." %repo_name)
        rmtree(repo_path)
    print("Cloning the %s repo." %repo_name)
    try:
        the_repo = git.Repo.clone_from(repo_url, repo_path, branch=repo_branch, single_branch=single_branch )
    except:
        print("Couldn't check out the %s repo with branch %s. Check the branch name you passed in." %(repo_name, repo_branch))
        sys.exit()
    # Are we where we think we are?
    branch_name = the_repo.head.ref
    print("We are now on branch: %s\n" %branch_name)
    return the_repo

def get_poky_hash(rel_dir,rel_type, branch):
    os.chdir(RC_SOURCE)
    files = glob.glob('*.bz2')
    allfiles = list(filter(lambda f: os.path.isfile(f), files))
    dirlist = list(filter(lambda x: "poky" in x, allfiles))
    if rel_type == "milestone":
        thing = dirlist[0]
    else:
        filelist = list(filter(lambda x: branch not in x, dirlist))
        thing = filelist[0]
    chunks = split_thing(thing, ".")
    new_chunk = split_thing(chunks[0], '-')
    hash = new_chunk.pop()
    return hash

def get_revisions(logfile):
    with open(logfile) as search:
        for line in search:
            line = line.rstrip()  # remove '\n' at end of line
            if "revisions" in line:
                rev_chunks = split_thing(line, " ")
                revs = rev_chunks[2]
    search.close()
    return revs

def find_bogus(results_dir, branch, commit):
   status = "GOOD"
   print("\nChecking the testresults for bogus branches and commits.\n")
   for dirname, subdir_list, file_list in os.walk(results_dir):
       for fname in file_list:
           bogus = []
           if TEST_FILE in fname:
               filename = os.path.join(dirname, fname)
               with open(filename) as f:
                   report = json.loads(f.read())
               key = list(report.keys())[0]

               meta_branch = report[key]['configuration']['LAYERS']['meta']['branch']
               meta_commit = report[key]['configuration']['LAYERS']['meta']['commit']
               meta_poky_branch = report[key]['configuration']['LAYERS']['meta-poky']['branch']
               meta_poky_commit = report[key]['configuration']['LAYERS']['meta-poky']['commit']
               meta_yocto_bsp_branch = report[key]['configuration']['LAYERS']['meta-yocto-bsp']['branch']
               meta_yocto_bsp_commit = report[key]['configuration']['LAYERS']['meta-yocto-bsp']['commit']

               if not (meta_branch == branch and meta_poky_branch == branch and meta_yocto_bsp_branch == branch):
                   bogus.append("branch")
               if not (meta_commit == commit and meta_poky_commit == commit and meta_yocto_bsp_commit == commit):
                   bogus.append("commit")
               if len(bogus) == 1:
                   print("Bogus %s in %s" %(bogus[0], filename))
               elif len(bogus) == 2:
                   print("Both a bogus branch and commit in %s" %filename)
               if len(bogus) > 0:
                   print("meta \t\t branch: %s \t commit: %s" %(meta_branch, meta_commit))
                   print("meta-poky \t branch: %s \t commit: %s" %(meta_poky_branch, meta_poky_commit))
                   print("meta-yocto_bsp \t branch: %s \t commit: %s" %(meta_yocto_bsp_branch, meta_yocto_bsp_commit))
                   status = "BAD"
                   print
   return status

def do_testreport(report_file, header_path):
    # Generate the testreport.
    outfile = open(report_file, 'a')
    print("Generating the %s file." %report_file)
    if os.path.isfile(header_path):
        infile = open(header_path, 'r')
    else:
        header_path = os.path.join(CONTRIB_PATH, "header.txt")
        if os.path.isfile(header_path):
            infile = open(header_path, 'r')
        else:
            print("Can't find a header file. Quitting.")
            sys.exit()
    all_the_things = infile.read()
    infile.close()
    outfile.writelines("%s\n" %all_the_things)
    outfile.flush()
    subprocess.call([RESULT_TOOL, "report", RC_SOURCE], stdout=outfile)
    outfile.close()
    print("Done.\n")
    return
 

if __name__ == '__main__':
 
    os.system("clear")
    print

    PATH_VARS = where_am_i()
    VHOSTS = PATH_VARS['VHOSTS']
    AB_HOME = PATH_VARS['AB_HOME']
    AB_BASE = PATH_VARS['AB_BASE']
    DL_HOME = PATH_VARS['DL_HOME']
    DL_BASE = PATH_VARS['DL_BASE']

    parser = optparse.OptionParser()
    parser.add_option("-i", "--build-id",
                      type="string", dest="build",
                      help="Required. Release candidate name including rc#. i.e. yocto-2.0.rc1, yocto-2.1_M1.rc3, etc.")
    parser.add_option("-b", "--branch",
                      type="string", dest="branch",
                      help="Required for Major and Point releases. i.e. thud, warrior, zeus, etc. Milestones use the codename for the major release. i.e. 2.8_M1 would use zeus.")
    (options, args) = parser.parse_args()

    if not (options.build and options.branch):
        print("You must specify the RC and the codename/branch.")
        print("Note that milestones need the release codename. i.e. 2.8_M1 would use zeus.")
        print("Please use -h or --help for options.")
        sys.exit()

    if options.build:
       VARS = release_type(options.build)
       RC = VARS['RC']
       RELEASE = VARS['RELEASE']
       REL_ID = VARS['REL_ID']
       RC_DIR = VARS['RC_DIR']
       REL_TYPE = VARS['REL_TYPE']
       MILESTONE = VARS['MILESTONE']
       RC_SOURCE = os.path.join(AB_BASE, RC_DIR)
       #RELEASE_DIR = os.path.join(AB_BASE, RELEASE)
    else:
       print("Build ID is a required argument.")
       print("Please use -h or --help for options.")
       sys.exit()

    if REL_TYPE == "milestone":
        if options.branch == "master":
            print("I need the release line for the milestone.")
            print("i.e. For 2.8_M1 it would be zeus. For 2.7_M1, it woutld be warrior. Etc.")
            sys.exit()
        else:
            POKY_BRANCH = "master"
            CODENAME = options.branch
    else:
        POKY_BRANCH = options.branch
        CODENAME = POKY_BRANCH

    HOME = os.getcwd()
    POKY_REPO = "git://git.yoctoproject.org/poky"
    CONTRIB_REPO = "git://git.yoctoproject.org/yocto-testresults-contrib"
    RESULTS_REPO = "git://git.yoctoproject.org/yocto-testresults"
    POKY_PATH = os.path.join(HOME, "poky")
    CONTRIB_PATH = os.path.join(HOME, "yocto-testresults-contrib")
    SACRED_PATH = os.path.join(HOME, "yocto-testresults")
    RESULT_TOOL = os.path.join(POKY_PATH, "scripts/resulttool")
    RESULTS_DIR = os.path.join(CONTRIB_PATH, "testresults-intel")
    HEADER_INTEL = "header-intel.txt"
    HEADER_PATH = os.path.join(CONTRIB_PATH, HEADER_INTEL)
    REPORT_FILE = os.path.join(RC_SOURCE, "testreport.txt")
    BUILD_RESULTS = os.path.join(RC_SOURCE, "testresults")
    INTEL_RESULTS = os.path.join(RC_SOURCE, "testresults-intel")
    TEST_FILE = "testresults.json"
    STORE_FILE = "store" + "-" + REL_ID + ".log"
    STORE_LOG = os.path.join(HOME, STORE_FILE)

    # Check to make sure that the release dir exists. If not, quit. Have to stage first.
    if not os.path.exists(RC_SOURCE):
        print("Can't find a %s source directory. Please verify if source directory exists? Quitting." %RC_SOURCE)
        sys.exit()
    # Check for testresults dir generated by the build. If not there, something is wrong with the build artefacts.
    if not os.path.exists(BUILD_RESULTS):
        print("I can't find the build testresults directory. Can't continue. Check the build artefacts.")
        sys.exit()
    if not os.listdir(BUILD_RESULTS):
        print("The build testresults directory appears to be empty. Can't continue. Check the build artefacts.")
        sys.exit()
    if os.path.exists(INTEL_RESULTS):
        print("I found an existing testresults-intel directory. Refusing to clobber.")
        sys.exit()
    if os.path.isfile(REPORT_FILE):
       print("I found an existing testreport.txt file. Refusing to clobber.")
       sys.exit()
  
    # Get the repos
    # We always use master for poky because we want the resulttool from master.
    poky_repo = get_repo(POKY_REPO, "master")     # would we want this to be master-next? What id definitive version of resulttool?
    contrib_repo = get_repo(CONTRIB_REPO, CODENAME, single_branch=True)
    results_repo = get_repo(RESULTS_REPO, POKY_BRANCH, single_branch=True)


    # Get the poky build hash VINEELA: Check from here
    POKY_HASH = get_poky_hash(RC_SOURCE, REL_TYPE, POKY_BRANCH)
    print("POKY_HASH: %s" %POKY_HASH)
    print("POKY_BRANCH: %s" %POKY_BRANCH)
    
    # Now check for bad hashes, bogus branch names. The ONLY branch and commit has we should see
    # are the ones we just got above: POKY_HASH and _POKY_BRANCH.
    bogus = find_bogus(RESULTS_DIR, POKY_BRANCH, POKY_HASH)
    if bogus == "BAD":
        print("Can't continue with bogus branch names or commits in testresults. Quitting.\n")
        sys.exit()
    else:
        print("No issues found.\n")

    # Get the testresults from the contrib repo and put them in the RELEASE_DIR.
    copytree(RESULTS_DIR, INTEL_RESULTS)

    # Generate the testreport
    do_testreport(REPORT_FILE, HEADER_PATH)
    
    # If we made it this far without dying, there should be only ONE revision in the resulttool output.
    print("Running the resultool store command.")
    if os.path.exists(STORE_LOG):
        os.remove(STORE_LOG)
    store_log = open(STORE_LOG, "a")
    subprocess.call([RESULT_TOOL, "store", RESULTS_DIR, SACRED_PATH, "-x", "Intel QA"], stderr=store_log)
    store_log.close()

   # See how many revisions we have as a final sanity check.
    revisions = get_revisions(STORE_LOG)
    print("Revisions found: %s" %revisions)
    if revisions != "1":
        print("We should have ONLY ONE revision. Something is super wrong. Quitting.\n")
        sys.exit()
    else:
        print("\nCheck the resulttool store log and make sure things look right.")
        print
        store_log = open(STORE_LOG, "r")
        tool_output = store_log.read()
        print(tool_output)