blob: b036a65b381e087d1a42c3bfaafb1055422bdd3b [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
1028 # Collect test results of all tests in output/result.json
1029 feeds = []
Benjamin Copeland15d743e2021-02-22 08:35:10 +00001030 if os.path.isfile("%s/result.json" % self.test["output"]):
1031 with open("%s/result.json" % self.test["output"], "r") as f:
Chase Qi87f4f402016-11-07 15:32:01 +08001032 feeds = json.load(f)
1033
1034 feeds.append(self.results)
Benjamin Copeland15d743e2021-02-22 08:35:10 +00001035 with open("%s/result.json" % self.test["output"], "w") as f:
Chase Qi87f4f402016-11-07 15:32:01 +08001036 json.dump(feeds, f, indent=4)
Chase Qi09edc7f2016-08-18 13:18:50 +08001037
1038 def dict_to_csv(self):
Chase Qica15cf52016-11-10 17:00:22 +08001039 # Convert dict self.results['params'] to a string.
Benjamin Copeland15d743e2021-02-22 08:35:10 +00001040 test_params = ""
1041 if self.results["params"]:
1042 params_dict = self.results["params"]
1043 test_params = ";".join(["%s=%s" % (k, v) for k, v in params_dict.items()])
Chase Qi09edc7f2016-08-18 13:18:50 +08001044
Benjamin Copeland15d743e2021-02-22 08:35:10 +00001045 for metric in self.results["metrics"]:
1046 metric["name"] = self.results["name"]
1047 metric["test_params"] = test_params
Chase Qica15cf52016-11-10 17:00:22 +08001048
1049 # Save test results to output/test_id/result.csv
Benjamin Copeland15d743e2021-02-22 08:35:10 +00001050 fieldnames = [
1051 "name",
1052 "test_case_id",
1053 "result",
1054 "measurement",
1055 "units",
1056 "test_params",
1057 ]
1058 with open("%s/result.csv" % self.test["test_path"], "w") as f:
Chase Qica15cf52016-11-10 17:00:22 +08001059 writer = csv.DictWriter(f, fieldnames=fieldnames)
Chase Qi09edc7f2016-08-18 13:18:50 +08001060 writer.writeheader()
Benjamin Copeland15d743e2021-02-22 08:35:10 +00001061 for metric in self.results["metrics"]:
Chase Qi09edc7f2016-08-18 13:18:50 +08001062 writer.writerow(metric)
1063
Chase Qi87f4f402016-11-07 15:32:01 +08001064 # Collect test results of all tests in output/result.csv
Benjamin Copeland15d743e2021-02-22 08:35:10 +00001065 if not os.path.isfile("%s/result.csv" % self.test["output"]):
1066 with open("%s/result.csv" % self.test["output"], "w") as f:
Chase Qi87f4f402016-11-07 15:32:01 +08001067 writer = csv.DictWriter(f, fieldnames=fieldnames)
1068 writer.writeheader()
1069
Benjamin Copeland15d743e2021-02-22 08:35:10 +00001070 with open("%s/result.csv" % self.test["output"], "a") as f:
Chase Qi09edc7f2016-08-18 13:18:50 +08001071 writer = csv.DictWriter(f, fieldnames=fieldnames)
Benjamin Copeland15d743e2021-02-22 08:35:10 +00001072 for metric in self.results["metrics"]:
Chase Qi09edc7f2016-08-18 13:18:50 +08001073 writer.writerow(metric)
1074
1075
Vishal Bhoje94da4a2020-05-15 12:22:40 +05301076def get_token_from_netrc(qa_reports_server):
1077 if qa_reports_server is None:
1078 return
1079 parse = urlparse(qa_reports_server)
1080 netrc_local = netrc.netrc()
1081 authTokens = netrc_local.authenticators("{}".format(parse.netloc))
1082 if authTokens is not None:
1083 hostname, username, authToken = authTokens
1084 return authToken
1085 # Unable to find Token hence returning None
1086 return
1087
1088
Chase Qi09edc7f2016-08-18 13:18:50 +08001089def get_args():
Milosz Wasilewski259ba192017-07-27 10:59:25 +01001090 parser = argparse.ArgumentParser(formatter_class=argparse.RawTextHelpFormatter)
Benjamin Copeland15d743e2021-02-22 08:35:10 +00001091 parser.add_argument(
1092 "-o",
1093 "--output",
1094 default=os.getenv("HOME", "") + "/output",
1095 dest="output",
1096 help=textwrap.dedent(
1097 """\
Chase Qi09edc7f2016-08-18 13:18:50 +08001098 specify a directory to store test and result files.
Nicolas Dechesnef6c4c212017-01-18 17:30:04 +01001099 Default: $HOME/output
Benjamin Copeland15d743e2021-02-22 08:35:10 +00001100 """
1101 ),
1102 )
1103 parser.add_argument(
1104 "-p",
1105 "--test_plan",
1106 default=None,
1107 dest="test_plan",
1108 help=textwrap.dedent(
1109 """\
Chase Qi09edc7f2016-08-18 13:18:50 +08001110 specify an test plan file which has tests and related
1111 params listed in yaml format.
Benjamin Copeland15d743e2021-02-22 08:35:10 +00001112 """
1113 ),
1114 )
1115 parser.add_argument(
1116 "-d",
1117 "--test_def",
1118 default=None,
1119 dest="test_def",
1120 help=textwrap.dedent(
1121 """\
Chase Qi09edc7f2016-08-18 13:18:50 +08001122 base on test definition repo location, specify relative
1123 path to the test definition to run.
1124 Format example: "ubuntu/smoke-tests-basic.yaml"
Benjamin Copeland15d743e2021-02-22 08:35:10 +00001125 """
1126 ),
1127 )
1128 parser.add_argument(
1129 "-r",
1130 "--test_def_params",
1131 default={},
1132 dest="test_def_params",
1133 action=StoreDictKeyPair,
1134 nargs="+",
1135 metavar="KEY=VALUE",
1136 help=textwrap.dedent(
1137 """\
Milosz Wasilewski259ba192017-07-27 10:59:25 +01001138 Set additional parameters when using test definition without
1139 a test plan. The name values are set similarily to environment
1140 variables:
1141 --test_def_params KEY1=VALUE1 KEY2=VALUE2 ...
Benjamin Copeland15d743e2021-02-22 08:35:10 +00001142 """
1143 ),
1144 )
1145 parser.add_argument(
1146 "-k",
1147 "--kind",
1148 default="automated",
1149 dest="kind",
1150 choices=["automated", "manual"],
1151 help=textwrap.dedent(
1152 """\
Milosz Wasilewski2fea70e2016-11-11 12:16:09 +00001153 Selects type of tests to be executed from the test plan.
1154 Possible options: automated, manual
Benjamin Copeland15d743e2021-02-22 08:35:10 +00001155 """
1156 ),
1157 )
1158 parser.add_argument(
1159 "-t",
1160 "--timeout",
1161 type=int,
1162 default=None,
1163 dest="timeout",
1164 help="Specify test timeout",
1165 )
1166 parser.add_argument(
1167 "-g",
1168 "--target",
1169 default=None,
1170 dest="target",
1171 help=textwrap.dedent(
1172 """\
Milosz Wasilewski682120e2017-03-13 13:37:18 +00001173 Specify SSH target to execute tests.
1174 Format: user@host
1175 Note: ssh authentication must be paswordless
Benjamin Copeland15d743e2021-02-22 08:35:10 +00001176 """
1177 ),
1178 )
1179 parser.add_argument(
1180 "-s",
1181 "--skip_install",
1182 dest="skip_install",
1183 default=False,
1184 action="store_true",
1185 help="skip install section defined in test definition.",
1186 )
1187 parser.add_argument(
1188 "-e",
1189 "--skip_environment",
1190 dest="skip_environment",
1191 default=False,
1192 action="store_true",
1193 help="skip environmental data collection (board name, distro, etc)",
1194 )
1195 parser.add_argument(
1196 "-l",
1197 "--lava_run",
1198 dest="lava_run",
1199 default=False,
1200 action="store_true",
1201 help="send test result to LAVA with lava-test-case.",
1202 )
1203 parser.add_argument(
1204 "-O",
1205 "--overlay",
1206 default=None,
1207 dest="overlay",
1208 help=textwrap.dedent(
1209 """\
Chase Qia158efe2017-11-17 12:35:11 +08001210 Specify test plan ovelay file to:
1211 * skip tests
1212 * amend test parameters
1213 * add new tests
Benjamin Copeland15d743e2021-02-22 08:35:10 +00001214 """
1215 ),
1216 )
1217 parser.add_argument(
1218 "-v",
1219 "--verbose",
1220 action="store_true",
1221 dest="verbose",
1222 default=False,
1223 help="Set log level to DEBUG.",
1224 )
Vishal Bhoje94da4a2020-05-15 12:22:40 +05301225 parser.add_argument(
1226 "--qa-reports-server",
1227 dest="qa_reports_server",
1228 default=None,
1229 help="qa reports server where the results have to be sent",
1230 )
1231 parser.add_argument(
1232 "--qa-reports-token",
1233 dest="qa_reports_token",
1234 default=None,
1235 help="qa reports token to upload the results to qa_reports_server",
1236 )
1237 parser.add_argument(
1238 "--qa-reports-project",
1239 dest="qa_reports_project",
1240 default=None,
1241 help="qa reports projects to which the results have to be uploaded",
1242 )
1243 parser.add_argument(
1244 "--qa-reports-group",
1245 dest="qa_reports_group",
1246 default=None,
1247 help="qa reports group in which the results have to be stored",
1248 )
1249 parser.add_argument(
1250 "--qa-reports-env",
1251 dest="qa_reports_env",
1252 default=None,
1253 help="qa reports environment for the results that have to be stored",
1254 )
1255 parser.add_argument(
1256 "--qa-reports-build-version",
1257 dest="qa_reports_build_version",
1258 default=None,
1259 help="qa reports build id for the result set",
1260 )
Milosz Wasilewskib4b40de2020-06-11 12:54:18 +01001261 parser.add_argument(
1262 "--qa-reports-disable-metadata",
1263 dest="qa_reports_disable_metadata",
1264 default=False,
Benjamin Copeland15d743e2021-02-22 08:35:10 +00001265 action="store_true",
Milosz Wasilewski37848d32020-06-11 14:33:43 +01001266 help="Disable sending metadata to SQUAD. Default: false",
1267 )
1268 parser.add_argument(
1269 "--qa-reports-metadata",
1270 dest="qa_reports_metadata",
1271 default={},
1272 action=StoreDictKeyPair,
1273 nargs="+",
1274 metavar="KEY=VALUE",
1275 help="List of metadata key=value pairs to be sent to SQUAD",
Milosz Wasilewskib4b40de2020-06-11 12:54:18 +01001276 )
Milosz Wasilewskiabe2dcc2020-06-12 10:54:46 +01001277 parser.add_argument(
1278 "--qa-reports-metadata-file",
1279 dest="qa_reports_metadata_file",
1280 default=None,
1281 help="YAML file that defines metadata to be reported to SQUAD",
1282 )
Milosz Wasilewskib4b40de2020-06-11 12:54:18 +01001283
Chase Qi09edc7f2016-08-18 13:18:50 +08001284 args = parser.parse_args()
1285 return args
1286
1287
1288def main():
Chase Qi0e9b36e2017-12-07 16:13:44 +08001289 args = get_args()
1290
Chase Qi09edc7f2016-08-18 13:18:50 +08001291 # Setup logger.
Benjamin Copeland15d743e2021-02-22 08:35:10 +00001292 logger = logging.getLogger("RUNNER")
Chase Qi0e9b36e2017-12-07 16:13:44 +08001293 logger.setLevel(logging.INFO)
1294 if args.verbose:
1295 logger.setLevel(logging.DEBUG)
Chase Qi09edc7f2016-08-18 13:18:50 +08001296 ch = logging.StreamHandler()
1297 ch.setLevel(logging.DEBUG)
Benjamin Copeland15d743e2021-02-22 08:35:10 +00001298 formatter = logging.Formatter("%(asctime)s - %(name)s: %(levelname)s: %(message)s")
Chase Qi09edc7f2016-08-18 13:18:50 +08001299 ch.setFormatter(formatter)
1300 logger.addHandler(ch)
1301
Benjamin Copeland15d743e2021-02-22 08:35:10 +00001302 logger.debug("Test job arguments: %s" % args)
Milosz Wasilewski682120e2017-03-13 13:37:18 +00001303 if args.kind != "manual" and args.target is None:
Milosz Wasilewski2fea70e2016-11-11 12:16:09 +00001304 if os.geteuid() != 0:
1305 logger.error("Sorry, you need to run this as root")
1306 sys.exit(1)
Chase Qi09edc7f2016-08-18 13:18:50 +08001307
Milosz Wasilewski682120e2017-03-13 13:37:18 +00001308 # Validate target argument format and connectivity.
1309 if args.target:
Benjamin Copeland15d743e2021-02-22 08:35:10 +00001310 rex = re.compile(".+@.+")
Milosz Wasilewski682120e2017-03-13 13:37:18 +00001311 if not rex.match(args.target):
Benjamin Copeland15d743e2021-02-22 08:35:10 +00001312 logger.error("Usage: -g username@host")
Milosz Wasilewski682120e2017-03-13 13:37:18 +00001313 sys.exit(1)
Benjamin Copeland15d743e2021-02-22 08:35:10 +00001314 if pexpect.which("ssh") is None:
1315 logger.error("openssh client must be installed on the host.")
Milosz Wasilewski682120e2017-03-13 13:37:18 +00001316 sys.exit(1)
1317 try:
Dan Ruea9eb01c2017-06-07 16:29:09 -05001318 run_command("exit", args.target)
Milosz Wasilewski682120e2017-03-13 13:37:18 +00001319 except subprocess.CalledProcessError as e:
Benjamin Copeland15d743e2021-02-22 08:35:10 +00001320 logger.error("ssh login failed.")
Milosz Wasilewski682120e2017-03-13 13:37:18 +00001321 print(e)
1322 sys.exit(1)
1323
Chase Qi09edc7f2016-08-18 13:18:50 +08001324 # Generate test plan.
Chase Qi09edc7f2016-08-18 13:18:50 +08001325 test_plan = TestPlan(args)
Milosz Wasilewski2fea70e2016-11-11 12:16:09 +00001326 test_list = test_plan.test_list(args.kind)
Benjamin Copeland15d743e2021-02-22 08:35:10 +00001327 logger.info("Tests to run:")
Chase Qi09edc7f2016-08-18 13:18:50 +08001328 for test in test_list:
1329 print(test)
1330
1331 # Run tests.
1332 for test in test_list:
Milosz Wasilewski682120e2017-03-13 13:37:18 +00001333 # Set and save test params to test dictionary.
Benjamin Copeland15d743e2021-02-22 08:35:10 +00001334 test["test_name"] = os.path.splitext(test["path"].split("/")[-1])[0]
1335 test["test_uuid"] = "%s_%s" % (test["test_name"], test["uuid"])
1336 test["output"] = os.path.realpath(args.output)
1337 if args.target is not None and "-o" not in sys.argv:
1338 test["output"] = os.path.join(test["output"], args.target)
1339 test["test_path"] = os.path.join(test["output"], test["test_uuid"])
Milosz Wasilewski682120e2017-03-13 13:37:18 +00001340 if args.target is not None:
Chase Qi204b5422017-04-06 11:01:58 +08001341 # Get relative directory path of yaml file for partial file copy.
1342 # '-d' takes any relative paths to the yaml file, so get the realpath first.
Benjamin Copeland15d743e2021-02-22 08:35:10 +00001343 tc_realpath = os.path.realpath(test["path"])
Chase Qi204b5422017-04-06 11:01:58 +08001344 tc_dirname = os.path.dirname(tc_realpath)
Benjamin Copeland15d743e2021-02-22 08:35:10 +00001345 test["tc_relative_dir"] = "%s%s" % (
1346 args.kind,
1347 tc_dirname.split(args.kind)[1],
1348 )
Dan Ruea9eb01c2017-06-07 16:29:09 -05001349 target_user_home = run_command("echo $HOME", args.target)
Benjamin Copeland15d743e2021-02-22 08:35:10 +00001350 test["target_test_path"] = "%s/output/%s" % (
1351 target_user_home,
1352 test["test_uuid"],
1353 )
1354 logger.debug("Test parameters: %s" % test)
Milosz Wasilewski682120e2017-03-13 13:37:18 +00001355
Chase Qi09edc7f2016-08-18 13:18:50 +08001356 # Create directories and copy files needed.
1357 setup = TestSetup(test, args)
1358 setup.create_dir()
1359 setup.copy_test_repo()
Milosz Wasilewski970431b2016-11-25 14:10:08 +00001360 setup.checkout_version()
Chase Qi09edc7f2016-08-18 13:18:50 +08001361 setup.create_uuid_file()
1362
1363 # Convert test definition.
1364 test_def = TestDefinition(test, args)
Milosz Wasilewski2fea70e2016-11-11 12:16:09 +00001365 if test_def.exists:
1366 test_def.definition()
1367 test_def.metadata()
Nicolas Dechesne51b85a82017-02-04 00:45:48 +01001368 test_def.mkrun()
Chase Qi09edc7f2016-08-18 13:18:50 +08001369
Milosz Wasilewski2fea70e2016-11-11 12:16:09 +00001370 # Run test.
Nicolas Dechesne51b85a82017-02-04 00:45:48 +01001371 test_def.run()
Chase Qi09edc7f2016-08-18 13:18:50 +08001372
Milosz Wasilewski2fea70e2016-11-11 12:16:09 +00001373 # Parse test output, save results in json and csv format.
1374 result_parser = ResultParser(test, args)
1375 result_parser.run()
1376 else:
Benjamin Copeland15d743e2021-02-22 08:35:10 +00001377 logger.warning("Requested test definition %s doesn't exist" % test["path"])
Chase Qi09edc7f2016-08-18 13:18:50 +08001378
Dan Ruea9eb01c2017-06-07 16:29:09 -05001379
Chase Qi09edc7f2016-08-18 13:18:50 +08001380if __name__ == "__main__":
1381 main()