blob: f2880e7a08e5b24bc02ce624116c1ae78d2a0a84 [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:
96 data = yaml.load(f)
97
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"]
117 and test["repository"] == skip_test["repository"]
118 ):
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",
682 "packages": [
683 "GeoIP-1.5.0-11.el7",
684 "NetworkManager-1.4.0-20.el7_3",
685 ...
686 "yum-plugin-fastestmirror-1.1.31-40.el7",
687 "zlib-1.2.7-17.el7"
688 ],
689 "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"
690 }
Dan Ruea9eb01c2017-06-07 16:29:09 -0500691 """
692
693 environment = {}
694 if skip_collection:
695 return environment
Milosz Wasilewskidf71a762017-07-20 13:26:21 +0100696 try:
Benjamin Copeland15d743e2021-02-22 08:35:10 +0000697 environment["linux_distribution"] = (
698 run_command("grep ^ID= /etc/os-release", target)
699 .split("=")[-1]
700 .strip('"')
701 .lower()
702 )
Milosz Wasilewskidf71a762017-07-20 13:26:21 +0100703 except subprocess.CalledProcessError:
Benjamin Copeland15d743e2021-02-22 08:35:10 +0000704 environment["linux_distribution"] = ""
Milosz Wasilewskidf71a762017-07-20 13:26:21 +0100705
706 try:
Benjamin Copeland15d743e2021-02-22 08:35:10 +0000707 environment["kernel"] = run_command("uname -r", target)
Milosz Wasilewskidf71a762017-07-20 13:26:21 +0100708 except subprocess.CalledProcessError:
Benjamin Copeland15d743e2021-02-22 08:35:10 +0000709 environment["kernel"] = ""
Milosz Wasilewskidf71a762017-07-20 13:26:21 +0100710
711 try:
Benjamin Copeland15d743e2021-02-22 08:35:10 +0000712 environment["uname"] = run_command("uname -a", target)
Milosz Wasilewskidf71a762017-07-20 13:26:21 +0100713 except subprocess.CalledProcessError:
Benjamin Copeland15d743e2021-02-22 08:35:10 +0000714 environment["uname"] = ""
Dan Ruea9eb01c2017-06-07 16:29:09 -0500715
716 try:
Benjamin Copeland15d743e2021-02-22 08:35:10 +0000717 environment["bios_version"] = run_command(
718 "cat /sys/devices/virtual/dmi/id/bios_version", target
719 )
Dan Ruea9eb01c2017-06-07 16:29:09 -0500720 except subprocess.CalledProcessError:
Benjamin Copeland15d743e2021-02-22 08:35:10 +0000721 environment["bios_version"] = ""
Dan Ruea9eb01c2017-06-07 16:29:09 -0500722
723 try:
Benjamin Copeland15d743e2021-02-22 08:35:10 +0000724 environment["board_vendor"] = run_command(
725 "cat /sys/devices/virtual/dmi/id/board_vendor", target
726 )
Dan Ruea9eb01c2017-06-07 16:29:09 -0500727 except subprocess.CalledProcessError:
Benjamin Copeland15d743e2021-02-22 08:35:10 +0000728 environment["board_vendor"] = ""
Dan Ruea9eb01c2017-06-07 16:29:09 -0500729
730 try:
Benjamin Copeland15d743e2021-02-22 08:35:10 +0000731 environment["board_name"] = run_command(
732 "cat /sys/devices/virtual/dmi/id/board_name", target
733 )
Dan Ruea9eb01c2017-06-07 16:29:09 -0500734 except subprocess.CalledProcessError:
Benjamin Copeland15d743e2021-02-22 08:35:10 +0000735 environment["board_name"] = ""
Dan Ruea9eb01c2017-06-07 16:29:09 -0500736
Milosz Wasilewskidf71a762017-07-20 13:26:21 +0100737 try:
Benjamin Copeland15d743e2021-02-22 08:35:10 +0000738 environment["packages"] = get_packages(
739 environment["linux_distribution"], target
740 )
Milosz Wasilewskidf71a762017-07-20 13:26:21 +0100741 except subprocess.CalledProcessError:
Benjamin Copeland15d743e2021-02-22 08:35:10 +0000742 environment["packages"] = []
Dan Ruea9eb01c2017-06-07 16:29:09 -0500743 return environment
744
745
Chase Qi09edc7f2016-08-18 13:18:50 +0800746class ResultParser(object):
747 def __init__(self, test, args):
Milosz Wasilewski682120e2017-03-13 13:37:18 +0000748 self.test = test
749 self.args = args
Chase Qi09edc7f2016-08-18 13:18:50 +0800750 self.metrics = []
751 self.results = {}
Benjamin Copeland15d743e2021-02-22 08:35:10 +0000752 self.results["test"] = test["test_name"]
753 self.results["id"] = test["test_uuid"]
754 self.results["test_plan"] = args.test_plan
755 self.results["environment"] = get_environment(
756 target=self.args.target, skip_collection=self.args.skip_environment
757 )
758 self.logger = logging.getLogger("RUNNER.ResultParser")
759 self.results["params"] = {}
Chase Qie94ba522017-05-26 12:05:18 +0800760 self.pattern = None
761 self.fixup = None
Vishal Bhoje94da4a2020-05-15 12:22:40 +0530762 self.qa_reports_server = args.qa_reports_server
763 if args.qa_reports_token is not None:
764 self.qa_reports_token = args.qa_reports_token
765 else:
Benjamin Copeland15d743e2021-02-22 08:35:10 +0000766 self.qa_reports_token = os.environ.get(
767 "QA_REPORTS_TOKEN", get_token_from_netrc(self.qa_reports_server)
768 )
Vishal Bhoje94da4a2020-05-15 12:22:40 +0530769 self.qa_reports_project = args.qa_reports_project
770 self.qa_reports_group = args.qa_reports_group
771 self.qa_reports_env = args.qa_reports_env
772 self.qa_reports_build_version = args.qa_reports_build_version
Milosz Wasilewskib4b40de2020-06-11 12:54:18 +0100773 self.qa_reports_disable_metadata = args.qa_reports_disable_metadata
Milosz Wasilewski37848d32020-06-11 14:33:43 +0100774 self.qa_reports_metadata = args.qa_reports_metadata
Milosz Wasilewskiabe2dcc2020-06-12 10:54:46 +0100775 self.qa_reports_metadata_file = args.qa_reports_metadata_file
Vishal Bhoje94da4a2020-05-15 12:22:40 +0530776
Benjamin Copeland15d743e2021-02-22 08:35:10 +0000777 with open(os.path.join(self.test["test_path"], "testdef.yaml"), "r") as f:
Milosz Wasilewskia76e8dd2016-11-25 14:13:25 +0000778 self.testdef = yaml.safe_load(f)
Benjamin Copeland15d743e2021-02-22 08:35:10 +0000779 self.results["name"] = ""
780 if (
781 "metadata" in self.testdef.keys()
782 and "name" in self.testdef["metadata"].keys()
783 ):
784 self.results["name"] = self.testdef["metadata"]["name"]
785 if "params" in self.testdef.keys():
786 self.results["params"] = self.testdef["params"]
Nicolas Dechesnefaa343d2017-10-23 00:33:10 +0200787 if self.args.test_def_params:
788 for param_name, param_value in self.args.test_def_params.items():
Benjamin Copeland15d743e2021-02-22 08:35:10 +0000789 self.results["params"][param_name] = param_value
790 if (
791 "parse" in self.testdef.keys()
792 and "pattern" in self.testdef["parse"].keys()
793 ):
794 self.pattern = self.testdef["parse"]["pattern"]
Chase Qie94ba522017-05-26 12:05:18 +0800795 self.logger.info("Enabling log parse pattern: %s" % self.pattern)
Benjamin Copeland15d743e2021-02-22 08:35:10 +0000796 if "fixupdict" in self.testdef["parse"].keys():
797 self.fixup = self.testdef["parse"]["fixupdict"]
798 self.logger.info(
799 "Enabling log parse pattern fixup: %s" % self.fixup
800 )
801 if "parameters" in test.keys():
802 self.results["params"].update(test["parameters"])
803 if "params" in test.keys():
804 self.results["params"].update(test["params"])
805 if "version" in test.keys():
806 self.results["version"] = test["version"]
Milosz Wasilewski970431b2016-11-25 14:10:08 +0000807 else:
808 path = os.getcwd()
Benjamin Copeland15d743e2021-02-22 08:35:10 +0000809 os.chdir(self.test["test_path"])
Chase Qi3efa7692017-06-26 15:54:05 +0800810 if sys.version_info[0] < 3:
811 test_version = subprocess.check_output("git rev-parse HEAD", shell=True)
812 else:
Benjamin Copeland15d743e2021-02-22 08:35:10 +0000813 test_version = subprocess.check_output(
814 "git rev-parse HEAD", shell=True
815 ).decode("utf-8")
816 self.results["version"] = test_version.rstrip()
Milosz Wasilewski970431b2016-11-25 14:10:08 +0000817 os.chdir(path)
Chase Qiea543352017-09-21 16:44:30 +0800818 self.lava_run = args.lava_run
Benjamin Copeland15d743e2021-02-22 08:35:10 +0000819 if self.lava_run and not find_executable("lava-test-case"):
820 self.logger.info(
821 "lava-test-case not found, '-l' or '--lava_run' option ignored'"
822 )
Chase Qiea543352017-09-21 16:44:30 +0800823 self.lava_run = False
Chase Qi09edc7f2016-08-18 13:18:50 +0800824
825 def run(self):
826 self.parse_stdout()
Chase Qie94ba522017-05-26 12:05:18 +0800827 if self.pattern:
828 self.parse_pattern()
829 # If 'metrics' is empty, add 'no-result-found fail'.
830 if not self.metrics:
Benjamin Copeland15d743e2021-02-22 08:35:10 +0000831 self.metrics = [
832 {
833 "test_case_id": "no-result-found",
834 "result": "fail",
835 "measurement": "",
836 "units": "",
837 }
838 ]
839 self.results["metrics"] = self.metrics
Chase Qi09edc7f2016-08-18 13:18:50 +0800840 self.dict_to_json()
841 self.dict_to_csv()
Vishal Bhoje94da4a2020-05-15 12:22:40 +0530842 self.send_to_qa_reports()
Milosz Wasilewski31ac9852021-11-18 11:21:28 +0000843 self.send_to_fiotest()
Benjamin Copeland15d743e2021-02-22 08:35:10 +0000844 self.logger.info("Result files saved to: %s" % self.test["test_path"])
845 print("--- Printing result.csv ---")
846 with open("%s/result.csv" % self.test["test_path"]) as f:
Chase Qi09edc7f2016-08-18 13:18:50 +0800847 print(f.read())
848
849 def parse_stdout(self):
Benjamin Copeland15d743e2021-02-22 08:35:10 +0000850 with open("%s/stdout.log" % self.test["test_path"], "r") as f:
Milosz Wasilewski5a4dd442016-12-07 15:01:57 +0000851 test_case_re = re.compile("TEST_CASE_ID=(.*)")
852 result_re = re.compile("RESULT=(.*)")
853 measurement_re = re.compile("MEASUREMENT=(.*)")
854 units_re = re.compile("UNITS=(.*)")
Chase Qi09edc7f2016-08-18 13:18:50 +0800855 for line in f:
Benjamin Copeland15d743e2021-02-22 08:35:10 +0000856 if re.match(r"\<(|LAVA_SIGNAL_TESTCASE )TEST_CASE_ID=.*", line):
857 line = line.strip("\n").strip("\r").strip("<>").split(" ")
858 data = {
859 "test_case_id": "",
860 "result": "",
861 "measurement": "",
862 "units": "",
863 }
Chase Qi09edc7f2016-08-18 13:18:50 +0800864
865 for string in line:
Milosz Wasilewskiff695622016-12-05 16:00:06 +0000866 test_case_match = test_case_re.match(string)
867 result_match = result_re.match(string)
868 measurement_match = measurement_re.match(string)
869 units_match = units_re.match(string)
870 if test_case_match:
Benjamin Copeland15d743e2021-02-22 08:35:10 +0000871 data["test_case_id"] = test_case_match.group(1)
Milosz Wasilewskiff695622016-12-05 16:00:06 +0000872 if result_match:
Benjamin Copeland15d743e2021-02-22 08:35:10 +0000873 data["result"] = result_match.group(1)
Milosz Wasilewskiff695622016-12-05 16:00:06 +0000874 if measurement_match:
Nicolas Dechesne508a2272020-10-03 11:10:37 +0200875 try:
Benjamin Copeland15d743e2021-02-22 08:35:10 +0000876 data["measurement"] = float(measurement_match.group(1))
Nicolas Dechesne508a2272020-10-03 11:10:37 +0200877 except ValueError as e:
878 pass
Milosz Wasilewskiff695622016-12-05 16:00:06 +0000879 if units_match:
Benjamin Copeland15d743e2021-02-22 08:35:10 +0000880 data["units"] = units_match.group(1)
Chase Qi09edc7f2016-08-18 13:18:50 +0800881
882 self.metrics.append(data.copy())
883
Chase Qiea543352017-09-21 16:44:30 +0800884 if self.lava_run:
885 self.send_to_lava(data)
886
Chase Qie94ba522017-05-26 12:05:18 +0800887 def parse_pattern(self):
Benjamin Copeland15d743e2021-02-22 08:35:10 +0000888 with open("%s/stdout.log" % self.test["test_path"], "r") as f:
889 rex_pattern = re.compile(r"%s" % self.pattern)
Chase Qie94ba522017-05-26 12:05:18 +0800890 for line in f:
891 data = {}
Aníbal Limónc36cb792017-11-29 11:54:30 -0600892 m = rex_pattern.search(line)
Chase Qie94ba522017-05-26 12:05:18 +0800893 if m:
894 data = m.groupdict()
Benjamin Copeland15d743e2021-02-22 08:35:10 +0000895 for x in ["measurement", "units"]:
Chase Qie94ba522017-05-26 12:05:18 +0800896 if x not in data:
Benjamin Copeland15d743e2021-02-22 08:35:10 +0000897 data[x] = ""
898 if self.fixup and data["result"] in self.fixup:
899 data["result"] = self.fixup[data["result"]]
Chase Qi1f2a9a02017-03-09 15:45:04 +0100900
Chase Qie94ba522017-05-26 12:05:18 +0800901 self.metrics.append(data.copy())
Chase Qi09edc7f2016-08-18 13:18:50 +0800902
Chase Qiea543352017-09-21 16:44:30 +0800903 if self.lava_run:
904 self.send_to_lava(data)
905
906 def send_to_lava(self, data):
Benjamin Copeland15d743e2021-02-22 08:35:10 +0000907 cmd = "lava-test-case {} --result {}".format(
908 data["test_case_id"], data["result"]
909 )
910 if data["measurement"]:
911 cmd = "{} --measurement {} --units {}".format(
912 cmd, data["measurement"], data["units"]
913 )
914 self.logger.debug("lava-run: cmd: {}".format(cmd))
Chase Qiea543352017-09-21 16:44:30 +0800915 subprocess.call(shlex.split(cmd))
916
Milosz Wasilewski31ac9852021-11-18 11:21:28 +0000917 def send_to_fiotest(self):
918 """
919 This method saves results as filesystem tree. This is required by
920 fiotest: https://github.com/foundriesio/fiotest/
921 """
922 # check if TEST_DIR variable is set
923 test_path = os.environ.get("TEST_DIR")
924 if not test_path:
Milosz Wasilewskid92aff12021-11-18 11:21:28 +0000925 self.logger.debug("TEST_DIR is not set")
926 self.logger.debug("NOT reporting result to fiotest")
Milosz Wasilewski31ac9852021-11-18 11:21:28 +0000927 return
928 # create directory with test name
929 try:
930 for metric in self.metrics:
931 local_ts = datetime.now()
932 dir_name = "{}-{}".format(local_ts.timestamp(), metric["test_case_id"])
933 os.makedirs(os.path.join(test_path, dir_name), exist_ok=True)
934 if metric["measurement"] != "":
935 metrics_dir = os.path.join(test_path, dir_name, "metrics")
936 os.makedirs(metrics_dir, exist_ok=True)
937 with open(os.path.join(metrics_dir, "value"), "w") as value_file:
938 value_file.write(metric["measurement"])
939 else:
940 if metric["result"] == "fail":
941 os.makedirs(
942 os.path.join(test_path, dir_name, "failed"), exist_ok=True
943 )
944 if metric["result"] == "skip":
945 os.makedirs(
946 os.path.join(test_path, dir_name, "skipped"), exist_ok=True
947 )
948 except PermissionError:
949 self.logger.error(
950 "Unable to prepare fiotest results due to lack of permissions"
951 )
952
Vishal Bhoje94da4a2020-05-15 12:22:40 +0530953 def send_to_qa_reports(self):
Benjamin Copeland15d743e2021-02-22 08:35:10 +0000954 if None in (
955 self.qa_reports_server,
956 self.qa_reports_token,
957 self.qa_reports_group,
958 self.qa_reports_project,
959 self.qa_reports_build_version,
960 self.qa_reports_env,
961 ):
962 self.logger.warning(
963 "All parameters for qa reports are not set, results will not be pushed to qa reports"
964 )
Vishal Bhoje94da4a2020-05-15 12:22:40 +0530965 return
966
Benjamin Copeland15d743e2021-02-22 08:35:10 +0000967 SquadApi.configure(url=self.qa_reports_server, token=self.qa_reports_token)
Vishal Bhoje94da4a2020-05-15 12:22:40 +0530968 tests = {}
969 metrics = {}
970 for metric in self.metrics:
Benjamin Copeland15d743e2021-02-22 08:35:10 +0000971 if metric["measurement"] != "":
972 metrics[
973 "{}/{}".format(self.test["test_name"], metric["test_case_id"])
974 ] = metric["measurement"]
Vishal Bhoje94da4a2020-05-15 12:22:40 +0530975 else:
Benjamin Copeland15d743e2021-02-22 08:35:10 +0000976 tests[
977 "{}/{}".format(self.test["test_name"], metric["test_case_id"])
978 ] = metric["result"]
Vishal Bhoje94da4a2020-05-15 12:22:40 +0530979
Benjamin Copeland15d743e2021-02-22 08:35:10 +0000980 with open("{}/stdout.log".format(self.test["test_path"]), "r") as logfile:
Vishal Bhoje94da4a2020-05-15 12:22:40 +0530981 log = logfile.read()
982
Milosz Wasilewskiabe2dcc2020-06-12 10:54:46 +0100983 metadata = {}
Milosz Wasilewskib00ceff2020-06-15 13:48:01 +0100984 if not self.qa_reports_disable_metadata:
985 if self.qa_reports_metadata:
986 metadata.update(self.qa_reports_metadata)
987 if self.qa_reports_metadata_file:
988 try:
989 with open(self.qa_reports_metadata_file, "r") as metadata_file:
Benjamin Copeland15d743e2021-02-22 08:35:10 +0000990 loaded_metadata = yaml.load(
991 metadata_file, Loader=yaml.SafeLoader
992 )
Milosz Wasilewskib00ceff2020-06-15 13:48:01 +0100993 # check if loaded metadata is key=value and both are strings
994 for key, value in loaded_metadata.items():
995 if type(key) == str and type(value) == str:
996 # only update metadata with simple keys
997 # ignore all other items in the dictionary
998 metadata.update({key: value})
999 else:
1000 self.logger.warning("Ignoring key: %s" % key)
1001 except FileNotFoundError:
1002 self.logger.warning("Metadata file not found")
1003 except PermissionError:
Benjamin Copeland15d743e2021-02-22 08:35:10 +00001004 self.logger.warning(
1005 "Insufficient permissions to open metadata file"
1006 )
Milosz Wasilewskif883f102020-06-11 12:46:58 +01001007 if submit_results(
Benjamin Copeland15d743e2021-02-22 08:35:10 +00001008 group_project_slug="{}/{}".format(
1009 self.qa_reports_group, self.qa_reports_project
1010 ),
1011 build_version=self.qa_reports_build_version,
1012 env_slug=self.qa_reports_env,
1013 tests=tests,
1014 metrics=metrics,
1015 log=log,
1016 metadata=metadata,
1017 attachments=None,
1018 ):
Milosz Wasilewskif883f102020-06-11 12:46:58 +01001019 self.logger.info("Results pushed to QA Reports")
1020 else:
1021 self.logger.warning("Results upload to QA Reports failed!")
Vishal Bhoje94da4a2020-05-15 12:22:40 +05301022
Chase Qi09edc7f2016-08-18 13:18:50 +08001023 def dict_to_json(self):
Chase Qi87f4f402016-11-07 15:32:01 +08001024 # Save test results to output/test_id/result.json
Benjamin Copeland15d743e2021-02-22 08:35:10 +00001025 with open("%s/result.json" % self.test["test_path"], "w") as f:
Chase Qi87f4f402016-11-07 15:32:01 +08001026 json.dump([self.results], f, indent=4)
1027
Milosz Wasilewskiba21eeb2022-02-15 10:23:11 +00001028 if not self.args.cleanup:
1029 # Collect test results of all tests in output/result.json
1030 feeds = []
1031 if os.path.isfile("%s/result.json" % self.test["output"]):
1032 with open("%s/result.json" % self.test["output"], "r") as f:
1033 feeds = json.load(f)
Chase Qi87f4f402016-11-07 15:32:01 +08001034
Milosz Wasilewskiba21eeb2022-02-15 10:23:11 +00001035 feeds.append(self.results)
1036 with open("%s/result.json" % self.test["output"], "w") as f:
1037 json.dump(feeds, f, indent=4)
Chase Qi09edc7f2016-08-18 13:18:50 +08001038
1039 def dict_to_csv(self):
Chase Qica15cf52016-11-10 17:00:22 +08001040 # Convert dict self.results['params'] to a string.
Benjamin Copeland15d743e2021-02-22 08:35:10 +00001041 test_params = ""
1042 if self.results["params"]:
1043 params_dict = self.results["params"]
1044 test_params = ";".join(["%s=%s" % (k, v) for k, v in params_dict.items()])
Chase Qi09edc7f2016-08-18 13:18:50 +08001045
Benjamin Copeland15d743e2021-02-22 08:35:10 +00001046 for metric in self.results["metrics"]:
1047 metric["name"] = self.results["name"]
1048 metric["test_params"] = test_params
Chase Qica15cf52016-11-10 17:00:22 +08001049
1050 # Save test results to output/test_id/result.csv
Benjamin Copeland15d743e2021-02-22 08:35:10 +00001051 fieldnames = [
1052 "name",
1053 "test_case_id",
1054 "result",
1055 "measurement",
1056 "units",
1057 "test_params",
1058 ]
1059 with open("%s/result.csv" % self.test["test_path"], "w") as f:
Chase Qica15cf52016-11-10 17:00:22 +08001060 writer = csv.DictWriter(f, fieldnames=fieldnames)
Chase Qi09edc7f2016-08-18 13:18:50 +08001061 writer.writeheader()
Benjamin Copeland15d743e2021-02-22 08:35:10 +00001062 for metric in self.results["metrics"]:
Chase Qi09edc7f2016-08-18 13:18:50 +08001063 writer.writerow(metric)
1064
Milosz Wasilewskiba21eeb2022-02-15 10:23:11 +00001065 if not self.args.cleanup:
1066 # Collect test results of all tests in output/result.csv
1067 if not os.path.isfile("%s/result.csv" % self.test["output"]):
1068 with open("%s/result.csv" % self.test["output"], "w") as f:
1069 writer = csv.DictWriter(f, fieldnames=fieldnames)
1070 writer.writeheader()
Chase Qi87f4f402016-11-07 15:32:01 +08001071
Milosz Wasilewskiba21eeb2022-02-15 10:23:11 +00001072 with open("%s/result.csv" % self.test["output"], "a") as f:
1073 writer = csv.DictWriter(f, fieldnames=fieldnames)
1074 for metric in self.results["metrics"]:
1075 writer.writerow(metric)
Chase Qi09edc7f2016-08-18 13:18:50 +08001076
1077
Vishal Bhoje94da4a2020-05-15 12:22:40 +05301078def get_token_from_netrc(qa_reports_server):
1079 if qa_reports_server is None:
1080 return
1081 parse = urlparse(qa_reports_server)
1082 netrc_local = netrc.netrc()
1083 authTokens = netrc_local.authenticators("{}".format(parse.netloc))
1084 if authTokens is not None:
1085 hostname, username, authToken = authTokens
1086 return authToken
1087 # Unable to find Token hence returning None
1088 return
1089
1090
Chase Qi09edc7f2016-08-18 13:18:50 +08001091def get_args():
Milosz Wasilewski259ba192017-07-27 10:59:25 +01001092 parser = argparse.ArgumentParser(formatter_class=argparse.RawTextHelpFormatter)
Benjamin Copeland15d743e2021-02-22 08:35:10 +00001093 parser.add_argument(
1094 "-o",
1095 "--output",
1096 default=os.getenv("HOME", "") + "/output",
1097 dest="output",
1098 help=textwrap.dedent(
1099 """\
Chase Qi09edc7f2016-08-18 13:18:50 +08001100 specify a directory to store test and result files.
Nicolas Dechesnef6c4c212017-01-18 17:30:04 +01001101 Default: $HOME/output
Benjamin Copeland15d743e2021-02-22 08:35:10 +00001102 """
1103 ),
1104 )
1105 parser.add_argument(
1106 "-p",
1107 "--test_plan",
1108 default=None,
1109 dest="test_plan",
1110 help=textwrap.dedent(
1111 """\
Chase Qi09edc7f2016-08-18 13:18:50 +08001112 specify an test plan file which has tests and related
1113 params listed in yaml format.
Benjamin Copeland15d743e2021-02-22 08:35:10 +00001114 """
1115 ),
1116 )
1117 parser.add_argument(
1118 "-d",
1119 "--test_def",
1120 default=None,
1121 dest="test_def",
1122 help=textwrap.dedent(
1123 """\
Chase Qi09edc7f2016-08-18 13:18:50 +08001124 base on test definition repo location, specify relative
1125 path to the test definition to run.
1126 Format example: "ubuntu/smoke-tests-basic.yaml"
Benjamin Copeland15d743e2021-02-22 08:35:10 +00001127 """
1128 ),
1129 )
1130 parser.add_argument(
1131 "-r",
1132 "--test_def_params",
1133 default={},
1134 dest="test_def_params",
1135 action=StoreDictKeyPair,
1136 nargs="+",
1137 metavar="KEY=VALUE",
1138 help=textwrap.dedent(
1139 """\
Milosz Wasilewski259ba192017-07-27 10:59:25 +01001140 Set additional parameters when using test definition without
1141 a test plan. The name values are set similarily to environment
1142 variables:
1143 --test_def_params KEY1=VALUE1 KEY2=VALUE2 ...
Benjamin Copeland15d743e2021-02-22 08:35:10 +00001144 """
1145 ),
1146 )
1147 parser.add_argument(
1148 "-k",
1149 "--kind",
1150 default="automated",
1151 dest="kind",
1152 choices=["automated", "manual"],
1153 help=textwrap.dedent(
1154 """\
Milosz Wasilewski2fea70e2016-11-11 12:16:09 +00001155 Selects type of tests to be executed from the test plan.
1156 Possible options: automated, manual
Benjamin Copeland15d743e2021-02-22 08:35:10 +00001157 """
1158 ),
1159 )
1160 parser.add_argument(
1161 "-t",
1162 "--timeout",
1163 type=int,
1164 default=None,
1165 dest="timeout",
1166 help="Specify test timeout",
1167 )
1168 parser.add_argument(
1169 "-g",
1170 "--target",
1171 default=None,
1172 dest="target",
1173 help=textwrap.dedent(
1174 """\
Milosz Wasilewski682120e2017-03-13 13:37:18 +00001175 Specify SSH target to execute tests.
1176 Format: user@host
1177 Note: ssh authentication must be paswordless
Benjamin Copeland15d743e2021-02-22 08:35:10 +00001178 """
1179 ),
1180 )
1181 parser.add_argument(
1182 "-s",
1183 "--skip_install",
1184 dest="skip_install",
1185 default=False,
1186 action="store_true",
1187 help="skip install section defined in test definition.",
1188 )
1189 parser.add_argument(
1190 "-e",
1191 "--skip_environment",
1192 dest="skip_environment",
1193 default=False,
1194 action="store_true",
1195 help="skip environmental data collection (board name, distro, etc)",
1196 )
1197 parser.add_argument(
1198 "-l",
1199 "--lava_run",
1200 dest="lava_run",
1201 default=False,
1202 action="store_true",
1203 help="send test result to LAVA with lava-test-case.",
1204 )
1205 parser.add_argument(
1206 "-O",
1207 "--overlay",
1208 default=None,
1209 dest="overlay",
1210 help=textwrap.dedent(
1211 """\
Chase Qia158efe2017-11-17 12:35:11 +08001212 Specify test plan ovelay file to:
1213 * skip tests
1214 * amend test parameters
1215 * add new tests
Benjamin Copeland15d743e2021-02-22 08:35:10 +00001216 """
1217 ),
1218 )
1219 parser.add_argument(
1220 "-v",
1221 "--verbose",
1222 action="store_true",
1223 dest="verbose",
1224 default=False,
1225 help="Set log level to DEBUG.",
1226 )
Vishal Bhoje94da4a2020-05-15 12:22:40 +05301227 parser.add_argument(
1228 "--qa-reports-server",
1229 dest="qa_reports_server",
1230 default=None,
1231 help="qa reports server where the results have to be sent",
1232 )
1233 parser.add_argument(
1234 "--qa-reports-token",
1235 dest="qa_reports_token",
1236 default=None,
1237 help="qa reports token to upload the results to qa_reports_server",
1238 )
1239 parser.add_argument(
1240 "--qa-reports-project",
1241 dest="qa_reports_project",
1242 default=None,
1243 help="qa reports projects to which the results have to be uploaded",
1244 )
1245 parser.add_argument(
1246 "--qa-reports-group",
1247 dest="qa_reports_group",
1248 default=None,
1249 help="qa reports group in which the results have to be stored",
1250 )
1251 parser.add_argument(
1252 "--qa-reports-env",
1253 dest="qa_reports_env",
1254 default=None,
1255 help="qa reports environment for the results that have to be stored",
1256 )
1257 parser.add_argument(
1258 "--qa-reports-build-version",
1259 dest="qa_reports_build_version",
1260 default=None,
1261 help="qa reports build id for the result set",
1262 )
Milosz Wasilewskib4b40de2020-06-11 12:54:18 +01001263 parser.add_argument(
1264 "--qa-reports-disable-metadata",
1265 dest="qa_reports_disable_metadata",
1266 default=False,
Benjamin Copeland15d743e2021-02-22 08:35:10 +00001267 action="store_true",
Milosz Wasilewski37848d32020-06-11 14:33:43 +01001268 help="Disable sending metadata to SQUAD. Default: false",
1269 )
1270 parser.add_argument(
1271 "--qa-reports-metadata",
1272 dest="qa_reports_metadata",
1273 default={},
1274 action=StoreDictKeyPair,
1275 nargs="+",
1276 metavar="KEY=VALUE",
1277 help="List of metadata key=value pairs to be sent to SQUAD",
Milosz Wasilewskib4b40de2020-06-11 12:54:18 +01001278 )
Milosz Wasilewskiabe2dcc2020-06-12 10:54:46 +01001279 parser.add_argument(
1280 "--qa-reports-metadata-file",
1281 dest="qa_reports_metadata_file",
1282 default=None,
1283 help="YAML file that defines metadata to be reported to SQUAD",
1284 )
Milosz Wasilewskiba21eeb2022-02-15 10:23:11 +00001285 parser.add_argument(
1286 "--cleanup",
1287 dest="cleanup",
1288 default=False,
1289 action="store_true",
1290 help="If set to true, test-runner will remove all temporary files \
1291 after running the test. It includes all collected logs and \
1292 test results. This option should only be used if uploading \
1293 results to SQUAD or LAVA. \
1294 Default: false",
1295 )
Milosz Wasilewskib4b40de2020-06-11 12:54:18 +01001296
Chase Qi09edc7f2016-08-18 13:18:50 +08001297 args = parser.parse_args()
1298 return args
1299
1300
1301def main():
Chase Qi0e9b36e2017-12-07 16:13:44 +08001302 args = get_args()
1303
Chase Qi09edc7f2016-08-18 13:18:50 +08001304 # Setup logger.
Benjamin Copeland15d743e2021-02-22 08:35:10 +00001305 logger = logging.getLogger("RUNNER")
Chase Qi0e9b36e2017-12-07 16:13:44 +08001306 logger.setLevel(logging.INFO)
1307 if args.verbose:
1308 logger.setLevel(logging.DEBUG)
Chase Qi09edc7f2016-08-18 13:18:50 +08001309 ch = logging.StreamHandler()
1310 ch.setLevel(logging.DEBUG)
Benjamin Copeland15d743e2021-02-22 08:35:10 +00001311 formatter = logging.Formatter("%(asctime)s - %(name)s: %(levelname)s: %(message)s")
Chase Qi09edc7f2016-08-18 13:18:50 +08001312 ch.setFormatter(formatter)
1313 logger.addHandler(ch)
1314
Benjamin Copeland15d743e2021-02-22 08:35:10 +00001315 logger.debug("Test job arguments: %s" % args)
Milosz Wasilewski682120e2017-03-13 13:37:18 +00001316 if args.kind != "manual" and args.target is None:
Milosz Wasilewski2fea70e2016-11-11 12:16:09 +00001317 if os.geteuid() != 0:
1318 logger.error("Sorry, you need to run this as root")
1319 sys.exit(1)
Chase Qi09edc7f2016-08-18 13:18:50 +08001320
Milosz Wasilewski682120e2017-03-13 13:37:18 +00001321 # Validate target argument format and connectivity.
1322 if args.target:
Benjamin Copeland15d743e2021-02-22 08:35:10 +00001323 rex = re.compile(".+@.+")
Milosz Wasilewski682120e2017-03-13 13:37:18 +00001324 if not rex.match(args.target):
Benjamin Copeland15d743e2021-02-22 08:35:10 +00001325 logger.error("Usage: -g username@host")
Milosz Wasilewski682120e2017-03-13 13:37:18 +00001326 sys.exit(1)
Benjamin Copeland15d743e2021-02-22 08:35:10 +00001327 if pexpect.which("ssh") is None:
1328 logger.error("openssh client must be installed on the host.")
Milosz Wasilewski682120e2017-03-13 13:37:18 +00001329 sys.exit(1)
1330 try:
Dan Ruea9eb01c2017-06-07 16:29:09 -05001331 run_command("exit", args.target)
Milosz Wasilewski682120e2017-03-13 13:37:18 +00001332 except subprocess.CalledProcessError as e:
Benjamin Copeland15d743e2021-02-22 08:35:10 +00001333 logger.error("ssh login failed.")
Milosz Wasilewski682120e2017-03-13 13:37:18 +00001334 print(e)
1335 sys.exit(1)
1336
Chase Qi09edc7f2016-08-18 13:18:50 +08001337 # Generate test plan.
Chase Qi09edc7f2016-08-18 13:18:50 +08001338 test_plan = TestPlan(args)
Milosz Wasilewski2fea70e2016-11-11 12:16:09 +00001339 test_list = test_plan.test_list(args.kind)
Benjamin Copeland15d743e2021-02-22 08:35:10 +00001340 logger.info("Tests to run:")
Chase Qi09edc7f2016-08-18 13:18:50 +08001341 for test in test_list:
1342 print(test)
1343
1344 # Run tests.
1345 for test in test_list:
Milosz Wasilewski682120e2017-03-13 13:37:18 +00001346 # Set and save test params to test dictionary.
Benjamin Copeland15d743e2021-02-22 08:35:10 +00001347 test["test_name"] = os.path.splitext(test["path"].split("/")[-1])[0]
1348 test["test_uuid"] = "%s_%s" % (test["test_name"], test["uuid"])
1349 test["output"] = os.path.realpath(args.output)
1350 if args.target is not None and "-o" not in sys.argv:
1351 test["output"] = os.path.join(test["output"], args.target)
1352 test["test_path"] = os.path.join(test["output"], test["test_uuid"])
Milosz Wasilewski682120e2017-03-13 13:37:18 +00001353 if args.target is not None:
Chase Qi204b5422017-04-06 11:01:58 +08001354 # Get relative directory path of yaml file for partial file copy.
1355 # '-d' takes any relative paths to the yaml file, so get the realpath first.
Benjamin Copeland15d743e2021-02-22 08:35:10 +00001356 tc_realpath = os.path.realpath(test["path"])
Chase Qi204b5422017-04-06 11:01:58 +08001357 tc_dirname = os.path.dirname(tc_realpath)
Benjamin Copeland15d743e2021-02-22 08:35:10 +00001358 test["tc_relative_dir"] = "%s%s" % (
1359 args.kind,
1360 tc_dirname.split(args.kind)[1],
1361 )
Dan Ruea9eb01c2017-06-07 16:29:09 -05001362 target_user_home = run_command("echo $HOME", args.target)
Benjamin Copeland15d743e2021-02-22 08:35:10 +00001363 test["target_test_path"] = "%s/output/%s" % (
1364 target_user_home,
1365 test["test_uuid"],
1366 )
1367 logger.debug("Test parameters: %s" % test)
Milosz Wasilewski682120e2017-03-13 13:37:18 +00001368
Chase Qi09edc7f2016-08-18 13:18:50 +08001369 # Create directories and copy files needed.
1370 setup = TestSetup(test, args)
1371 setup.create_dir()
1372 setup.copy_test_repo()
Milosz Wasilewski970431b2016-11-25 14:10:08 +00001373 setup.checkout_version()
Chase Qi09edc7f2016-08-18 13:18:50 +08001374 setup.create_uuid_file()
1375
1376 # Convert test definition.
1377 test_def = TestDefinition(test, args)
Milosz Wasilewski2fea70e2016-11-11 12:16:09 +00001378 if test_def.exists:
1379 test_def.definition()
1380 test_def.metadata()
Nicolas Dechesne51b85a82017-02-04 00:45:48 +01001381 test_def.mkrun()
Chase Qi09edc7f2016-08-18 13:18:50 +08001382
Milosz Wasilewski2fea70e2016-11-11 12:16:09 +00001383 # Run test.
Nicolas Dechesne51b85a82017-02-04 00:45:48 +01001384 test_def.run()
Chase Qi09edc7f2016-08-18 13:18:50 +08001385
Milosz Wasilewski2fea70e2016-11-11 12:16:09 +00001386 # Parse test output, save results in json and csv format.
1387 result_parser = ResultParser(test, args)
1388 result_parser.run()
Milosz Wasilewskiba21eeb2022-02-15 10:23:11 +00001389 if args.cleanup:
1390 # remove a copy of test-definitions
1391 logger.warning("Removing a copy of test-definitions")
1392 logger.warning("Removing all collected logs")
1393 shutil.rmtree(test["test_path"])
Milosz Wasilewski2fea70e2016-11-11 12:16:09 +00001394 else:
Benjamin Copeland15d743e2021-02-22 08:35:10 +00001395 logger.warning("Requested test definition %s doesn't exist" % test["path"])
Chase Qi09edc7f2016-08-18 13:18:50 +08001396
Dan Ruea9eb01c2017-06-07 16:29:09 -05001397
Chase Qi09edc7f2016-08-18 13:18:50 +08001398if __name__ == "__main__":
1399 main()