blob: 7616bf1af513f6a21df3cc7106d680352f7c2705 [file] [log] [blame]
Vishal Bhojdc338f62020-05-18 15:38:18 +05301#!/usr/bin/env python3
Chase Qi09edc7f2016-08-18 13:18:50 +08002import argparse
3import csv
Milosz Wasilewski2fea70e2016-11-11 12:16:09 +00004import cmd
Chase Qia158efe2017-11-17 12:35:11 +08005import copy
Chase Qi09edc7f2016-08-18 13:18:50 +08006import json
7import logging
Vishal Bhoje94da4a2020-05-15 12:22:40 +05308import netrc
Chase Qi09edc7f2016-08-18 13:18:50 +08009import os
Chase Qi09edc7f2016-08-18 13:18:50 +080010import re
Milosz Wasilewski682120e2017-03-13 13:37:18 +000011import shlex
Chase Qi09edc7f2016-08-18 13:18:50 +080012import shutil
Milosz Wasilewski970431b2016-11-25 14:10:08 +000013import subprocess
Chase Qi09edc7f2016-08-18 13:18:50 +080014import sys
Milosz Wasilewski259ba192017-07-27 10:59:25 +010015import textwrap
Chase Qi09edc7f2016-08-18 13:18:50 +080016import time
Chase Qi09edc7f2016-08-18 13:18:50 +080017from uuid import uuid4
Milosz Wasilewski31ac9852021-11-18 11:21:28 +000018from datetime import datetime
Chase Qiea543352017-09-21 16:44:30 +080019from distutils.spawn import find_executable
Chase Qi09edc7f2016-08-18 13:18:50 +080020
21
Chase Qifaf7d282016-08-29 19:34:01 +080022try:
Vishal Bhoje94da4a2020-05-15 12:22:40 +053023 from squad_client.core.api import SquadApi
24 from squad_client.shortcuts import submit_results
25 from squad_client.core.models import Squad
26 from urllib.parse import urlparse
27except ImportError as e:
Benjamin Copeland15d743e2021-02-22 08:35:10 +000028 logger = logging.getLogger("RUNNER")
29 logger.warning("squad_client is needed if you want to upload to qa-reports")
Vishal Bhoje94da4a2020-05-15 12:22:40 +053030
31
32try:
Chase Qifaf7d282016-08-29 19:34:01 +080033 import pexpect
34 import yaml
35except ImportError as e:
36 print(e)
Benjamin Copeland15d743e2021-02-22 08:35:10 +000037 print("Please run the below command to install modules required")
38 print("pip3 install -r ${REPO_PATH}/automated/utils/requirements.txt")
Chase Qifaf7d282016-08-29 19:34:01 +080039 sys.exit(1)
40
41
Milosz Wasilewski259ba192017-07-27 10:59:25 +010042class StoreDictKeyPair(argparse.Action):
43 def __init__(self, option_strings, dest, nargs=None, **kwargs):
44 self._nargs = nargs
Benjamin Copeland15d743e2021-02-22 08:35:10 +000045 super(StoreDictKeyPair, self).__init__(
46 option_strings, dest, nargs=nargs, **kwargs
47 )
Milosz Wasilewski259ba192017-07-27 10:59:25 +010048
49 def __call__(self, parser, namespace, values, option_string=None):
50 my_dict = {}
51 for kv in values:
Milosz Wasilewskif9c8c062017-08-02 16:10:40 +010052 if "=" in kv:
53 k, v = kv.split("=", 1)
54 my_dict[k] = v
55 else:
56 print("Invalid parameter: %s" % kv)
Milosz Wasilewski259ba192017-07-27 10:59:25 +010057 setattr(namespace, self.dest, my_dict)
58
59
Milosz Wasilewskidf71a762017-07-20 13:26:21 +010060# quit gracefully if the connection is closed by remote host
61SSH_PARAMS = "-o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o ServerAliveInterval=5"
Milosz Wasilewski682120e2017-03-13 13:37:18 +000062
63
Dan Ruea9eb01c2017-06-07 16:29:09 -050064def run_command(command, target=None):
Anders Roxelld5c6cab2021-04-29 00:52:35 +020065 """Run a shell command. If target is specified, ssh to the given target first."""
Dan Ruea9eb01c2017-06-07 16:29:09 -050066
67 run = command
68 if target:
69 run = 'ssh {} {} "{}"'.format(SSH_PARAMS, target, command)
70
Benjamin Copeland15d743e2021-02-22 08:35:10 +000071 logger = logging.getLogger("RUNNER.run_command")
Dan Ruea9eb01c2017-06-07 16:29:09 -050072 logger.debug(run)
Chase Qi3efa7692017-06-26 15:54:05 +080073 if sys.version_info[0] < 3:
74 return subprocess.check_output(shlex.split(run)).strip()
75 else:
Benjamin Copeland15d743e2021-02-22 08:35:10 +000076 return subprocess.check_output(shlex.split(run)).strip().decode("utf-8")
Milosz Wasilewski682120e2017-03-13 13:37:18 +000077
78
Chase Qi09edc7f2016-08-18 13:18:50 +080079class TestPlan(object):
80 """
81 Analysis args specified, then generate test plan.
82 """
83
84 def __init__(self, args):
Chase Qi09edc7f2016-08-18 13:18:50 +080085 self.test_def = args.test_def
86 self.test_plan = args.test_plan
87 self.timeout = args.timeout
88 self.skip_install = args.skip_install
Benjamin Copeland15d743e2021-02-22 08:35:10 +000089 self.logger = logging.getLogger("RUNNER.TestPlan")
Chase Qia158efe2017-11-17 12:35:11 +080090 self.overlay = args.overlay
91
92 def apply_overlay(self, test_list):
93 fixed_test_list = copy.deepcopy(test_list)
Benjamin Copeland15d743e2021-02-22 08:35:10 +000094 logger = logging.getLogger("RUNNER.TestPlan.Overlay")
Chase Qia158efe2017-11-17 12:35:11 +080095 with open(self.overlay) as f:
Milosz Wasilewskid6ffade2022-02-21 15:14:17 +000096 data = yaml.load(f, Loader=yaml.SafeLoader)
Chase Qia158efe2017-11-17 12:35:11 +080097
Benjamin Copeland15d743e2021-02-22 08:35:10 +000098 if data.get("skip"):
99 skip_tests = data["skip"]
Chase Qia158efe2017-11-17 12:35:11 +0800100 for test in test_list:
101 for skip_test in skip_tests:
Benjamin Copeland15d743e2021-02-22 08:35:10 +0000102 if (
103 test["path"] == skip_test["path"]
104 and test["repository"] == skip_test["repository"]
105 ):
Chase Qia158efe2017-11-17 12:35:11 +0800106 fixed_test_list.remove(test)
107 logger.info("Skipped: {}".format(test))
108 else:
109 continue
110
Benjamin Copeland15d743e2021-02-22 08:35:10 +0000111 if data.get("amend"):
112 amend_tests = data["amend"]
Chase Qia158efe2017-11-17 12:35:11 +0800113 for test in fixed_test_list:
114 for amend_test in amend_tests:
Benjamin Copeland15d743e2021-02-22 08:35:10 +0000115 if (
116 test["path"] == amend_test["path"]
Milosz Wasilewskid6ffade2022-02-21 15:14:17 +0000117 and test["repository"] == amend_test["repository"]
Benjamin Copeland15d743e2021-02-22 08:35:10 +0000118 ):
119 if amend_test.get("parameters"):
120 if test.get("parameters"):
121 test["parameters"].update(amend_test["parameters"])
Chase Qia158efe2017-11-17 12:35:11 +0800122 else:
Benjamin Copeland15d743e2021-02-22 08:35:10 +0000123 test["parameters"] = amend_test["parameters"]
124 logger.info("Updated: {}".format(test))
Chase Qia158efe2017-11-17 12:35:11 +0800125 else:
Benjamin Copeland15d743e2021-02-22 08:35:10 +0000126 logger.warning(
127 "'parameters' not found in {}, nothing to amend.".format(
128 amend_test
129 )
130 )
Chase Qia158efe2017-11-17 12:35:11 +0800131
Benjamin Copeland15d743e2021-02-22 08:35:10 +0000132 if data.get("add"):
133 add_tests = data["add"]
Chase Qia158efe2017-11-17 12:35:11 +0800134 unique_add_tests = []
135 for test in add_tests:
136 if test not in unique_add_tests:
137 unique_add_tests.append(test)
138 else:
139 logger.warning("Skipping duplicate test {}".format(test))
140
141 for test in test_list:
Benjamin Copeland15d743e2021-02-22 08:35:10 +0000142 del test["uuid"]
Chase Qia158efe2017-11-17 12:35:11 +0800143
144 for add_test in unique_add_tests:
145 if add_test in test_list:
Benjamin Copeland15d743e2021-02-22 08:35:10 +0000146 logger.warning(
147 "{} already included in test plan, do nothing.".format(add_test)
148 )
Chase Qia158efe2017-11-17 12:35:11 +0800149 else:
Benjamin Copeland15d743e2021-02-22 08:35:10 +0000150 add_test["uuid"] = str(uuid4())
Chase Qia158efe2017-11-17 12:35:11 +0800151 fixed_test_list.append(add_test)
152 logger.info("Added: {}".format(add_test))
153
154 return fixed_test_list
Chase Qi09edc7f2016-08-18 13:18:50 +0800155
Milosz Wasilewski2fea70e2016-11-11 12:16:09 +0000156 def test_list(self, kind="automated"):
Chase Qi09edc7f2016-08-18 13:18:50 +0800157 if self.test_def:
158 if not os.path.exists(self.test_def):
Benjamin Copeland15d743e2021-02-22 08:35:10 +0000159 self.logger.error(" %s NOT found, exiting..." % self.test_def)
Chase Qi09edc7f2016-08-18 13:18:50 +0800160 sys.exit(1)
161
Benjamin Copeland15d743e2021-02-22 08:35:10 +0000162 test_list = [{"path": self.test_def}]
163 test_list[0]["uuid"] = str(uuid4())
164 test_list[0]["timeout"] = self.timeout
165 test_list[0]["skip_install"] = self.skip_install
Chase Qi09edc7f2016-08-18 13:18:50 +0800166 elif self.test_plan:
167 if not os.path.exists(self.test_plan):
Benjamin Copeland15d743e2021-02-22 08:35:10 +0000168 self.logger.error(" %s NOT found, exiting..." % self.test_plan)
Chase Qi09edc7f2016-08-18 13:18:50 +0800169 sys.exit(1)
170
Benjamin Copeland15d743e2021-02-22 08:35:10 +0000171 with open(self.test_plan, "r") as f:
Chase Qi09edc7f2016-08-18 13:18:50 +0800172 test_plan = yaml.safe_load(f)
173 try:
Benjamin Copeland15d743e2021-02-22 08:35:10 +0000174 plan_version = test_plan["metadata"].get("format")
175 self.logger.info("Test plan version: {}".format(plan_version))
Milosz Wasilewski8d64bb22019-06-18 12:32:08 +0100176 tests = []
Chase Qidca4fb62017-11-22 12:09:42 +0800177 if plan_version == "Linaro Test Plan v2":
Benjamin Copeland15d743e2021-02-22 08:35:10 +0000178 tests = test_plan["tests"][kind]
Chase Qidca4fb62017-11-22 12:09:42 +0800179 elif plan_version == "Linaro Test Plan v1" or plan_version is None:
Benjamin Copeland15d743e2021-02-22 08:35:10 +0000180 for requirement in test_plan["requirements"]:
181 if "tests" in requirement.keys():
182 if (
183 requirement["tests"]
184 and kind in requirement["tests"].keys()
185 and requirement["tests"][kind]
186 ):
187 for test in requirement["tests"][kind]:
Chase Qidca4fb62017-11-22 12:09:42 +0800188 tests.append(test)
189
Milosz Wasilewski2fea70e2016-11-11 12:16:09 +0000190 test_list = []
Dan Rued9d7b652017-05-26 14:00:10 -0500191 unique_tests = [] # List of test hashes
Chase Qidca4fb62017-11-22 12:09:42 +0800192 for test in tests:
193 test_hash = hash(json.dumps(test, sort_keys=True))
194 if test_hash in unique_tests:
195 # Test is already in the test_list; don't add it again.
196 self.logger.warning("Skipping duplicate test {}".format(test))
197 continue
198 unique_tests.append(test_hash)
199 test_list.append(test)
Chase Qi09edc7f2016-08-18 13:18:50 +0800200 for test in test_list:
Benjamin Copeland15d743e2021-02-22 08:35:10 +0000201 test["uuid"] = str(uuid4())
Chase Qi09edc7f2016-08-18 13:18:50 +0800202 except KeyError as e:
203 self.logger.error("%s is missing from test plan" % str(e))
204 sys.exit(1)
205 else:
Benjamin Copeland15d743e2021-02-22 08:35:10 +0000206 self.logger.error("Plese specify a test or test plan.")
Chase Qi09edc7f2016-08-18 13:18:50 +0800207 sys.exit(1)
208
Chase Qia158efe2017-11-17 12:35:11 +0800209 if self.overlay is None:
210 return test_list
211 else:
212 return self.apply_overlay(test_list)
Chase Qi09edc7f2016-08-18 13:18:50 +0800213
214
215class TestSetup(object):
216 """
217 Create directories required, then copy files needed to these directories.
218 """
219
220 def __init__(self, test, args):
Milosz Wasilewski682120e2017-03-13 13:37:18 +0000221 self.test = test
222 self.args = args
Benjamin Copeland15d743e2021-02-22 08:35:10 +0000223 self.logger = logging.getLogger("RUNNER.TestSetup")
Milosz Wasilewski2fea70e2016-11-11 12:16:09 +0000224 self.test_kind = args.kind
Benjamin Copeland15d743e2021-02-22 08:35:10 +0000225 self.test_version = test.get("version", None)
Chase Qi09edc7f2016-08-18 13:18:50 +0800226
227 def validate_env(self):
228 # Inspect if environment set properly.
229 try:
Benjamin Copeland15d743e2021-02-22 08:35:10 +0000230 self.repo_path = os.environ["REPO_PATH"]
Chase Qi09edc7f2016-08-18 13:18:50 +0800231 except KeyError:
Benjamin Copeland15d743e2021-02-22 08:35:10 +0000232 self.logger.error("KeyError: REPO_PATH")
233 self.logger.error(
234 "Please run '. ./bin/setenv.sh' to setup test environment"
235 )
Chase Qi09edc7f2016-08-18 13:18:50 +0800236 sys.exit(1)
237
238 def create_dir(self):
Benjamin Copeland15d743e2021-02-22 08:35:10 +0000239 if not os.path.exists(self.test["output"]):
240 os.makedirs(self.test["output"])
241 self.logger.info("Output directory created: %s" % self.test["output"])
Chase Qi09edc7f2016-08-18 13:18:50 +0800242
243 def copy_test_repo(self):
244 self.validate_env()
Benjamin Copeland15d743e2021-02-22 08:35:10 +0000245 shutil.rmtree(self.test["test_path"], ignore_errors=True)
246 if self.repo_path in self.test["test_path"]:
247 self.logger.error(
248 "Cannot copy repository into itself. Please choose output directory outside repository path"
249 )
Chase Qi33eb7652016-12-02 10:43:46 +0800250 sys.exit(1)
Benjamin Copeland15d743e2021-02-22 08:35:10 +0000251 shutil.copytree(self.repo_path, self.test["test_path"], symlinks=True)
252 self.logger.info("Test repo copied to: %s" % self.test["test_path"])
Chase Qi09edc7f2016-08-18 13:18:50 +0800253
Milosz Wasilewski970431b2016-11-25 14:10:08 +0000254 def checkout_version(self):
255 if self.test_version:
256 path = os.getcwd()
Benjamin Copeland15d743e2021-02-22 08:35:10 +0000257 os.chdir(self.test["test_path"])
Milosz Wasilewski970431b2016-11-25 14:10:08 +0000258 subprocess.call("git checkout %s" % self.test_version, shell=True)
259 os.chdir(path)
260
Chase Qi09edc7f2016-08-18 13:18:50 +0800261 def create_uuid_file(self):
Benjamin Copeland15d743e2021-02-22 08:35:10 +0000262 with open("%s/uuid" % self.test["test_path"], "w") as f:
263 f.write(self.test["uuid"])
Chase Qi09edc7f2016-08-18 13:18:50 +0800264
265
266class TestDefinition(object):
267 """
268 Convert test definition to testdef.yaml, testdef_metadata and run.sh.
269 """
270
271 def __init__(self, test, args):
Milosz Wasilewski2fea70e2016-11-11 12:16:09 +0000272 self.test = test
273 self.args = args
Benjamin Copeland15d743e2021-02-22 08:35:10 +0000274 self.logger = logging.getLogger("RUNNER.TestDef")
Chase Qi09edc7f2016-08-18 13:18:50 +0800275 self.skip_install = args.skip_install
Milosz Wasilewski2fea70e2016-11-11 12:16:09 +0000276 self.is_manual = False
Benjamin Copeland15d743e2021-02-22 08:35:10 +0000277 if "skip_install" in test:
278 self.skip_install = test["skip_install"]
Chase Qi09edc7f2016-08-18 13:18:50 +0800279 self.custom_params = None
Benjamin Copeland15d743e2021-02-22 08:35:10 +0000280 if "parameters" in test:
281 self.custom_params = test["parameters"]
282 if "params" in test:
283 self.custom_params = test["params"]
Milosz Wasilewski2fea70e2016-11-11 12:16:09 +0000284 self.exists = False
Benjamin Copeland15d743e2021-02-22 08:35:10 +0000285 if os.path.isfile(self.test["path"]):
Milosz Wasilewski2fea70e2016-11-11 12:16:09 +0000286 self.exists = True
Benjamin Copeland15d743e2021-02-22 08:35:10 +0000287 with open(self.test["path"], "r") as f:
Milosz Wasilewski2fea70e2016-11-11 12:16:09 +0000288 self.testdef = yaml.safe_load(f)
Benjamin Copeland15d743e2021-02-22 08:35:10 +0000289 if self.testdef["metadata"]["format"].startswith(
290 "Manual Test Definition"
291 ):
Milosz Wasilewski2fea70e2016-11-11 12:16:09 +0000292 self.is_manual = True
Nicolas Dechesne51b85a82017-02-04 00:45:48 +0100293 if self.is_manual:
294 self.runner = ManualTestRun(test, args)
Chase Qi43bb9122017-05-23 14:37:48 +0800295 elif self.args.target is not None:
Nicolas Dechesne51b85a82017-02-04 00:45:48 +0100296 self.runner = RemoteTestRun(test, args)
Chase Qi43bb9122017-05-23 14:37:48 +0800297 else:
298 self.runner = AutomatedTestRun(test, args)
Chase Qi09edc7f2016-08-18 13:18:50 +0800299
300 def definition(self):
Benjamin Copeland15d743e2021-02-22 08:35:10 +0000301 with open("%s/testdef.yaml" % self.test["test_path"], "wb") as f:
302 f.write(yaml.dump(self.testdef, encoding="utf-8", allow_unicode=True))
Chase Qi09edc7f2016-08-18 13:18:50 +0800303
304 def metadata(self):
Benjamin Copeland15d743e2021-02-22 08:35:10 +0000305 with open("%s/testdef_metadata" % self.test["test_path"], "wb") as f:
306 f.write(
307 yaml.dump(
308 self.testdef["metadata"], encoding="utf-8", allow_unicode=True
309 )
310 )
Chase Qi09edc7f2016-08-18 13:18:50 +0800311
Nicolas Dechesne51b85a82017-02-04 00:45:48 +0100312 def mkrun(self):
Milosz Wasilewski2fea70e2016-11-11 12:16:09 +0000313 if not self.is_manual:
Benjamin Copeland15d743e2021-02-22 08:35:10 +0000314 with open("%s/run.sh" % self.test["test_path"], "a") as f:
315 f.write("#!/bin/sh\n")
Chase Qi09edc7f2016-08-18 13:18:50 +0800316
Milosz Wasilewski2fea70e2016-11-11 12:16:09 +0000317 self.parameters = self.handle_parameters()
318 if self.parameters:
319 for line in self.parameters:
320 f.write(line)
Chase Qi09edc7f2016-08-18 13:18:50 +0800321
Benjamin Copeland15d743e2021-02-22 08:35:10 +0000322 f.write("set -e\n")
323 f.write("set -x\n")
324 f.write("export TESTRUN_ID=%s\n" % self.testdef["metadata"]["name"])
Milosz Wasilewski682120e2017-03-13 13:37:18 +0000325 if self.args.target is None:
Benjamin Copeland15d743e2021-02-22 08:35:10 +0000326 f.write("cd %s\n" % (self.test["test_path"]))
Milosz Wasilewski682120e2017-03-13 13:37:18 +0000327 else:
Benjamin Copeland15d743e2021-02-22 08:35:10 +0000328 f.write("cd %s\n" % (self.test["target_test_path"]))
329 f.write("UUID=`cat uuid`\n")
Milosz Wasilewski2fea70e2016-11-11 12:16:09 +0000330 f.write('echo "<STARTRUN $TESTRUN_ID $UUID>"\n')
Benjamin Copeland15d743e2021-02-22 08:35:10 +0000331 f.write(
332 "export PATH=/usr/local/bin:/usr/bin:/bin:/usr/local/sbin:/usr/sbin:/sbin\n"
333 )
334 steps = self.testdef["run"].get("steps", [])
Milosz Wasilewski2fea70e2016-11-11 12:16:09 +0000335 if steps:
Dan Rueb592da12017-06-07 16:32:43 -0500336 for step in steps:
337 command = step
Benjamin Copeland15d743e2021-02-22 08:35:10 +0000338 if "--cmd" in step or "--shell" in step:
339 command = re.sub(r"\$(\d+)\b", r"\\$\1", step)
340 f.write("%s\n" % command)
Milosz Wasilewski2fea70e2016-11-11 12:16:09 +0000341 f.write('echo "<ENDRUN $TESTRUN_ID $UUID>"\n')
Chase Qi09edc7f2016-08-18 13:18:50 +0800342
Benjamin Copeland15d743e2021-02-22 08:35:10 +0000343 os.chmod("%s/run.sh" % self.test["test_path"], 0o755)
Milosz Wasilewski2fea70e2016-11-11 12:16:09 +0000344
Nicolas Dechesne51b85a82017-02-04 00:45:48 +0100345 def run(self):
346 self.runner.run()
Chase Qi09edc7f2016-08-18 13:18:50 +0800347
348 def handle_parameters(self):
Benjamin Copeland15d743e2021-02-22 08:35:10 +0000349 ret_val = ["###default parameters from test definition###\n"]
Chase Qi09edc7f2016-08-18 13:18:50 +0800350
Benjamin Copeland15d743e2021-02-22 08:35:10 +0000351 if "params" in self.testdef:
352 for def_param_name, def_param_value in list(self.testdef["params"].items()):
Chase Qi09edc7f2016-08-18 13:18:50 +0800353 # ?'yaml_line'
Benjamin Copeland15d743e2021-02-22 08:35:10 +0000354 if def_param_name == "yaml_line":
Chase Qi09edc7f2016-08-18 13:18:50 +0800355 continue
Benjamin Copeland15d743e2021-02-22 08:35:10 +0000356 ret_val.append("%s='%s'\n" % (def_param_name, def_param_value))
357 elif "parameters" in self.testdef:
358 for def_param_name, def_param_value in list(
359 self.testdef["parameters"].items()
360 ):
361 if def_param_name == "yaml_line":
Chase Qi09edc7f2016-08-18 13:18:50 +0800362 continue
Benjamin Copeland15d743e2021-02-22 08:35:10 +0000363 ret_val.append("%s='%s'\n" % (def_param_name, def_param_value))
Chase Qi09edc7f2016-08-18 13:18:50 +0800364 else:
365 return None
366
Benjamin Copeland15d743e2021-02-22 08:35:10 +0000367 ret_val.append("######\n")
Chase Qi09edc7f2016-08-18 13:18:50 +0800368
Benjamin Copeland15d743e2021-02-22 08:35:10 +0000369 ret_val.append("###custom parameters from test plan###\n")
Chase Qi09edc7f2016-08-18 13:18:50 +0800370 if self.custom_params:
371 for param_name, param_value in list(self.custom_params.items()):
Benjamin Copeland15d743e2021-02-22 08:35:10 +0000372 if param_name == "yaml_line":
Chase Qi09edc7f2016-08-18 13:18:50 +0800373 continue
Benjamin Copeland15d743e2021-02-22 08:35:10 +0000374 ret_val.append("%s='%s'\n" % (param_name, param_value))
Chase Qi09edc7f2016-08-18 13:18:50 +0800375
376 if self.skip_install:
377 ret_val.append('SKIP_INSTALL="True"\n')
Benjamin Copeland15d743e2021-02-22 08:35:10 +0000378 ret_val.append("######\n")
Chase Qi09edc7f2016-08-18 13:18:50 +0800379
Benjamin Copeland15d743e2021-02-22 08:35:10 +0000380 ret_val.append("###custom parameters from command line###\n")
Milosz Wasilewski259ba192017-07-27 10:59:25 +0100381 if self.args.test_def_params:
382 for param_name, param_value in self.args.test_def_params.items():
Benjamin Copeland15d743e2021-02-22 08:35:10 +0000383 ret_val.append("%s='%s'\n" % (param_name, param_value))
384 ret_val.append("######\n")
Chase Qi09edc7f2016-08-18 13:18:50 +0800385 return ret_val
386
387
388class TestRun(object):
389 def __init__(self, test, args):
Milosz Wasilewski682120e2017-03-13 13:37:18 +0000390 self.test = test
391 self.args = args
Benjamin Copeland15d743e2021-02-22 08:35:10 +0000392 self.logger = logging.getLogger("RUNNER.TestRun")
Milosz Wasilewski682120e2017-03-13 13:37:18 +0000393 self.test_timeout = self.args.timeout
Benjamin Copeland15d743e2021-02-22 08:35:10 +0000394 if "timeout" in test:
395 self.test_timeout = test["timeout"]
Milosz Wasilewski2fea70e2016-11-11 12:16:09 +0000396
397 def run(self):
398 raise NotImplementedError
399
400 def check_result(self):
401 raise NotImplementedError
402
403
404class AutomatedTestRun(TestRun):
405 def run(self):
Benjamin Copeland15d743e2021-02-22 08:35:10 +0000406 self.logger.info("Executing %s/run.sh" % self.test["test_path"])
407 shell_cmd = "%s/run.sh 2>&1 | tee %s/stdout.log" % (
408 self.test["test_path"],
409 self.test["test_path"],
410 )
411 self.child = pexpect.spawnu("/bin/sh", ["-c", shell_cmd])
Milosz Wasilewski682120e2017-03-13 13:37:18 +0000412 self.check_result()
Chase Qi09edc7f2016-08-18 13:18:50 +0800413
Milosz Wasilewski2fea70e2016-11-11 12:16:09 +0000414 def check_result(self):
Chase Qi09edc7f2016-08-18 13:18:50 +0800415 if self.test_timeout:
Benjamin Copeland15d743e2021-02-22 08:35:10 +0000416 self.logger.info("Test timeout: %s" % self.test_timeout)
Chase Qi09edc7f2016-08-18 13:18:50 +0800417 test_end = time.time() + self.test_timeout
418
419 while self.child.isalive():
420 if self.test_timeout and time.time() > test_end:
Benjamin Copeland15d743e2021-02-22 08:35:10 +0000421 self.logger.warning(
422 "%s test timed out, killing test process..."
423 % self.test["test_uuid"]
424 )
Chase Qi09edc7f2016-08-18 13:18:50 +0800425 self.child.terminate(force=True)
426 break
427 try:
Benjamin Copeland15d743e2021-02-22 08:35:10 +0000428 self.child.expect("\r\n")
Milosz Wasilewskid71f28a2020-06-10 20:05:13 +0100429 print(self.child.before)
Chase Qi09edc7f2016-08-18 13:18:50 +0800430 except pexpect.TIMEOUT:
431 continue
432 except pexpect.EOF:
Benjamin Copeland15d743e2021-02-22 08:35:10 +0000433 self.logger.info("%s test finished.\n" % self.test["test_uuid"])
Chase Qi09edc7f2016-08-18 13:18:50 +0800434 break
435
436
Milosz Wasilewski682120e2017-03-13 13:37:18 +0000437class RemoteTestRun(AutomatedTestRun):
438 def copy_to_target(self):
Benjamin Copeland15d743e2021-02-22 08:35:10 +0000439 os.chdir(self.test["test_path"])
Milosz Wasilewski682120e2017-03-13 13:37:18 +0000440 tarball_name = "target-test-files.tar"
Dan Ruea9eb01c2017-06-07 16:29:09 -0500441
442 self.logger.info("Archiving test files")
443 run_command(
Benjamin Copeland15d743e2021-02-22 08:35:10 +0000444 "tar -caf %s run.sh uuid automated/lib automated/bin automated/utils %s"
445 % (tarball_name, self.test["tc_relative_dir"])
446 )
Dan Ruea9eb01c2017-06-07 16:29:09 -0500447
448 self.logger.info("Creating test path")
Benjamin Copeland15d743e2021-02-22 08:35:10 +0000449 run_command("mkdir -p %s" % (self.test["target_test_path"]), self.args.target)
Dan Ruea9eb01c2017-06-07 16:29:09 -0500450
451 self.logger.info("Copying test archive to target host")
Benjamin Copeland15d743e2021-02-22 08:35:10 +0000452 run_command(
453 "scp %s ./%s %s:%s"
454 % (
455 SSH_PARAMS,
456 tarball_name,
457 self.args.target,
458 self.test["target_test_path"],
459 )
460 )
Dan Ruea9eb01c2017-06-07 16:29:09 -0500461
462 self.logger.info("Unarchiving test files on target")
Benjamin Copeland15d743e2021-02-22 08:35:10 +0000463 run_command(
464 "cd %s && tar -xf %s" % (self.test["target_test_path"], tarball_name),
465 self.args.target,
466 )
Dan Ruea9eb01c2017-06-07 16:29:09 -0500467
468 self.logger.info("Removing test file archive from target")
Benjamin Copeland15d743e2021-02-22 08:35:10 +0000469 run_command(
470 "rm %s/%s" % (self.test["target_test_path"], tarball_name), self.args.target
471 )
Milosz Wasilewski682120e2017-03-13 13:37:18 +0000472
Milosz Wasilewski4cf59a82021-11-22 15:17:28 +0000473 def cleanup_target(self):
474 self.logger.info("Removing files from target after testing")
475 run_command("rm -rf %s" % (self.test["target_test_path"]), self.args.target)
476
Milosz Wasilewski682120e2017-03-13 13:37:18 +0000477 def run(self):
478 self.copy_to_target()
Benjamin Copeland15d743e2021-02-22 08:35:10 +0000479 self.logger.info(
480 "Executing %s/run.sh remotely on %s"
481 % (self.test["target_test_path"], self.args.target)
482 )
483 shell_cmd = 'ssh %s %s "%s/run.sh 2>&1"' % (
484 SSH_PARAMS,
485 self.args.target,
486 self.test["target_test_path"],
487 )
488 self.logger.debug("shell_cmd: %s" % shell_cmd)
489 output = open("%s/stdout.log" % self.test["test_path"], "w")
Milosz Wasilewskid71f28a2020-06-10 20:05:13 +0100490 self.child = pexpect.spawnu(shell_cmd)
Milosz Wasilewski682120e2017-03-13 13:37:18 +0000491 self.child.logfile = output
492 self.check_result()
Milosz Wasilewski4cf59a82021-11-22 15:17:28 +0000493 self.cleanup_target()
Milosz Wasilewski682120e2017-03-13 13:37:18 +0000494
495
Milosz Wasilewski2fea70e2016-11-11 12:16:09 +0000496class ManualTestShell(cmd.Cmd):
Nicolas Dechesne1945d3f2020-10-07 23:36:34 +0200497 def __init__(self, test_dict, result_path, test_case_id):
Milosz Wasilewski2fea70e2016-11-11 12:16:09 +0000498 cmd.Cmd.__init__(self)
499 self.test_dict = test_dict
Nicolas Dechesne1945d3f2020-10-07 23:36:34 +0200500 self.test_case_id = test_case_id
Milosz Wasilewski2fea70e2016-11-11 12:16:09 +0000501 self.result_path = result_path
502 self.current_step_index = 0
Benjamin Copeland15d743e2021-02-22 08:35:10 +0000503 self.steps = self.test_dict["run"]["steps"]
504 self.expected = self.test_dict["run"]["expected"]
505 self.prompt = "%s[%s] > " % (
506 self.test_dict["metadata"]["name"],
507 self.test_case_id,
508 )
Milosz Wasilewski2fea70e2016-11-11 12:16:09 +0000509 self.result = None
510 self.intro = """
511 Welcome to manual test executor. Type 'help' for available commands.
512 This shell is meant to be executed on your computer, not on the system
513 under test. Please execute the steps from the test case, compare to
514 expected result and record the test result as 'pass' or 'fail'. If there
515 is an issue that prevents from executing the step, please record the result
516 as 'skip'.
517 """
518
519 def do_quit(self, line):
520 """
521 Exit test execution
522 """
523 if self.result is not None:
524 return True
525 if line.find("-f") >= 0:
526 self._record_result("skip")
527 return True
Benjamin Copeland15d743e2021-02-22 08:35:10 +0000528 print(
529 "Test result not recorded. Use -f to force. Forced quit records result as 'skip'"
530 )
Milosz Wasilewski2fea70e2016-11-11 12:16:09 +0000531
532 do_EOF = do_quit
533
534 def do_description(self, line):
535 """
536 Prints current test overall description
537 """
Benjamin Copeland15d743e2021-02-22 08:35:10 +0000538 print(self.test_dict["metadata"]["description"])
Milosz Wasilewski2fea70e2016-11-11 12:16:09 +0000539
540 def do_steps(self, line):
541 """
542 Prints all steps of the current test case
543 """
544 for index, step in enumerate(self.steps):
Chase Qic69235d2017-05-23 14:56:47 +0800545 print("%s. %s" % (index, step))
Milosz Wasilewski2fea70e2016-11-11 12:16:09 +0000546
547 def do_expected(self, line):
548 """
549 Prints all expected results of the current test case
550 """
551 for index, expected in enumerate(self.expected):
Chase Qic69235d2017-05-23 14:56:47 +0800552 print("%s. %s" % (index, expected))
Milosz Wasilewski2fea70e2016-11-11 12:16:09 +0000553
554 def do_current(self, line):
555 """
556 Prints current test step
557 """
558 self._print_step()
559
560 do_start = do_current
561
562 def do_next(self, line):
563 """
564 Prints next test step
565 """
566 if len(self.steps) > self.current_step_index + 1:
567 self.current_step_index += 1
568 self._print_step()
569
570 def _print_step(self):
Chase Qic69235d2017-05-23 14:56:47 +0800571 print("%s. %s" % (self.current_step_index, self.steps[self.current_step_index]))
Milosz Wasilewski2fea70e2016-11-11 12:16:09 +0000572
573 def _record_result(self, result):
Chase Qic69235d2017-05-23 14:56:47 +0800574 print("Recording %s in %s/stdout.log" % (result, self.result_path))
Milosz Wasilewski2fea70e2016-11-11 12:16:09 +0000575 with open("%s/stdout.log" % self.result_path, "a") as f:
Benjamin Copeland15d743e2021-02-22 08:35:10 +0000576 f.write(
577 "<LAVA_SIGNAL_TESTCASE TEST_CASE_ID=%s RESULT=%s>"
578 % (self.test_case_id, result)
579 )
Milosz Wasilewski2fea70e2016-11-11 12:16:09 +0000580
581 def do_pass(self, line):
582 """
583 Records PASS as test result
584 """
585 self.result = "pass"
586 self._record_result(self.result)
587 return True
588
589 def do_fail(self, line):
590 """
591 Records FAIL as test result
592 """
593 self.result = "fail"
594 self._record_result(self.result)
595 return True
596
597 def do_skip(self, line):
598 """
599 Records SKIP as test result
600 """
601 self.result = "skip"
602 self._record_result(self.result)
603 return True
604
605
606class ManualTestRun(TestRun, cmd.Cmd):
607 def run(self):
Benjamin Copeland15d743e2021-02-22 08:35:10 +0000608 print(self.test["test_name"])
609 with open("%s/testdef.yaml" % self.test["test_path"], "r") as f:
Milosz Wasilewski2fea70e2016-11-11 12:16:09 +0000610 self.testdef = yaml.safe_load(f)
611
Benjamin Copeland15d743e2021-02-22 08:35:10 +0000612 if "name" in self.test:
613 test_case_id = self.test["name"]
Nicolas Dechesne1945d3f2020-10-07 23:36:34 +0200614 else:
Benjamin Copeland15d743e2021-02-22 08:35:10 +0000615 test_case_id = self.testdef["metadata"]["name"]
Nicolas Dechesne1945d3f2020-10-07 23:36:34 +0200616
Benjamin Copeland15d743e2021-02-22 08:35:10 +0000617 ManualTestShell(self.testdef, self.test["test_path"], test_case_id).cmdloop()
Milosz Wasilewski2fea70e2016-11-11 12:16:09 +0000618
619 def check_result(self):
620 pass
621
622
Dan Ruea9eb01c2017-06-07 16:29:09 -0500623def get_packages(linux_distribution, target=None):
Benjamin Copelande7debbe2021-02-22 09:31:14 +0000624 """Return a list of installed packages with versions
Dan Ruea9eb01c2017-06-07 16:29:09 -0500625
Benjamin Copelande7debbe2021-02-22 09:31:14 +0000626 linux_distribution is a string that may be 'debian',
627 'ubuntu', 'centos', or 'fedora'.
Dan Ruea9eb01c2017-06-07 16:29:09 -0500628
Benjamin Copelande7debbe2021-02-22 09:31:14 +0000629 For example (ubuntu):
630 'packages': ['acl-2.2.52-2',
631 'adduser-3.113+nmu3',
632 ...
633 'zlib1g:amd64-1:1.2.8.dfsg-2+b1',
634 'zlib1g-dev:amd64-1:1.2.8.dfsg-2+b1']
Dan Ruea9eb01c2017-06-07 16:29:09 -0500635
Benjamin Copelande7debbe2021-02-22 09:31:14 +0000636 (centos):
637 "packages": ["acl-2.2.51-12.el7",
638 "apr-1.4.8-3.el7",
639 ...
640 "zlib-1.2.7-17.el7",
641 "zlib-devel-1.2.7-17.el7"
642 ]
Dan Ruea9eb01c2017-06-07 16:29:09 -0500643 """
644
Benjamin Copeland15d743e2021-02-22 08:35:10 +0000645 logger = logging.getLogger("RUNNER.get_packages")
Dan Ruea9eb01c2017-06-07 16:29:09 -0500646 packages = []
Benjamin Copeland15d743e2021-02-22 08:35:10 +0000647 if linux_distribution in ["debian", "ubuntu"]:
Dan Ruea9eb01c2017-06-07 16:29:09 -0500648 # Debian (apt) based system
Benjamin Copeland15d743e2021-02-22 08:35:10 +0000649 packages = run_command(
650 "dpkg-query -W -f '${package}-${version}\n'", target
651 ).splitlines()
Dan Ruea9eb01c2017-06-07 16:29:09 -0500652
Benjamin Copeland15d743e2021-02-22 08:35:10 +0000653 elif linux_distribution in ["centos", "fedora"]:
Dan Ruea9eb01c2017-06-07 16:29:09 -0500654 # RedHat (rpm) based system
Benjamin Copeland15d743e2021-02-22 08:35:10 +0000655 packages = run_command(
656 "rpm -qa --qf '%{NAME}-%{VERSION}-%{RELEASE}\n'", target
657 ).splitlines()
Dan Ruea9eb01c2017-06-07 16:29:09 -0500658 else:
Benjamin Copeland15d743e2021-02-22 08:35:10 +0000659 logger.warning(
660 "Unknown linux distribution '{}'; package list not populated.".format(
661 linux_distribution
662 )
663 )
Dan Ruea9eb01c2017-06-07 16:29:09 -0500664
665 packages.sort()
666 return packages
667
668
669def get_environment(target=None, skip_collection=False):
Benjamin Copelande7debbe2021-02-22 09:31:14 +0000670 """Return a dictionary with environmental information
Dan Ruea9eb01c2017-06-07 16:29:09 -0500671
Benjamin Copelande7debbe2021-02-22 09:31:14 +0000672 target: optional ssh host string to gather environment remotely.
673 skip_collection: Skip data collection and return an empty dictionary.
Dan Ruea9eb01c2017-06-07 16:29:09 -0500674
Benjamin Copelande7debbe2021-02-22 09:31:14 +0000675 For example (on a HiSilicon D03):
676 {
677 "bios_version": "Hisilicon D03 UEFI 16.12 Release",
678 "board_name": "D03",
679 "board_vendor": "Huawei",
680 "kernel": "4.9.0-20.gitedc2a1c.linaro.aarch64",
681 "linux_distribution": "centos",
Mikko Rapeli82ebc052022-10-21 13:47:51 +0100682 "linux_distribution_version": "6.2",
Benjamin Copelande7debbe2021-02-22 09:31:14 +0000683 "packages": [
684 "GeoIP-1.5.0-11.el7",
685 "NetworkManager-1.4.0-20.el7_3",
686 ...
687 "yum-plugin-fastestmirror-1.1.31-40.el7",
688 "zlib-1.2.7-17.el7"
689 ],
690 "uname": "Linux localhost.localdomain 4.9.0-20.gitedc2a1c.linaro.aarch64 #1 SMP Wed Dec 14 17:50:15 UTC 2016 aarch64 aarch64 aarch64 GNU/Linux"
691 }
Dan Ruea9eb01c2017-06-07 16:29:09 -0500692 """
693
694 environment = {}
695 if skip_collection:
696 return environment
Milosz Wasilewskidf71a762017-07-20 13:26:21 +0100697 try:
Benjamin Copeland15d743e2021-02-22 08:35:10 +0000698 environment["linux_distribution"] = (
699 run_command("grep ^ID= /etc/os-release", target)
700 .split("=")[-1]
701 .strip('"')
702 .lower()
703 )
Milosz Wasilewskidf71a762017-07-20 13:26:21 +0100704 except subprocess.CalledProcessError:
Benjamin Copeland15d743e2021-02-22 08:35:10 +0000705 environment["linux_distribution"] = ""
Milosz Wasilewskidf71a762017-07-20 13:26:21 +0100706
707 try:
Mikko Rapeli82ebc052022-10-21 13:47:51 +0100708 environment["linux_distribution_version"] = (
709 run_command("grep ^VERSION= /etc/os-release", target)
710 .split("=")[-1]
711 .strip('"')
712 )
713 except subprocess.CalledProcessError:
714 environment["linux_distribution_version"] = ""
715
716 try:
717 environment["linux_distribution_version_id"] = (
718 run_command("grep ^VERSION_ID= /etc/os-release", target)
719 .split("=")[-1]
720 .strip('"')
721 )
722 except subprocess.CalledProcessError:
723 environment["linux_distribution_version_id"] = ""
724
725 try:
726 environment["linux_distribution_version_codename"] = (
727 run_command("grep ^VERSION_CODENAME= /etc/os-release", target)
728 .split("=")[-1]
729 .strip('"')
730 )
731 except subprocess.CalledProcessError:
732 environment["linux_distribution_version_codename"] = ""
733
734 try:
Benjamin Copeland15d743e2021-02-22 08:35:10 +0000735 environment["kernel"] = run_command("uname -r", target)
Milosz Wasilewskidf71a762017-07-20 13:26:21 +0100736 except subprocess.CalledProcessError:
Benjamin Copeland15d743e2021-02-22 08:35:10 +0000737 environment["kernel"] = ""
Milosz Wasilewskidf71a762017-07-20 13:26:21 +0100738
739 try:
Benjamin Copeland15d743e2021-02-22 08:35:10 +0000740 environment["uname"] = run_command("uname -a", target)
Milosz Wasilewskidf71a762017-07-20 13:26:21 +0100741 except subprocess.CalledProcessError:
Benjamin Copeland15d743e2021-02-22 08:35:10 +0000742 environment["uname"] = ""
Dan Ruea9eb01c2017-06-07 16:29:09 -0500743
744 try:
Benjamin Copeland15d743e2021-02-22 08:35:10 +0000745 environment["bios_version"] = run_command(
746 "cat /sys/devices/virtual/dmi/id/bios_version", target
747 )
Dan Ruea9eb01c2017-06-07 16:29:09 -0500748 except subprocess.CalledProcessError:
Benjamin Copeland15d743e2021-02-22 08:35:10 +0000749 environment["bios_version"] = ""
Dan Ruea9eb01c2017-06-07 16:29:09 -0500750
751 try:
Benjamin Copeland15d743e2021-02-22 08:35:10 +0000752 environment["board_vendor"] = run_command(
753 "cat /sys/devices/virtual/dmi/id/board_vendor", target
754 )
Dan Ruea9eb01c2017-06-07 16:29:09 -0500755 except subprocess.CalledProcessError:
Benjamin Copeland15d743e2021-02-22 08:35:10 +0000756 environment["board_vendor"] = ""
Dan Ruea9eb01c2017-06-07 16:29:09 -0500757
758 try:
Benjamin Copeland15d743e2021-02-22 08:35:10 +0000759 environment["board_name"] = run_command(
760 "cat /sys/devices/virtual/dmi/id/board_name", target
761 )
Dan Ruea9eb01c2017-06-07 16:29:09 -0500762 except subprocess.CalledProcessError:
Benjamin Copeland15d743e2021-02-22 08:35:10 +0000763 environment["board_name"] = ""
Dan Ruea9eb01c2017-06-07 16:29:09 -0500764
Milosz Wasilewskidf71a762017-07-20 13:26:21 +0100765 try:
Benjamin Copeland15d743e2021-02-22 08:35:10 +0000766 environment["packages"] = get_packages(
767 environment["linux_distribution"], target
768 )
Milosz Wasilewskidf71a762017-07-20 13:26:21 +0100769 except subprocess.CalledProcessError:
Benjamin Copeland15d743e2021-02-22 08:35:10 +0000770 environment["packages"] = []
Dan Ruea9eb01c2017-06-07 16:29:09 -0500771 return environment
772
773
Chase Qi09edc7f2016-08-18 13:18:50 +0800774class ResultParser(object):
775 def __init__(self, test, args):
Milosz Wasilewski682120e2017-03-13 13:37:18 +0000776 self.test = test
777 self.args = args
Chase Qi09edc7f2016-08-18 13:18:50 +0800778 self.metrics = []
779 self.results = {}
Benjamin Copeland15d743e2021-02-22 08:35:10 +0000780 self.results["test"] = test["test_name"]
781 self.results["id"] = test["test_uuid"]
782 self.results["test_plan"] = args.test_plan
783 self.results["environment"] = get_environment(
784 target=self.args.target, skip_collection=self.args.skip_environment
785 )
786 self.logger = logging.getLogger("RUNNER.ResultParser")
787 self.results["params"] = {}
Chase Qie94ba522017-05-26 12:05:18 +0800788 self.pattern = None
789 self.fixup = None
Vishal Bhoje94da4a2020-05-15 12:22:40 +0530790 self.qa_reports_server = args.qa_reports_server
791 if args.qa_reports_token is not None:
792 self.qa_reports_token = args.qa_reports_token
793 else:
Benjamin Copeland15d743e2021-02-22 08:35:10 +0000794 self.qa_reports_token = os.environ.get(
795 "QA_REPORTS_TOKEN", get_token_from_netrc(self.qa_reports_server)
796 )
Vishal Bhoje94da4a2020-05-15 12:22:40 +0530797 self.qa_reports_project = args.qa_reports_project
798 self.qa_reports_group = args.qa_reports_group
799 self.qa_reports_env = args.qa_reports_env
800 self.qa_reports_build_version = args.qa_reports_build_version
Milosz Wasilewskib4b40de2020-06-11 12:54:18 +0100801 self.qa_reports_disable_metadata = args.qa_reports_disable_metadata
Milosz Wasilewski37848d32020-06-11 14:33:43 +0100802 self.qa_reports_metadata = args.qa_reports_metadata
Milosz Wasilewskiabe2dcc2020-06-12 10:54:46 +0100803 self.qa_reports_metadata_file = args.qa_reports_metadata_file
Vishal Bhoje94da4a2020-05-15 12:22:40 +0530804
Benjamin Copeland15d743e2021-02-22 08:35:10 +0000805 with open(os.path.join(self.test["test_path"], "testdef.yaml"), "r") as f:
Milosz Wasilewskia76e8dd2016-11-25 14:13:25 +0000806 self.testdef = yaml.safe_load(f)
Benjamin Copeland15d743e2021-02-22 08:35:10 +0000807 self.results["name"] = ""
808 if (
809 "metadata" in self.testdef.keys()
810 and "name" in self.testdef["metadata"].keys()
811 ):
812 self.results["name"] = self.testdef["metadata"]["name"]
813 if "params" in self.testdef.keys():
814 self.results["params"] = self.testdef["params"]
Nicolas Dechesnefaa343d2017-10-23 00:33:10 +0200815 if self.args.test_def_params:
816 for param_name, param_value in self.args.test_def_params.items():
Benjamin Copeland15d743e2021-02-22 08:35:10 +0000817 self.results["params"][param_name] = param_value
818 if (
819 "parse" in self.testdef.keys()
820 and "pattern" in self.testdef["parse"].keys()
821 ):
822 self.pattern = self.testdef["parse"]["pattern"]
Chase Qie94ba522017-05-26 12:05:18 +0800823 self.logger.info("Enabling log parse pattern: %s" % self.pattern)
Benjamin Copeland15d743e2021-02-22 08:35:10 +0000824 if "fixupdict" in self.testdef["parse"].keys():
825 self.fixup = self.testdef["parse"]["fixupdict"]
826 self.logger.info(
827 "Enabling log parse pattern fixup: %s" % self.fixup
828 )
829 if "parameters" in test.keys():
830 self.results["params"].update(test["parameters"])
831 if "params" in test.keys():
832 self.results["params"].update(test["params"])
833 if "version" in test.keys():
834 self.results["version"] = test["version"]
Milosz Wasilewski970431b2016-11-25 14:10:08 +0000835 else:
836 path = os.getcwd()
Benjamin Copeland15d743e2021-02-22 08:35:10 +0000837 os.chdir(self.test["test_path"])
Chase Qi3efa7692017-06-26 15:54:05 +0800838 if sys.version_info[0] < 3:
839 test_version = subprocess.check_output("git rev-parse HEAD", shell=True)
840 else:
Benjamin Copeland15d743e2021-02-22 08:35:10 +0000841 test_version = subprocess.check_output(
842 "git rev-parse HEAD", shell=True
843 ).decode("utf-8")
844 self.results["version"] = test_version.rstrip()
Milosz Wasilewski970431b2016-11-25 14:10:08 +0000845 os.chdir(path)
Chase Qiea543352017-09-21 16:44:30 +0800846 self.lava_run = args.lava_run
Benjamin Copeland15d743e2021-02-22 08:35:10 +0000847 if self.lava_run and not find_executable("lava-test-case"):
848 self.logger.info(
849 "lava-test-case not found, '-l' or '--lava_run' option ignored'"
850 )
Chase Qiea543352017-09-21 16:44:30 +0800851 self.lava_run = False
Chase Qi09edc7f2016-08-18 13:18:50 +0800852
853 def run(self):
854 self.parse_stdout()
Chase Qie94ba522017-05-26 12:05:18 +0800855 if self.pattern:
856 self.parse_pattern()
857 # If 'metrics' is empty, add 'no-result-found fail'.
858 if not self.metrics:
Benjamin Copeland15d743e2021-02-22 08:35:10 +0000859 self.metrics = [
860 {
861 "test_case_id": "no-result-found",
862 "result": "fail",
863 "measurement": "",
864 "units": "",
865 }
866 ]
867 self.results["metrics"] = self.metrics
Chase Qi09edc7f2016-08-18 13:18:50 +0800868 self.dict_to_json()
869 self.dict_to_csv()
Vishal Bhoje94da4a2020-05-15 12:22:40 +0530870 self.send_to_qa_reports()
Milosz Wasilewski31ac9852021-11-18 11:21:28 +0000871 self.send_to_fiotest()
Benjamin Copeland15d743e2021-02-22 08:35:10 +0000872 self.logger.info("Result files saved to: %s" % self.test["test_path"])
873 print("--- Printing result.csv ---")
874 with open("%s/result.csv" % self.test["test_path"]) as f:
Chase Qi09edc7f2016-08-18 13:18:50 +0800875 print(f.read())
876
877 def parse_stdout(self):
Benjamin Copeland15d743e2021-02-22 08:35:10 +0000878 with open("%s/stdout.log" % self.test["test_path"], "r") as f:
Milosz Wasilewski5a4dd442016-12-07 15:01:57 +0000879 test_case_re = re.compile("TEST_CASE_ID=(.*)")
880 result_re = re.compile("RESULT=(.*)")
881 measurement_re = re.compile("MEASUREMENT=(.*)")
882 units_re = re.compile("UNITS=(.*)")
Chase Qi09edc7f2016-08-18 13:18:50 +0800883 for line in f:
Benjamin Copeland15d743e2021-02-22 08:35:10 +0000884 if re.match(r"\<(|LAVA_SIGNAL_TESTCASE )TEST_CASE_ID=.*", line):
885 line = line.strip("\n").strip("\r").strip("<>").split(" ")
886 data = {
887 "test_case_id": "",
888 "result": "",
889 "measurement": "",
890 "units": "",
891 }
Chase Qi09edc7f2016-08-18 13:18:50 +0800892
893 for string in line:
Milosz Wasilewskiff695622016-12-05 16:00:06 +0000894 test_case_match = test_case_re.match(string)
895 result_match = result_re.match(string)
896 measurement_match = measurement_re.match(string)
897 units_match = units_re.match(string)
898 if test_case_match:
Benjamin Copeland15d743e2021-02-22 08:35:10 +0000899 data["test_case_id"] = test_case_match.group(1)
Milosz Wasilewskiff695622016-12-05 16:00:06 +0000900 if result_match:
Benjamin Copeland15d743e2021-02-22 08:35:10 +0000901 data["result"] = result_match.group(1)
Milosz Wasilewskiff695622016-12-05 16:00:06 +0000902 if measurement_match:
Nicolas Dechesne508a2272020-10-03 11:10:37 +0200903 try:
Benjamin Copeland15d743e2021-02-22 08:35:10 +0000904 data["measurement"] = float(measurement_match.group(1))
Nicolas Dechesne508a2272020-10-03 11:10:37 +0200905 except ValueError as e:
906 pass
Milosz Wasilewskiff695622016-12-05 16:00:06 +0000907 if units_match:
Benjamin Copeland15d743e2021-02-22 08:35:10 +0000908 data["units"] = units_match.group(1)
Chase Qi09edc7f2016-08-18 13:18:50 +0800909
910 self.metrics.append(data.copy())
911
Chase Qiea543352017-09-21 16:44:30 +0800912 if self.lava_run:
913 self.send_to_lava(data)
914
Chase Qie94ba522017-05-26 12:05:18 +0800915 def parse_pattern(self):
Benjamin Copeland15d743e2021-02-22 08:35:10 +0000916 with open("%s/stdout.log" % self.test["test_path"], "r") as f:
917 rex_pattern = re.compile(r"%s" % self.pattern)
Chase Qie94ba522017-05-26 12:05:18 +0800918 for line in f:
919 data = {}
Aníbal Limónc36cb792017-11-29 11:54:30 -0600920 m = rex_pattern.search(line)
Chase Qie94ba522017-05-26 12:05:18 +0800921 if m:
922 data = m.groupdict()
Benjamin Copeland15d743e2021-02-22 08:35:10 +0000923 for x in ["measurement", "units"]:
Chase Qie94ba522017-05-26 12:05:18 +0800924 if x not in data:
Benjamin Copeland15d743e2021-02-22 08:35:10 +0000925 data[x] = ""
926 if self.fixup and data["result"] in self.fixup:
927 data["result"] = self.fixup[data["result"]]
Chase Qi1f2a9a02017-03-09 15:45:04 +0100928
Chase Qie94ba522017-05-26 12:05:18 +0800929 self.metrics.append(data.copy())
Chase Qi09edc7f2016-08-18 13:18:50 +0800930
Chase Qiea543352017-09-21 16:44:30 +0800931 if self.lava_run:
932 self.send_to_lava(data)
933
934 def send_to_lava(self, data):
Benjamin Copeland15d743e2021-02-22 08:35:10 +0000935 cmd = "lava-test-case {} --result {}".format(
936 data["test_case_id"], data["result"]
937 )
938 if data["measurement"]:
939 cmd = "{} --measurement {} --units {}".format(
940 cmd, data["measurement"], data["units"]
941 )
942 self.logger.debug("lava-run: cmd: {}".format(cmd))
Chase Qiea543352017-09-21 16:44:30 +0800943 subprocess.call(shlex.split(cmd))
944
Milosz Wasilewski31ac9852021-11-18 11:21:28 +0000945 def send_to_fiotest(self):
946 """
947 This method saves results as filesystem tree. This is required by
948 fiotest: https://github.com/foundriesio/fiotest/
949 """
950 # check if TEST_DIR variable is set
951 test_path = os.environ.get("TEST_DIR")
952 if not test_path:
Milosz Wasilewskid92aff12021-11-18 11:21:28 +0000953 self.logger.debug("TEST_DIR is not set")
954 self.logger.debug("NOT reporting result to fiotest")
Milosz Wasilewski31ac9852021-11-18 11:21:28 +0000955 return
956 # create directory with test name
957 try:
958 for metric in self.metrics:
959 local_ts = datetime.now()
960 dir_name = "{}-{}".format(local_ts.timestamp(), metric["test_case_id"])
961 os.makedirs(os.path.join(test_path, dir_name), exist_ok=True)
962 if metric["measurement"] != "":
963 metrics_dir = os.path.join(test_path, dir_name, "metrics")
964 os.makedirs(metrics_dir, exist_ok=True)
965 with open(os.path.join(metrics_dir, "value"), "w") as value_file:
966 value_file.write(metric["measurement"])
967 else:
968 if metric["result"] == "fail":
969 os.makedirs(
970 os.path.join(test_path, dir_name, "failed"), exist_ok=True
971 )
972 if metric["result"] == "skip":
973 os.makedirs(
974 os.path.join(test_path, dir_name, "skipped"), exist_ok=True
975 )
976 except PermissionError:
977 self.logger.error(
978 "Unable to prepare fiotest results due to lack of permissions"
979 )
980
Vishal Bhoje94da4a2020-05-15 12:22:40 +0530981 def send_to_qa_reports(self):
Benjamin Copeland15d743e2021-02-22 08:35:10 +0000982 if None in (
983 self.qa_reports_server,
984 self.qa_reports_token,
985 self.qa_reports_group,
986 self.qa_reports_project,
987 self.qa_reports_build_version,
988 self.qa_reports_env,
989 ):
990 self.logger.warning(
991 "All parameters for qa reports are not set, results will not be pushed to qa reports"
992 )
Vishal Bhoje94da4a2020-05-15 12:22:40 +0530993 return
994
Benjamin Copeland15d743e2021-02-22 08:35:10 +0000995 SquadApi.configure(url=self.qa_reports_server, token=self.qa_reports_token)
Vishal Bhoje94da4a2020-05-15 12:22:40 +0530996 tests = {}
997 metrics = {}
998 for metric in self.metrics:
Benjamin Copeland15d743e2021-02-22 08:35:10 +0000999 if metric["measurement"] != "":
1000 metrics[
1001 "{}/{}".format(self.test["test_name"], metric["test_case_id"])
1002 ] = metric["measurement"]
Vishal Bhoje94da4a2020-05-15 12:22:40 +05301003 else:
Benjamin Copeland15d743e2021-02-22 08:35:10 +00001004 tests[
1005 "{}/{}".format(self.test["test_name"], metric["test_case_id"])
1006 ] = metric["result"]
Vishal Bhoje94da4a2020-05-15 12:22:40 +05301007
Benjamin Copeland15d743e2021-02-22 08:35:10 +00001008 with open("{}/stdout.log".format(self.test["test_path"]), "r") as logfile:
Vishal Bhoje94da4a2020-05-15 12:22:40 +05301009 log = logfile.read()
1010
Milosz Wasilewskiabe2dcc2020-06-12 10:54:46 +01001011 metadata = {}
Milosz Wasilewskib00ceff2020-06-15 13:48:01 +01001012 if not self.qa_reports_disable_metadata:
1013 if self.qa_reports_metadata:
1014 metadata.update(self.qa_reports_metadata)
1015 if self.qa_reports_metadata_file:
1016 try:
1017 with open(self.qa_reports_metadata_file, "r") as metadata_file:
Benjamin Copeland15d743e2021-02-22 08:35:10 +00001018 loaded_metadata = yaml.load(
1019 metadata_file, Loader=yaml.SafeLoader
1020 )
Milosz Wasilewskib00ceff2020-06-15 13:48:01 +01001021 # check if loaded metadata is key=value and both are strings
1022 for key, value in loaded_metadata.items():
1023 if type(key) == str and type(value) == str:
1024 # only update metadata with simple keys
1025 # ignore all other items in the dictionary
1026 metadata.update({key: value})
1027 else:
1028 self.logger.warning("Ignoring key: %s" % key)
1029 except FileNotFoundError:
1030 self.logger.warning("Metadata file not found")
1031 except PermissionError:
Benjamin Copeland15d743e2021-02-22 08:35:10 +00001032 self.logger.warning(
1033 "Insufficient permissions to open metadata file"
1034 )
Milosz Wasilewskif883f102020-06-11 12:46:58 +01001035 if submit_results(
Benjamin Copeland15d743e2021-02-22 08:35:10 +00001036 group_project_slug="{}/{}".format(
1037 self.qa_reports_group, self.qa_reports_project
1038 ),
1039 build_version=self.qa_reports_build_version,
1040 env_slug=self.qa_reports_env,
1041 tests=tests,
1042 metrics=metrics,
1043 log=log,
1044 metadata=metadata,
1045 attachments=None,
1046 ):
Milosz Wasilewskif883f102020-06-11 12:46:58 +01001047 self.logger.info("Results pushed to QA Reports")
1048 else:
1049 self.logger.warning("Results upload to QA Reports failed!")
Vishal Bhoje94da4a2020-05-15 12:22:40 +05301050
Chase Qi09edc7f2016-08-18 13:18:50 +08001051 def dict_to_json(self):
Chase Qi87f4f402016-11-07 15:32:01 +08001052 # Save test results to output/test_id/result.json
Benjamin Copeland15d743e2021-02-22 08:35:10 +00001053 with open("%s/result.json" % self.test["test_path"], "w") as f:
Chase Qi87f4f402016-11-07 15:32:01 +08001054 json.dump([self.results], f, indent=4)
1055
Milosz Wasilewskiba21eeb2022-02-15 10:23:11 +00001056 if not self.args.cleanup:
1057 # Collect test results of all tests in output/result.json
1058 feeds = []
1059 if os.path.isfile("%s/result.json" % self.test["output"]):
1060 with open("%s/result.json" % self.test["output"], "r") as f:
1061 feeds = json.load(f)
Chase Qi87f4f402016-11-07 15:32:01 +08001062
Milosz Wasilewskiba21eeb2022-02-15 10:23:11 +00001063 feeds.append(self.results)
1064 with open("%s/result.json" % self.test["output"], "w") as f:
1065 json.dump(feeds, f, indent=4)
Chase Qi09edc7f2016-08-18 13:18:50 +08001066
1067 def dict_to_csv(self):
Chase Qica15cf52016-11-10 17:00:22 +08001068 # Convert dict self.results['params'] to a string.
Benjamin Copeland15d743e2021-02-22 08:35:10 +00001069 test_params = ""
1070 if self.results["params"]:
1071 params_dict = self.results["params"]
1072 test_params = ";".join(["%s=%s" % (k, v) for k, v in params_dict.items()])
Chase Qi09edc7f2016-08-18 13:18:50 +08001073
Benjamin Copeland15d743e2021-02-22 08:35:10 +00001074 for metric in self.results["metrics"]:
1075 metric["name"] = self.results["name"]
1076 metric["test_params"] = test_params
Chase Qica15cf52016-11-10 17:00:22 +08001077
1078 # Save test results to output/test_id/result.csv
Benjamin Copeland15d743e2021-02-22 08:35:10 +00001079 fieldnames = [
1080 "name",
1081 "test_case_id",
1082 "result",
1083 "measurement",
1084 "units",
1085 "test_params",
1086 ]
1087 with open("%s/result.csv" % self.test["test_path"], "w") as f:
Chase Qica15cf52016-11-10 17:00:22 +08001088 writer = csv.DictWriter(f, fieldnames=fieldnames)
Chase Qi09edc7f2016-08-18 13:18:50 +08001089 writer.writeheader()
Benjamin Copeland15d743e2021-02-22 08:35:10 +00001090 for metric in self.results["metrics"]:
Chase Qi09edc7f2016-08-18 13:18:50 +08001091 writer.writerow(metric)
1092
Milosz Wasilewskiba21eeb2022-02-15 10:23:11 +00001093 if not self.args.cleanup:
1094 # Collect test results of all tests in output/result.csv
1095 if not os.path.isfile("%s/result.csv" % self.test["output"]):
1096 with open("%s/result.csv" % self.test["output"], "w") as f:
1097 writer = csv.DictWriter(f, fieldnames=fieldnames)
1098 writer.writeheader()
Chase Qi87f4f402016-11-07 15:32:01 +08001099
Milosz Wasilewskiba21eeb2022-02-15 10:23:11 +00001100 with open("%s/result.csv" % self.test["output"], "a") as f:
1101 writer = csv.DictWriter(f, fieldnames=fieldnames)
1102 for metric in self.results["metrics"]:
1103 writer.writerow(metric)
Chase Qi09edc7f2016-08-18 13:18:50 +08001104
1105
Vishal Bhoje94da4a2020-05-15 12:22:40 +05301106def get_token_from_netrc(qa_reports_server):
1107 if qa_reports_server is None:
1108 return
1109 parse = urlparse(qa_reports_server)
1110 netrc_local = netrc.netrc()
1111 authTokens = netrc_local.authenticators("{}".format(parse.netloc))
1112 if authTokens is not None:
1113 hostname, username, authToken = authTokens
1114 return authToken
1115 # Unable to find Token hence returning None
1116 return
1117
1118
Chase Qi09edc7f2016-08-18 13:18:50 +08001119def get_args():
Milosz Wasilewski259ba192017-07-27 10:59:25 +01001120 parser = argparse.ArgumentParser(formatter_class=argparse.RawTextHelpFormatter)
Benjamin Copeland15d743e2021-02-22 08:35:10 +00001121 parser.add_argument(
1122 "-o",
1123 "--output",
1124 default=os.getenv("HOME", "") + "/output",
1125 dest="output",
1126 help=textwrap.dedent(
1127 """\
Chase Qi09edc7f2016-08-18 13:18:50 +08001128 specify a directory to store test and result files.
Nicolas Dechesnef6c4c212017-01-18 17:30:04 +01001129 Default: $HOME/output
Benjamin Copeland15d743e2021-02-22 08:35:10 +00001130 """
1131 ),
1132 )
1133 parser.add_argument(
1134 "-p",
1135 "--test_plan",
1136 default=None,
1137 dest="test_plan",
1138 help=textwrap.dedent(
1139 """\
Thomas Böhler4b0ab502024-06-12 15:44:30 +02001140 specify a test plan file which has tests and related
Chase Qi09edc7f2016-08-18 13:18:50 +08001141 params listed in yaml format.
Benjamin Copeland15d743e2021-02-22 08:35:10 +00001142 """
1143 ),
1144 )
1145 parser.add_argument(
1146 "-d",
1147 "--test_def",
1148 default=None,
1149 dest="test_def",
1150 help=textwrap.dedent(
1151 """\
Chase Qi09edc7f2016-08-18 13:18:50 +08001152 base on test definition repo location, specify relative
1153 path to the test definition to run.
1154 Format example: "ubuntu/smoke-tests-basic.yaml"
Benjamin Copeland15d743e2021-02-22 08:35:10 +00001155 """
1156 ),
1157 )
1158 parser.add_argument(
1159 "-r",
1160 "--test_def_params",
1161 default={},
1162 dest="test_def_params",
1163 action=StoreDictKeyPair,
1164 nargs="+",
1165 metavar="KEY=VALUE",
1166 help=textwrap.dedent(
1167 """\
Milosz Wasilewski259ba192017-07-27 10:59:25 +01001168 Set additional parameters when using test definition without
1169 a test plan. The name values are set similarily to environment
1170 variables:
1171 --test_def_params KEY1=VALUE1 KEY2=VALUE2 ...
Benjamin Copeland15d743e2021-02-22 08:35:10 +00001172 """
1173 ),
1174 )
1175 parser.add_argument(
1176 "-k",
1177 "--kind",
1178 default="automated",
1179 dest="kind",
1180 choices=["automated", "manual"],
1181 help=textwrap.dedent(
1182 """\
Milosz Wasilewski2fea70e2016-11-11 12:16:09 +00001183 Selects type of tests to be executed from the test plan.
1184 Possible options: automated, manual
Benjamin Copeland15d743e2021-02-22 08:35:10 +00001185 """
1186 ),
1187 )
1188 parser.add_argument(
1189 "-t",
1190 "--timeout",
1191 type=int,
1192 default=None,
1193 dest="timeout",
1194 help="Specify test timeout",
1195 )
1196 parser.add_argument(
1197 "-g",
1198 "--target",
1199 default=None,
1200 dest="target",
1201 help=textwrap.dedent(
1202 """\
Milosz Wasilewski682120e2017-03-13 13:37:18 +00001203 Specify SSH target to execute tests.
1204 Format: user@host
1205 Note: ssh authentication must be paswordless
Benjamin Copeland15d743e2021-02-22 08:35:10 +00001206 """
1207 ),
1208 )
1209 parser.add_argument(
1210 "-s",
1211 "--skip_install",
1212 dest="skip_install",
1213 default=False,
1214 action="store_true",
1215 help="skip install section defined in test definition.",
1216 )
1217 parser.add_argument(
1218 "-e",
1219 "--skip_environment",
1220 dest="skip_environment",
1221 default=False,
1222 action="store_true",
1223 help="skip environmental data collection (board name, distro, etc)",
1224 )
1225 parser.add_argument(
1226 "-l",
1227 "--lava_run",
1228 dest="lava_run",
1229 default=False,
1230 action="store_true",
1231 help="send test result to LAVA with lava-test-case.",
1232 )
1233 parser.add_argument(
1234 "-O",
1235 "--overlay",
1236 default=None,
1237 dest="overlay",
1238 help=textwrap.dedent(
1239 """\
Thomas Böhler4b0ab502024-06-12 15:44:30 +02001240 Specify test plan overlay file to:
Chase Qia158efe2017-11-17 12:35:11 +08001241 * skip tests
1242 * amend test parameters
1243 * add new tests
Benjamin Copeland15d743e2021-02-22 08:35:10 +00001244 """
1245 ),
1246 )
1247 parser.add_argument(
1248 "-v",
1249 "--verbose",
1250 action="store_true",
1251 dest="verbose",
1252 default=False,
1253 help="Set log level to DEBUG.",
1254 )
Vishal Bhoje94da4a2020-05-15 12:22:40 +05301255 parser.add_argument(
1256 "--qa-reports-server",
1257 dest="qa_reports_server",
1258 default=None,
1259 help="qa reports server where the results have to be sent",
1260 )
1261 parser.add_argument(
1262 "--qa-reports-token",
1263 dest="qa_reports_token",
1264 default=None,
1265 help="qa reports token to upload the results to qa_reports_server",
1266 )
1267 parser.add_argument(
1268 "--qa-reports-project",
1269 dest="qa_reports_project",
1270 default=None,
1271 help="qa reports projects to which the results have to be uploaded",
1272 )
1273 parser.add_argument(
1274 "--qa-reports-group",
1275 dest="qa_reports_group",
1276 default=None,
1277 help="qa reports group in which the results have to be stored",
1278 )
1279 parser.add_argument(
1280 "--qa-reports-env",
1281 dest="qa_reports_env",
1282 default=None,
1283 help="qa reports environment for the results that have to be stored",
1284 )
1285 parser.add_argument(
1286 "--qa-reports-build-version",
1287 dest="qa_reports_build_version",
1288 default=None,
1289 help="qa reports build id for the result set",
1290 )
Milosz Wasilewskib4b40de2020-06-11 12:54:18 +01001291 parser.add_argument(
1292 "--qa-reports-disable-metadata",
1293 dest="qa_reports_disable_metadata",
1294 default=False,
Benjamin Copeland15d743e2021-02-22 08:35:10 +00001295 action="store_true",
Milosz Wasilewski37848d32020-06-11 14:33:43 +01001296 help="Disable sending metadata to SQUAD. Default: false",
1297 )
1298 parser.add_argument(
1299 "--qa-reports-metadata",
1300 dest="qa_reports_metadata",
1301 default={},
1302 action=StoreDictKeyPair,
1303 nargs="+",
1304 metavar="KEY=VALUE",
1305 help="List of metadata key=value pairs to be sent to SQUAD",
Milosz Wasilewskib4b40de2020-06-11 12:54:18 +01001306 )
Milosz Wasilewskiabe2dcc2020-06-12 10:54:46 +01001307 parser.add_argument(
1308 "--qa-reports-metadata-file",
1309 dest="qa_reports_metadata_file",
1310 default=None,
1311 help="YAML file that defines metadata to be reported to SQUAD",
1312 )
Milosz Wasilewskiba21eeb2022-02-15 10:23:11 +00001313 parser.add_argument(
1314 "--cleanup",
1315 dest="cleanup",
1316 default=False,
1317 action="store_true",
Thomas Böhler56ff5852024-06-12 15:44:51 +02001318 help=textwrap.dedent(
1319 """\
1320 If set to true, test-runner will remove all temporary files after
1321 running the test. It includes all collected logs and test results. This
1322 option should only be used if uploading results to SQUAD or LAVA.
1323 Default: false
1324 """
1325 ),
Milosz Wasilewskiba21eeb2022-02-15 10:23:11 +00001326 )
Milosz Wasilewskib4b40de2020-06-11 12:54:18 +01001327
Chase Qi09edc7f2016-08-18 13:18:50 +08001328 args = parser.parse_args()
1329 return args
1330
1331
1332def main():
Chase Qi0e9b36e2017-12-07 16:13:44 +08001333 args = get_args()
1334
Chase Qi09edc7f2016-08-18 13:18:50 +08001335 # Setup logger.
Benjamin Copeland15d743e2021-02-22 08:35:10 +00001336 logger = logging.getLogger("RUNNER")
Chase Qi0e9b36e2017-12-07 16:13:44 +08001337 logger.setLevel(logging.INFO)
1338 if args.verbose:
1339 logger.setLevel(logging.DEBUG)
Chase Qi09edc7f2016-08-18 13:18:50 +08001340 ch = logging.StreamHandler()
1341 ch.setLevel(logging.DEBUG)
Benjamin Copeland15d743e2021-02-22 08:35:10 +00001342 formatter = logging.Formatter("%(asctime)s - %(name)s: %(levelname)s: %(message)s")
Chase Qi09edc7f2016-08-18 13:18:50 +08001343 ch.setFormatter(formatter)
1344 logger.addHandler(ch)
1345
Benjamin Copeland15d743e2021-02-22 08:35:10 +00001346 logger.debug("Test job arguments: %s" % args)
Milosz Wasilewski682120e2017-03-13 13:37:18 +00001347 if args.kind != "manual" and args.target is None:
Milosz Wasilewski2fea70e2016-11-11 12:16:09 +00001348 if os.geteuid() != 0:
1349 logger.error("Sorry, you need to run this as root")
1350 sys.exit(1)
Chase Qi09edc7f2016-08-18 13:18:50 +08001351
Milosz Wasilewski682120e2017-03-13 13:37:18 +00001352 # Validate target argument format and connectivity.
1353 if args.target:
Benjamin Copeland15d743e2021-02-22 08:35:10 +00001354 rex = re.compile(".+@.+")
Milosz Wasilewski682120e2017-03-13 13:37:18 +00001355 if not rex.match(args.target):
Benjamin Copeland15d743e2021-02-22 08:35:10 +00001356 logger.error("Usage: -g username@host")
Milosz Wasilewski682120e2017-03-13 13:37:18 +00001357 sys.exit(1)
Benjamin Copeland15d743e2021-02-22 08:35:10 +00001358 if pexpect.which("ssh") is None:
1359 logger.error("openssh client must be installed on the host.")
Milosz Wasilewski682120e2017-03-13 13:37:18 +00001360 sys.exit(1)
1361 try:
Dan Ruea9eb01c2017-06-07 16:29:09 -05001362 run_command("exit", args.target)
Milosz Wasilewski682120e2017-03-13 13:37:18 +00001363 except subprocess.CalledProcessError as e:
Benjamin Copeland15d743e2021-02-22 08:35:10 +00001364 logger.error("ssh login failed.")
Milosz Wasilewski682120e2017-03-13 13:37:18 +00001365 print(e)
1366 sys.exit(1)
1367
Chase Qi09edc7f2016-08-18 13:18:50 +08001368 # Generate test plan.
Chase Qi09edc7f2016-08-18 13:18:50 +08001369 test_plan = TestPlan(args)
Milosz Wasilewski2fea70e2016-11-11 12:16:09 +00001370 test_list = test_plan.test_list(args.kind)
Benjamin Copeland15d743e2021-02-22 08:35:10 +00001371 logger.info("Tests to run:")
Chase Qi09edc7f2016-08-18 13:18:50 +08001372 for test in test_list:
1373 print(test)
1374
1375 # Run tests.
1376 for test in test_list:
Milosz Wasilewski682120e2017-03-13 13:37:18 +00001377 # Set and save test params to test dictionary.
Benjamin Copeland15d743e2021-02-22 08:35:10 +00001378 test["test_name"] = os.path.splitext(test["path"].split("/")[-1])[0]
1379 test["test_uuid"] = "%s_%s" % (test["test_name"], test["uuid"])
1380 test["output"] = os.path.realpath(args.output)
1381 if args.target is not None and "-o" not in sys.argv:
1382 test["output"] = os.path.join(test["output"], args.target)
1383 test["test_path"] = os.path.join(test["output"], test["test_uuid"])
Milosz Wasilewski682120e2017-03-13 13:37:18 +00001384 if args.target is not None:
Chase Qi204b5422017-04-06 11:01:58 +08001385 # Get relative directory path of yaml file for partial file copy.
1386 # '-d' takes any relative paths to the yaml file, so get the realpath first.
Benjamin Copeland15d743e2021-02-22 08:35:10 +00001387 tc_realpath = os.path.realpath(test["path"])
Chase Qi204b5422017-04-06 11:01:58 +08001388 tc_dirname = os.path.dirname(tc_realpath)
Benjamin Copeland15d743e2021-02-22 08:35:10 +00001389 test["tc_relative_dir"] = "%s%s" % (
1390 args.kind,
1391 tc_dirname.split(args.kind)[1],
1392 )
Dan Ruea9eb01c2017-06-07 16:29:09 -05001393 target_user_home = run_command("echo $HOME", args.target)
Benjamin Copeland15d743e2021-02-22 08:35:10 +00001394 test["target_test_path"] = "%s/output/%s" % (
1395 target_user_home,
1396 test["test_uuid"],
1397 )
1398 logger.debug("Test parameters: %s" % test)
Milosz Wasilewski682120e2017-03-13 13:37:18 +00001399
Chase Qi09edc7f2016-08-18 13:18:50 +08001400 # Create directories and copy files needed.
1401 setup = TestSetup(test, args)
1402 setup.create_dir()
1403 setup.copy_test_repo()
Milosz Wasilewski970431b2016-11-25 14:10:08 +00001404 setup.checkout_version()
Chase Qi09edc7f2016-08-18 13:18:50 +08001405 setup.create_uuid_file()
1406
1407 # Convert test definition.
1408 test_def = TestDefinition(test, args)
Milosz Wasilewski2fea70e2016-11-11 12:16:09 +00001409 if test_def.exists:
1410 test_def.definition()
1411 test_def.metadata()
Nicolas Dechesne51b85a82017-02-04 00:45:48 +01001412 test_def.mkrun()
Chase Qi09edc7f2016-08-18 13:18:50 +08001413
Milosz Wasilewski2fea70e2016-11-11 12:16:09 +00001414 # Run test.
Nicolas Dechesne51b85a82017-02-04 00:45:48 +01001415 test_def.run()
Chase Qi09edc7f2016-08-18 13:18:50 +08001416
Milosz Wasilewski2fea70e2016-11-11 12:16:09 +00001417 # Parse test output, save results in json and csv format.
1418 result_parser = ResultParser(test, args)
1419 result_parser.run()
Milosz Wasilewskiba21eeb2022-02-15 10:23:11 +00001420 if args.cleanup:
1421 # remove a copy of test-definitions
1422 logger.warning("Removing a copy of test-definitions")
1423 logger.warning("Removing all collected logs")
1424 shutil.rmtree(test["test_path"])
Milosz Wasilewski2fea70e2016-11-11 12:16:09 +00001425 else:
Benjamin Copeland15d743e2021-02-22 08:35:10 +00001426 logger.warning("Requested test definition %s doesn't exist" % test["path"])
Chase Qi09edc7f2016-08-18 13:18:50 +08001427
Dan Ruea9eb01c2017-06-07 16:29:09 -05001428
Chase Qi09edc7f2016-08-18 13:18:50 +08001429if __name__ == "__main__":
1430 main()