blob: f423673319e0fbea002bf5d12042917088fbaa75 [file] [log] [blame]
Fathi Boudra9984a982019-12-17 17:37:58 +02001import argparse
2import os
3import re
4import requests
5import sys
6import StringIO
7from copy import deepcopy
8from string import Template
9from jinja2 import Environment, FileSystemLoader, StrictUndefined
10from ruamel.yaml import YAML
11
12
13try:
14 from urllib.parse import urlsplit
15except ImportError:
16 from urlparse import urlsplit
17
18
19# Templates base path
20template_base_path = 'configs/openembedded-lkft/lava-job-definitions'
21testplan_base_path = 'configs/openembedded-lkft/lava-job-definitions/'
22testplan_device_path = 'devices/'
23# Snapshots base URL
24snapshots_url = 'https://snapshots.linaro.org/openembedded/lkft'
25
26def parse_template(yaml_string):
27 '''
28 Round trip lava_job through ruamel to test parsing and
29 improve formatting. Comments are preserved.
30
31 In: yaml-formatted string
32 Out: validated yaml-formatted string
33 '''
34 yaml = YAML()
35 # ruamel does not provide a mechanism to dump to string, so use StringIO
36 # to catch it
37 output = StringIO.StringIO()
38 yaml.dump(yaml.load(yaml_string), output)
39 # strip empty lines from output
40 return re.sub(r'^\s*$\n', '', output.getvalue(), flags=re.MULTILINE)
41
42def get_job_name(lava_job_string):
43 '''
44 In: yaml-formatted string
45 Out: LAVA job's name
46 '''
47 yaml = YAML()
48 lava_job = yaml.load(lava_job_string)
49 return lava_job['job_name']
50
51def _load_template(template_name, template_path, device_type):
52 template = ''
53 template_file_name = ''
54
55 if template_name:
56 template_file_name = "%s/%s/%s" % (template_path,
57 device_type,
58 template_name)
59 if os.path.exists(template_file_name):
60 with open(template_file_name, 'r') as f:
61 template = f.read()
62 else:
63 print('template (%s) was specified but not exists' %
64 template_file_name)
65 sys.exit(1)
66
67 return template, template_file_name
68
69
70def _submit_to_squad(lava_job, lava_url_base, qa_server_api, qa_server_base, qa_token, quiet):
71 headers = {
72 "Auth-Token": qa_token
73 }
74
75 try:
76 data = {
77 "definition": lava_job,
78 "backend": urlsplit(lava_url_base).netloc # qa-reports backends are named as lava instances
79 }
80 print("Submit to: %s" % qa_server_api)
81 results = requests.post(qa_server_api, data=data, headers=headers,
82 timeout=31)
83 if results.status_code < 300:
84 print("%s/testjob/%s %s" % (qa_server_base, results.text, get_job_name(lava_job)))
85 else:
86 print(results.status_code)
87 print(results.text)
88 except requests.exceptions.RequestException as err:
89 print("QA Reports submission failed")
90 if not quiet:
91 print("offending job definition:")
92 print(lava_job)
93
94
95def main():
96 parser = argparse.ArgumentParser()
97 parser.add_argument("--device-type",
98 help="Device type in LAVA",
99 dest="device_type",
100 required=True)
101 parser.add_argument("--environment",
102 help="User specified the environment name, prefix or suffix won't be used",
103 dest="environment",
104 default="")
105 parser.add_argument("--env-prefix",
106 help="Prefix for the environment name",
107 dest="env_prefix",
108 default="")
109 parser.add_argument("--env-suffix",
110 help="Suffix for the environment name",
111 dest="env_suffix",
112 default="")
113 parser.add_argument("--build-number",
114 help="Build number",
115 dest="build_number",
116 required=True)
117 parser.add_argument("--qa-server-team",
118 help="Team in QA Reports service",
119 dest="qa_server_team",
120 required=True)
121 parser.add_argument("--qa-server-project",
122 help="Project in QA Reports service",
123 dest="qa_server_project",
124 required=True)
125 parser.add_argument("--qa-server",
126 help="QA Reports server",
127 dest="qa_server",
128 default="https://qa-reports.linaro.org")
129 parser.add_argument("--qa-token",
130 help="QA Reports token",
131 dest="qa_token",
132 default=os.environ.get('QA_REPORTS_TOKEN'))
133 parser.add_argument("--lava-server",
134 help="LAVA server URL",
135 dest="lava_server",
136 required=True)
137 parser.add_argument("--git-commit",
138 help="git commit ID",
139 dest="git_commit",
140 required=True)
141 parser.add_argument("--template-path",
142 help="Path to LAVA job templates",
143 dest="template_path",
144 default=template_base_path)
145 parser.add_argument("--testplan-path",
146 help="Path to Jinja2 LAVA job templates",
147 dest="testplan_path",
148 default=testplan_base_path)
149 parser.add_argument("--testplan-device-path",
150 help="Relative path to Jinja2 device deployment fragments",
151 dest="testplan_device_path",
152 default=testplan_device_path)
153 parser.add_argument("--template-base-pre",
154 help="base template used to construct templates, previous",
155 dest="template_base_pre")
156 parser.add_argument("--template-base-post",
157 help="base template used to construct templates, posterior",
158 dest="template_base_post")
159 parser.add_argument("--template-names",
160 help="list of the templates to submit for testing",
161 dest="template_names",
162 nargs="+",
163 default=[])
164 parser.add_argument("--test-plan",
165 help="""list of the Jinja2 templates to submit for testing.
166 It is assumed that the templates produce valid LAVA job
167 definitions. All varaibles are substituted using Jinja2
168 engine. This includes environment variables.""",
169 dest="test_plan",
170 nargs="+",
171 default=[])
172 parser.add_argument("--dry-run",
173 help="""Prepare and write templates to tmp/.
174 Don't submit to actual servers.""",
175 action='store_true',
176 dest="dryrun")
177 parser.add_argument("--quiet",
178 help="Only output the final qa-reports URL",
179 action='store_true',
180 dest="quiet")
181
182 args, _ = parser.parse_known_args()
183
184 output_path = "tmp"
185 if args.dryrun:
186 if not os.path.exists(output_path):
187 os.mkdir(output_path)
188 if args.qa_token is None and not args.dryrun:
189 print("QA_REPORTS_TOKEN is missing")
190 sys.exit(1)
191
192 qa_server_base = args.qa_server
193 if not (qa_server_base.startswith("http://") or qa_server_base.startswith("https://")):
194 qa_server_base = "https://" + qa_server_base
195 qa_server_team = args.qa_server_team
196 qa_server_project = args.qa_server_project
197 qa_server_build = args.git_commit
198
199 if not args.environment:
200 # when user not specify value for the environment option,
201 # use the device_type as before
202 qa_server_env = args.env_prefix + args.device_type + args.env_suffix
203 else:
204 # when user specified value for the environment option,
205 # use the user specified value
206 qa_server_env = args.environment
207
208 qa_server_api = "%s/api/submitjob/%s/%s/%s/%s" % (
209 qa_server_base,
210 qa_server_team,
211 qa_server_project,
212 qa_server_build,
213 qa_server_env)
214 lava_server = args.lava_server
215 if not (lava_server.startswith("http://") or lava_server.startswith("https://")):
216 lava_server = "https://" + lava_server
217 lava_url_base = "%s://%s/" % (urlsplit(lava_server).scheme, urlsplit(lava_server).netloc)
218
219 template_base_pre, _ = _load_template(args.template_base_pre,
220 args.template_path,
221 args.device_type)
222 template_base_post, _ = _load_template(args.template_base_post,
223 args.template_path,
224 args.device_type)
225 lava_jobs = []
226 for test in args.template_names:
227 test_template, template_file_name = _load_template(test,
228 args.template_path,
229 args.device_type)
230 if template_base_pre:
231 test_template = "%s\n%s" % (template_base_pre, test_template)
232 if template_base_post:
233 test_template = "%s\n%s" % (test_template, template_base_post)
234
235 template = Template(test_template)
236 print("using template: %s" % template_file_name)
237 lava_job = template.substitute(os.environ)
238 lava_job = parse_template(lava_job)
239 lava_jobs.append(lava_job)
240
241 if not args.quiet:
242 print(lava_job)
243 if args.dryrun:
244 testpath = os.path.join(output_path, args.device_type, test)
245 if not os.path.exists(os.path.dirname(testpath)):
246 os.makedirs(os.path.dirname(testpath))
247 with open(os.path.join(testpath), 'w') as f:
248 f.write(lava_job)
249
250 THIS_DIR = os.path.abspath(args.testplan_path)
251 # prevent creating templates when variables are missing
252 j2_env = Environment(loader=FileSystemLoader(THIS_DIR, followlinks=True), undefined=StrictUndefined)
253 context = deepcopy(os.environ)
254 context.update({"device_type": os.path.join(args.testplan_device_path, args.device_type)})
255 for test in args.test_plan:
256 ''' Prepare lava jobs '''
257 lava_job = j2_env.get_template(test).render(context)
258 lava_job = parse_template(lava_job)
259 lava_jobs.append(lava_job)
260
261 if not args.quiet:
262 print(lava_job)
263 if args.dryrun:
264 testpath = os.path.join(output_path, args.device_type, test)
265 if not os.path.exists(os.path.dirname(testpath)):
266 os.makedirs(os.path.dirname(testpath))
267 with open(os.path.join(testpath), 'w') as f:
268 f.write(lava_job)
269
270 for lava_job in lava_jobs:
271 ''' Submit lava jobs '''
272 if not args.dryrun:
273 _submit_to_squad(lava_job,
274 lava_url_base,
275 qa_server_api,
276 qa_server_base,
277 args.qa_token,
278 args.quiet)
279
280
281if __name__ == "__main__":
282 main()