Repository navigation
test: collect DUT logs in test mode #1739
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
aca3f3e
bfb5971
bc3032e
645f0c3
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,28 @@ | ||
| #!/bin/sh | ||
| # In test mode, forward all logs to the test controller's syslog sink. | ||
| # | ||
| # The test mode settings are a JSON object, from qemu (fw_cfg) or the | ||
| # test-mode file on aux, which is an empty marker on older setups: | ||
| # | ||
| # { "management-interface": "e1", "physical-name": "dut1" } | ||
|
|
||
| ident=$(basename "$0") | ||
| conf=/etc/syslog.d/test-mode.conf | ||
| group=ff02::161 | ||
|
|
||
| [ -f /mnt/aux/test-mode ] || exit 0 | ||
|
|
||
| # Qemu settings (for testing) take precedence over the test-mode file | ||
| file=/sys/firmware/qemu_fw_cfg/by_name/opt/test-mode/raw | ||
| [ -f "$file" ] || file=/mnt/aux/test-mode | ||
|
|
||
| iface=$(jq -r '."management-interface" // empty' "$file" 2>/dev/null) | ||
| [ -n "$iface" ] || exit 0 | ||
| name=$(jq -r '."physical-name" // empty' "$file" 2>/dev/null) | ||
|
|
||
| opts="RFC5424,iface=$iface" | ||
| [ -n "$name" ] && opts="$opts,sd=[device@32473 name=\"$name\"]" | ||
| echo "*.* @[$group] ;$opts" > "$conf" | ||
|
|
||
| logger -k -p user.notice -t "$ident" "Forwarding logs to $group on $iface" | ||
| kill -HUP "$(cat /run/syslogd.pid)" | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,57 @@ | ||
| """Mark test and step boundaries in the DUTs' logs, see doc/test-arch.md""" | ||
| import os | ||
| import sys | ||
| import threading | ||
| import time | ||
|
|
||
| SDID = "test@32473" | ||
|
|
||
|
|
||
| def test_name(): | ||
| """Name of the running test, as 9PM knows it or its directory""" | ||
| name = os.path.basename(os.path.dirname(os.path.realpath(sys.argv[0]))) | ||
| return os.environ.get("NINEPM_TEST_NAME", name) | ||
|
|
||
|
|
||
| class Markers: | ||
| """Best-effort, a DUT that does not answer is skipped until attached again""" | ||
|
|
||
| def __init__(self): | ||
| self.test = test_name() | ||
| self.devs = {} | ||
|
|
||
| def _log_one(self, dev, msgid, text, **params): | ||
| try: | ||
| dev.log(text, app_name="infamy", msgid=msgid, | ||
| sd={SDID: {"name": self.test, "role": dev.name, **params}}) | ||
| return True | ||
| except Exception as e: | ||
| print(f"{dev.name}: failed logging {msgid} marker: {e}") | ||
| return False | ||
|
|
||
| def _log(self, devs, msgid, text, **params): | ||
| def log(dev): | ||
| if self._log_one(dev, msgid, text, **params): | ||
| done.add(dev.name) | ||
|
|
||
| done = set() | ||
| threads = [threading.Thread(target=log, args=(dev,), daemon=True) for dev in devs] | ||
| for t in threads: | ||
| t.start() | ||
| deadline = time.monotonic() + 1 | ||
| for t in threads: | ||
| t.join(max(0, deadline - time.monotonic())) | ||
|
Comment on lines
+37
to
+43
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Could we not reuse
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Currently util.parallel() does not have timeout and wee need that for the markers. I could add optional timeout parameter to parallel() if we decide to? |
||
|
|
||
| for dev in devs: | ||
| if dev.name not in done: | ||
| self.devs.pop(dev.name, None) | ||
|
|
||
| def attach(self, dev): | ||
| if dev.name in self.devs or self._log_one(dev, "test-start", f"Test {self.test} starts"): | ||
| self.devs[dev.name] = dev | ||
|
|
||
| def step(self, num, msg): | ||
| self._log(list(self.devs.values()), "step", f"Step {num}: {msg}", step=str(num)) | ||
|
|
||
| def stop(self): | ||
| self._log(list(self.devs.values()), "test-stop", f"Test {self.test} stops") | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,47 @@ | ||
| #!/bin/sh | ||
| # Collect the logs DUTs in test mode forward to ff02::161 on their mgmt | ||
| # port, all in one file. See doc/test-arch.md, and utils/syslog_split.py | ||
| set -e | ||
|
|
||
| syslogd=/usr/sbin/syslogd | ||
| group=ff02::161 | ||
|
|
||
| usage() | ||
| { | ||
| echo "usage: syslog-sink <TOPOLOGY> <LOGFILE>" | ||
| } | ||
|
|
||
| [ $# -eq 2 ] || { usage; exit 1; } | ||
| topology=$1 | ||
| log=$2 | ||
|
|
||
| if [ ! -x "$syslogd" ]; then | ||
| echo "syslog-sink: $syslogd missing, not collecting DUT logs" >&2 | ||
| exit 0 | ||
| fi | ||
|
|
||
| ports=$(python3 - "$topology" <<'EOF' | ||
| import sys | ||
| import pydot | ||
| from infamy.topology import Topology | ||
|
|
||
| top = Topology(pydot.graph_from_dot_file(sys.argv[1])[0]) | ||
| ctrl = top.get_ctrl() | ||
| links = [top.get_mgmt_link(ctrl, node) for node in top.get_infixen()] | ||
| print(" ".join(link[0] for link in links if link)) | ||
| EOF | ||
| ) | ||
|
|
||
| dir=$(mktemp -d /tmp/syslog-sink.XXXXXX) | ||
| { | ||
| echo "secure_mode 0" | ||
| for port in $ports; do | ||
| echo "listen [$group%$port]%$port" | ||
| done | ||
| echo "*.* -$log ;RFC5424" | ||
| } > "$dir/syslog.conf" | ||
|
|
||
| mkdir -p "$(dirname "$log")" | ||
| $syslogd -f "$dir/syslog.conf" -p "$dir/log.sock" -P "$dir/syslogd.pid" \ | ||
| -C "$dir/syslogd.cache" -m 0 -8 -H -n -K | ||
| echo "syslog-sink: DUT logs saved in $log" |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,3 +1,4 @@ | ||
| -fw_cfg name=opt/mactab,file=$imgdir/{{name}}.mactab \ | ||
| -fw_cfg name=opt/wifi,file=$imgdir/{{name}}.wifi \ | ||
| -fw_cfg name=opt/modem,file=$imgdir/{{name}}.modem \ | ||
| -fw_cfg name=opt/test-mode,file=$imgdir/{{name}}.test-mode \ |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,7 @@ | ||
| # Test mode settings for this node, read at boot by 30-test-mode (fw_cfg | ||
| # opt/test-mode): its port on the mgmt link and its name in the topology. | ||
| mgmt= | ||
| {{#links}} | ||
| case " {{provides}} " in *" mgmt "*) mgmt="{{qn_name}}";; esac | ||
| {{/links}} | ||
| echo "{ \"management-interface\": \"$mgmt\", \"physical-name\": \"{{name}}\" }" > $imgdir/{{name}}.test-mode |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fw_cfgis a very neat way to import the test mode config. Is there any reason to also look for the marker?Could we not use the existence of either the on-disk marker, or the
fw_cfgfile as an indication to enable test-mode? Then we could also get rid of the test-mode-injection juggling intest/env.