This is used to generate measurements by interacting with devices that are not reachable as a node, and avoid target-specific support.
Each device.conf file can define a host command or script used when entering a lava_command_run and leaving a lava_command_run, In the style of the PDU power cycling commands.
host_hook_enter_command = <enter_hook> (command line to pass to host) host_hook_exit_command = <exit_hook>
Hooks calling abi:
ENTRER: arg0 = <enter_hook> arg1 = "output-file-fullpath" & EXIT: arg0 = <exit_hook> arg1 = "output-file-fullpath" &
The MO is close to lava_test_shell:
the LAVA_SIGNAL_TESTCASE search pattern will be applied to the output of the exit hook and if a measurement is found, a test_case will be added to the bundle stream of the job. Any file found in the logdir will be attached, starting with stdout.log. If a {output-file-fullpath}.minetype is found, its content is used as 'mime_type' for the attached file (as with lava_shell_test)
example, see Job http://lava.baylibre.com:10080/dashboard/streams/anonymous/lab-health/bundle... as an example of unit-test power statistics record and display as test_cases.
Signed-off-by: Marc Titinger mtitinger@baylibre.com ---
v2: expect LAVA_SIGNAL_TESTCASE patterns like with lava_test_shell
--- lava_dispatcher/actions/lava_command.py | 46 +++++++++++++++++++++++++++++++-- lava_dispatcher/config.py | 5 ++++ 2 files changed, 49 insertions(+), 2 deletions(-)
diff --git a/lava_dispatcher/actions/lava_command.py b/lava_dispatcher/actions/lava_command.py index 7bf787a..0748b51 100644 --- a/lava_dispatcher/actions/lava_command.py +++ b/lava_dispatcher/actions/lava_command.py @@ -33,6 +33,8 @@ from lava_dispatcher.test_data import create_attachment from lava_dispatcher.utils import read_content from datetime import datetime
+from lava_dispatcher.actions import lava_test_shell +from lava_dispatcher.lava_test_shell import parse_testcase_result
class cmd_lava_command_run(BaseAction):
@@ -54,16 +56,30 @@ class cmd_lava_command_run(BaseAction): _parser = None _fixupdict = {} _results_from_log_file = [] + _cur_record = None + _record_index = 0 + _uuid = None
def run(self, commands, parser=None, iterations=1, fixupdict=None, timeout=-1): target = self.client.target_device + context = self.context log_dir = tempfile.mkdtemp(dir=target.scratch_dir) self._logfile = os.path.join(log_dir, 'stdout.log') + self._uuid = str(uuid4()) if parser is not None: self._parser = parser if fixupdict is not None: self._fixupdict = fixupdict logging.info("lava_command logfile: %s" % self._logfile) + + #if there is a host-side hook to call: + host_enter_hook = context.device_config.host_hook_enter_command + if host_enter_hook: + self._cur_record = os.path.join(log_dir, self._uuid.split('-')[0]) + host_enter_hook = host_enter_hook.rstrip('&') + " " + self._cur_record + " &" + logging.warning('Running enter hook on host %s' % host_enter_hook) + context.run_command(host_enter_hook) + with self.client.tester_session() as session: for count in range(iterations): logging.info("Executing lava_command_run iteration: %s" % count) @@ -81,6 +97,27 @@ class cmd_lava_command_run(BaseAction): self._results_from_log_file.append(res) logging.error(e)
+ #if there is a host-side hook to call: + host_exit_hook = context.device_config.host_hook_exit_command + if host_exit_hook: + host_exit_hook = host_exit_hook.rstrip('&') + " " + self._cur_record + " &" + logging.warning('Running EXIT hook on dispatcher host %s' % host_exit_hook) + output = context.run_command_get_output(host_exit_hook) + + # See https://github.com/BayLibre/iio-capture as an instance of an + # app that will produce compatible output when called from the host + # Hook. + test_pattern = r"<LAVA_SIGNAL_TESTCASE TEST_CASE_ID=(?P<test_case_id>.*)\s+"\ + "RESULT=(?P<result>(PASS|pass|FAIL|fail|SKIP|skip|UNKNOWN|unknown))\s+"\ + "UNITS=(?P<units>.*)\s+MEASUREMENT=(?P<measurement>.*)>" + test_case_pattern = re.compile(test_pattern) + + for line in output.split(os.linesep): + match = test_case_pattern.match(line.strip()) + if match: + res = parse_testcase_result(match.groupdict()) + self._results_from_log_file.append(res) + bundle = self._get_bundle() self._write_results_bundle(bundle)
@@ -126,12 +163,17 @@ class cmd_lava_command_run(BaseAction):
def _get_test_runs(self): now = datetime.utcnow().strftime('%Y-%m-%dT%H:%M:%SZ') - attachment = [create_attachment(os.path.basename(self._logfile), read_content(self._logfile))] + attachment_dir = os.path.dirname(self._logfile) + attachment = lava_test_shell._attachments_from_dir(os.path.dirname(self._logfile)) + # fixup default mimetypes, for stdout.log mainly + for entry in attachment: + if entry['pathname'].endswith(".log"): + entry['mime_type'] = "text/plain" results = self._get_test_results() return { 'test_id': 'lava-command', 'analyzer_assigned_date': now, - 'analyzer_assigned_uuid': str(uuid4()), + 'analyzer_assigned_uuid': self._uuid, 'time_check_performed': False, 'test_results': results, 'attachments': attachment diff --git a/lava_dispatcher/config.py b/lava_dispatcher/config.py index 63aa3c6..10bccc8 100644 --- a/lava_dispatcher/config.py +++ b/lava_dispatcher/config.py @@ -29,6 +29,11 @@ from configglue import parser, schema
class DeviceSchema(schema.Schema): + + # Host-side hook for lava_command_run + host_hook_enter_command = schema.StringOption() + host_hook_exit_command = schema.StringOption() + master_testboot_dir = schema.StringOption() master_testboot_label = schema.StringOption() master_testrootfs_dir = schema.StringOption()