diff --git a/.github/workflows/trigger.yml b/.github/workflows/trigger.yml
index c0db755ff..ed1f274da 100644
--- a/.github/workflows/trigger.yml
+++ b/.github/workflows/trigger.yml
@@ -9,6 +9,10 @@ on:
- ci-work
workflow_dispatch:
+concurrency:
+ group: ci-${{ github.event.pull_request.number || github.ref }}
+ cancel-in-progress: true
+
jobs:
build-x86_64:
if: startsWith(github.repository, 'kernelkit/')
diff --git a/board/common/rootfs/usr/bin/show-legacy b/board/common/rootfs/usr/bin/show-legacy
old mode 100644
new mode 100755
diff --git a/doc/ChangeLog.md b/doc/ChangeLog.md
index acb7e7b80..9a911897a 100644
--- a/doc/ChangeLog.md
+++ b/doc/ChangeLog.md
@@ -3,7 +3,7 @@ Change Log
All notable changes to the project are documented in this file.
-[v25.08.1][] - 2025-10-01
+[v25.08.1][] - 2025-09-30
-------------------------
### Changes
@@ -12,6 +12,8 @@ All notable changes to the project are documented in this file.
### Fixes
- Fix #1150: `show-legacy` wrapper permissions
- Fix #1155: `show ospf` commands regression
+- Fix #1169: Expected neighbors not shown in sysrepocfg
+- Various test stability improvments
[v25.08.0][] - 2025-09-01
-------------------------
diff --git a/src/klish-plugin-infix/xml/infix.xml b/src/klish-plugin-infix/xml/infix.xml
index 1ce5ff1a4..8da12d07a 100644
--- a/src/klish-plugin-infix/xml/infix.xml
+++ b/src/klish-plugin-infix/xml/infix.xml
@@ -338,17 +338,17 @@
if [ -z "$KLISH_PARAM_name" ]; then
- doas vtysh -c "show-legacy ip ospf" |pager
+ doas vtysh -c "show ip ospf" |pager
elif [ "$KLISH_PARAM_name" == "neighbor" ];then
- doas vtysh -c "show-legacy ip ospf neighbor" |pager
+ doas vtysh -c "show ip ospf neighbor" |pager
elif [ "$KLISH_PARAM_name" == "interfaces" ];then
- doas vtysh -c "show-legacy ip ospf interface" |pager
+ doas vtysh -c "show ip ospf interface" |pager
elif [ "$KLISH_PARAM_name" == "routes" ];then
- doas vtysh -c "show-legacy ip ospf route" |pager
+ doas vtysh -c "show ip ospf route" |pager
elif [ "$KLISH_PARAM_name" == "database" ];then
- doas vtysh -c "show-legacy ip ospf database" |pager
+ doas vtysh -c "show ip ospf database" |pager
elif [ "$KLISH_PARAM_name" == "bfd" ];then
- doas vtysh -c "show-legacy bfd peers" |pager
+ doas vtysh -c "show bfd peers" |pager
fi
diff --git a/src/statd/python/ospf_status/ospf_status.py b/src/statd/python/ospf_status/ospf_status.py
index 09bcff842..ecc89217e 100755
--- a/src/statd/python/ospf_status/ospf_status.py
+++ b/src/statd/python/ospf_status/ospf_status.py
@@ -40,6 +40,11 @@ def main():
for ifname, iface in interfaces["interfaces"].items():
iface["name"] = ifname
iface["neighbors"] = []
+
+ # Skip interfaces that don't have OSPF enabled or area configured
+ if not iface.get("ospfEnabled", False) or not iface.get("area"):
+ continue
+
for area_id in ospf["areas"]:
area_type=""
diff --git a/test/case/all.yaml b/test/case/all.yaml
index 22cbecec0..74174a306 100644
--- a/test/case/all.yaml
+++ b/test/case/all.yaml
@@ -6,12 +6,26 @@
infamy:
specification: False
+- case: meta/bootorder.py
+ infamy:
+ specification: False
+
+- case: meta/check-version.py
+ infamy:
+ specification: False
+
+
- name: Misc tests
suite: misc/misc.yaml
- name: ietf-system
suite: ietf_system/ietf_system.yaml
+# Upgrade may leave wrong boot order
+- case: meta/bootorder.py
+ infamy:
+ specification: False
+
- name: ietf-syslog
suite: ietf_syslog/ietf_syslog.yaml
diff --git a/test/case/infix_containers/container_environment/test.py b/test/case/infix_containers/container_environment/test.py
index f571fcfbe..eb2d5189c 100755
--- a/test/case/infix_containers/container_environment/test.py
+++ b/test/case/infix_containers/container_environment/test.py
@@ -3,27 +3,32 @@
Container environment variables
Verify that environment variables can be set in container configuration
-and are available inside the running container. Tests the 'env' list
-functionality by:
+and are available inside the running container.
-1. Creating a container with multiple environment variables
-2. Using a custom script to extract env vars and serve them via HTTP
-3. Fetching the served content to verify environment variables are set correctly
-
-Uses the nftables container image with custom rc.local script.
+1 Set up a container config with multiple environment variables
+2. Serve variables back to host using a CGI script in container
+3. Verify served content against environment variables
"""
import infamy
from infamy.util import until, to_binary
with infamy.Test() as test:
+ ENV_VARS = [
+ {"key": "TEST_VAR", "value": "hello-world"},
+ {"key": "APP_PORT", "value": "8080"},
+ {"key": "DEBUG_MODE", "value": "true"},
+ {"key": "PATH_WITH_SPACES", "value": "/path with spaces/test"}
+ ]
NAME = "web-env"
DUTIP = "10.0.0.2"
OURIP = "10.0.0.1"
+ url = infamy.Furl(f"http://{DUTIP}:8080/cgi-bin/env.cgi")
with test.step("Set up topology and attach to target DUT"):
env = infamy.Env()
target = env.attach("target", "mgmt")
+ _, hport = env.ltop.xlate("host", "data")
if not target.has_model("infix-containers"):
test.skip()
@@ -44,44 +49,33 @@
}
})
- with test.step("Create container with environment variables"):
- script = to_binary("""#!/bin/sh
-# Create HTTP response with environment variables
-printf "HTTP/1.1 200 OK\\r\\n" > /var/www/response.txt
-printf "Content-Type: text/plain\\r\\n" >> /var/www/response.txt
-printf "Connection: close\\r\\n\\r\\n" >> /var/www/response.txt
-
-# Add environment variables using printf to control encoding
-printf "TEST_VAR=\\"%s\\"\\n" "$TEST_VAR" >> /var/www/response.txt
-printf "APP_PORT=%s\\n" "$APP_PORT" >> /var/www/response.txt
-printf "DEBUG_MODE=\\"%s\\"\\n" "$DEBUG_MODE" >> /var/www/response.txt
-printf "PATH_WITH_SPACES=\\"%s\\"\\n" "$PATH_WITH_SPACES" >> /var/www/response.txt
+ with test.step("Set up container with environment variables"):
+ cgi = [
+ '#!/bin/sh',
+ '# CGI script to output environment variables',
+ 'echo "Content-Type: text/plain"',
+ 'echo ""'
+ ]
-while true; do
- nc -l -p 8080 < /var/www/response.txt 2>>/var/www/debug.log || sleep 1
-done
-""")
+ for var in ENV_VARS:
+ cgi.append(f'echo "{var["key"]}=${var["key"]}"')
target.put_config_dict("infix-containers", {
"containers": {
"container": [
{
"name": f"{NAME}",
- "image": f"oci-archive:{infamy.Container.NFTABLES_IMAGE}",
- "env": [
- {"key": "TEST_VAR", "value": "hello-world"},
- {"key": "APP_PORT", "value": "8080"},
- {"key": "DEBUG_MODE", "value": "true"},
- {"key": "PATH_WITH_SPACES", "value": "/path with spaces/test"}
- ],
+ "image": f"oci-archive:{infamy.Container.HTTPD_IMAGE}",
+ "command": "/usr/sbin/httpd -f -v -p 8080",
+ "env": ENV_VARS,
"network": {
"host": True
},
"mount": [
{
- "name": "rc.local",
- "content": script,
- "target": "/etc/rc.local",
+ "name": "env.cgi",
+ "content": to_binary('\n'.join(cgi) + '\n'),
+ "target": "/var/www/cgi-bin/env.cgi",
"mode": "0755"
}
],
@@ -98,20 +92,17 @@
c = infamy.Container(target)
until(lambda: c.running(NAME), attempts=60)
- with test.step("Verify environment variables are available via HTTP"):
- _, hport = env.ltop.xlate("host", "data")
- url = infamy.Furl(f"http://{DUTIP}:8080/env.html")
+ with infamy.IsolatedMacVlan(hport) as ns:
+ ns.addip(OURIP)
- with infamy.IsolatedMacVlan(hport) as ns:
- ns.addip(OURIP)
+ with test.step("Verify basic connectivity to data interface"):
+ ns.must_reach(DUTIP)
- with test.step("Verify basic connectivity to data interface"):
- ns.must_reach(DUTIP)
+ with test.step("Verify environment variables in CGI response"):
+ expected_strings = []
+ for var in ENV_VARS:
+ expected_strings.append(f'{var["key"]}={var["value"]}')
- with test.step("Verify environment variables in HTTP response"):
- until(lambda: url.nscheck(ns, "TEST_VAR=\"hello-world\""), attempts=10)
- until(lambda: url.nscheck(ns, "APP_PORT=8080"), attempts=10)
- until(lambda: url.nscheck(ns, "DEBUG_MODE=\"true\""), attempts=10)
- until(lambda: url.nscheck(ns, "PATH_WITH_SPACES=\"/path with spaces/test\""), attempts=10)
+ until(lambda: url.nscheck(ns, expected_strings))
test.succeed()
diff --git a/test/case/infix_containers/infix_containers.yaml b/test/case/infix_containers/infix_containers.yaml
index 83d07fd74..b3f65e959 100644
--- a/test/case/infix_containers/infix_containers.yaml
+++ b/test/case/infix_containers/infix_containers.yaml
@@ -6,9 +6,8 @@
- name: container_enabled
case: container_enabled/test.py
-# Disabled for v25.08, new/unstable
-# - name: container_environment
-# case: container_environment/test.py
+- name: container_environment
+ case: container_environment/test.py
- name: container_bridge
case: container_bridge/test.py
diff --git a/test/case/infix_services/mdns/mdns.yaml b/test/case/infix_services/mdns/mdns.yaml
index 09c785466..248c901f5 100644
--- a/test/case/infix_services/mdns/mdns.yaml
+++ b/test/case/infix_services/mdns/mdns.yaml
@@ -2,6 +2,5 @@
- name: mdns_enable_disable
case: mdns_enable_disable/test.py
-# Disabled for v25.08, unstable
-# - name: mdns_allow_deny
-# case: mdns_allow_deny/test.py
+- name: mdns_allow_deny
+ case: mdns_allow_deny/test.py
diff --git a/test/case/infix_services/mdns/mdns_allow_deny/test.py b/test/case/infix_services/mdns/mdns_allow_deny/test.py
index f53adbb65..8fa5707aa 100755
--- a/test/case/infix_services/mdns/mdns_allow_deny/test.py
+++ b/test/case/infix_services/mdns/mdns_allow_deny/test.py
@@ -10,22 +10,55 @@
3. Allow p1 and p3, deny p2 and p3, traffic only on p1
"""
-
-import time
+import re
import infamy
-from infamy.util import parallel
-def mdns_scan(tgt):
- """Trigger Avahi to send traffic on allowed interfaces"""
- time.sleep(2)
- tgt.runsh("logger -t scan 'calling avahi-browse ...'")
- tgt.runsh("avahi-browse -lat")
+def mdns_scan():
+ """Start packet captures, trigger mDNS scan, return capture results"""
+ pcap1 = ns1.pcap("host 10.0.1.1 and port 5353")
+ pcap2 = ns2.pcap("host 10.0.2.1 and port 5353")
+ pcap3 = ns3.pcap("host 10.0.3.1 and port 5353")
+
+ with pcap1, pcap2, pcap3:
+ ssh.runsh("logger -t scan 'calling avahi-browse ...'")
+ ssh.runsh("avahi-browse -lat")
+
+ def has_packets(output):
+ if not output:
+ return False
+ lines = output.strip().split('\n')
+ m = re.search(r'^(\d+) packets.*', lines[1])
+ if m and int(m.group(1)) > 0:
+ return True
+ return False
+
+ out1 = pcap1.tcpdump("--count")
+ out2 = pcap2.tcpdump("--count")
+ out3 = pcap3.tcpdump("--count")
+
+ return (has_packets(out1), has_packets(out2), has_packets(out3))
+
+def check(expected, allow=None, deny=None):
+ """Execute complete mDNS test scenario"""
+ try:
+ dut.delete_xpath("/infix-services:mdns/interfaces")
+ except ValueError:
+ # Ignore if xpath doesn't exist (first run)
+ pass
-def check(ns, expr, must):
- """Wrap netns.must_receive() with common defaults"""
- return ns.must_receive(expr, timeout=3, must=must)
+ mdns_config = {"mdns": {"interfaces": {}}}
+ if allow:
+ mdns_config["mdns"]["interfaces"]["allow"] = allow
+ if deny:
+ mdns_config["mdns"]["interfaces"]["deny"] = deny
+
+ dut.put_config_dict("infix-services", mdns_config)
+
+ actual = mdns_scan()
+ if actual != tuple(expected):
+ raise AssertionError(f"Expected {expected}, got {actual}")
with infamy.Test() as test:
@@ -45,58 +78,41 @@ def check(ns, expr, must):
{
"ietf-interfaces": {
"interfaces": {
- "interface": [
- {
- "name": p1,
- "enabled": True,
- "ipv4": {
- "address": [
- {
- "ip": "10.0.1.1",
- "prefix-length": 24
- }
- ]
- }
-
- },
- {
- "name": p2,
- "enabled": True,
- "ipv4": {
- "address": [
- {
- "ip": "10.0.2.1",
- "prefix-length": 24
- }
- ]
- }
-
- },
- {
- "name": p3,
- "enabled": True,
- "ipv4": {
- "address": [
- {
- "ip": "10.0.3.1",
- "prefix-length": 24
- }
- ]
- }
-
- },
- ]
- }
+ "interface": [{
+ "name": p1,
+ "enabled": True,
+ "ipv4": {
+ "address": [{
+ "ip": "10.0.1.1",
+ "prefix-length": 24
+ }]
+ }
+ }, {
+ "name": p2,
+ "enabled": True,
+ "ipv4": {
+ "address": [{
+ "ip": "10.0.2.1",
+ "prefix-length": 24
+ }]
+ }
+ }, {
+ "name": p3,
+ "enabled": True,
+ "ipv4": {
+ "address": [{
+ "ip": "10.0.3.1",
+ "prefix-length": 24
+ }]
+ }
+ }]
+ }
},
"ietf-system": {
- "system": {
- "hostname": "dut"
- }
+ "system": {"hostname": "dut"}
},
"infix-services": {
- "mdns": {
- "enabled": True
- }
+ "mdns": {"enabled": True}
}
}
)
@@ -108,53 +124,16 @@ def check(ns, expr, must):
ns2.addip("10.0.2.2")
ns3.addip("10.0.3.2")
- EXPR1 = "host 10.0.1.1 and port 5353"
- EXPR2 = "host 10.0.2.1 and port 5353"
- EXPR3 = "host 10.0.3.1 and port 5353"
-
with test.step("Allow mDNS on a single interface: p2"):
- dut.put_config_dict("infix-services", {
- "mdns": {
- "interfaces": {
- "allow": [p2],
- }
- }
- })
-
- parallel(lambda: mdns_scan(ssh),
- lambda: check(ns1, EXPR1, False),
- lambda: check(ns2, EXPR2, True),
- lambda: check(ns3, EXPR3, False))
+ # p1:no, p2:yes, p3:no
+ check([False, True, False], allow=[p2])
with test.step("Deny mDNS on a single interface: p2"):
- dut.delete_xpath("/infix-services:mdns/interfaces")
- dut.put_config_dict("infix-services", {
- "mdns": {
- "interfaces": {
- "deny": [p2],
- }
- }
- })
-
- parallel(lambda: mdns_scan(ssh),
- lambda: check(ns1, EXPR1, True),
- lambda: check(ns2, EXPR2, False),
- lambda: check(ns3, EXPR3, True))
+ # p1:yes, p2:no, p3:yes
+ check([True, False, True], deny=[p2])
with test.step("Allow mDNS on p1, p3 deny on p2, p3"):
- dut.delete_xpath("/infix-services:mdns/interfaces")
- dut.put_config_dict("infix-services", {
- "mdns": {
- "interfaces": {
- "allow": [p1, p3],
- "deny": [p2, p3],
- }
- }
- })
-
- parallel(lambda: mdns_scan(ssh),
- lambda: check(ns1, EXPR1, True),
- lambda: check(ns2, EXPR2, False),
- lambda: check(ns3, EXPR3, False))
+ # p1:yes, p2:no, p3:no (deny overrides allow)
+ check([True, False, False], allow=[p1, p3], deny=[p2, p3])
test.succeed()
diff --git a/test/case/meta/bootorder.py b/test/case/meta/bootorder.py
new file mode 100755
index 000000000..a82d4c7b1
--- /dev/null
+++ b/test/case/meta/bootorder.py
@@ -0,0 +1,24 @@
+#!/usr/bin/env python3
+import infamy
+
+with infamy.Test() as test:
+ with test.step("Discover topology and attach to available DUTs"):
+ env = infamy.Env(False)
+ ctrl = env.ptop.get_ctrl()
+ duts = {}
+ duts_state = {}
+ for ix in env.ptop.get_infixen():
+ cport, ixport = env.ptop.get_mgmt_link(ctrl, ix)
+ print(f"Attaching to {ix}:{ixport} via {ctrl}:{cport}")
+ duts[ix] = env.attach(ix, ixport)
+
+ with test.step("Verify bootorder"):
+ for name, tgt in duts.items():
+ expected = env.ptop.get_expected_boot(name)
+ running = tgt.get_data("/ietf-system:system-state")
+ running = running['system-state']['software']['booted']
+ print(f"{name}: booted: {running} expected: {expected}")
+
+ if running != expected:
+ test.fail()
+ test.succeed()
diff --git a/test/case/meta/check-version.py b/test/case/meta/check-version.py
new file mode 100755
index 000000000..1ec8570a3
--- /dev/null
+++ b/test/case/meta/check-version.py
@@ -0,0 +1,24 @@
+#!/usr/bin/env python3
+import infamy
+import os
+
+with infamy.Test() as test:
+ with test.step("Discover topology and attach to available DUTs"):
+ env = infamy.Env(False)
+ ctrl = env.ptop.get_ctrl()
+ duts = {}
+ duts_state = {}
+ for ix in env.ptop.get_infixen():
+ cport, ixport = env.ptop.get_mgmt_link(ctrl, ix)
+ print(f"Attaching to {ix}:{ixport} via {ctrl}:{cport}")
+ duts[ix] = env.attach(ix, ixport)
+
+ with test.step("Verify software version"):
+ expected=os.environ.get("VERSION")
+ for name, tgt in duts.items():
+ running = tgt.get_data("/ietf-system:system-state")
+ running = running['system-state']['platform']['os-version']
+ print(f"{name}: booted: {running} expected {expected}")
+ if running != expected:
+ test.fail()
+ test.succeed()
diff --git a/test/case/meta/reproducible.py b/test/case/meta/reproducible.py
index e6f68515a..6e2ad565a 100755
--- a/test/case/meta/reproducible.py
+++ b/test/case/meta/reproducible.py
@@ -13,27 +13,4 @@
else:
print(f"Specify PYTHONHASHSEED={seed} to reproduce this test environment")
- with test.step("Discover topology and attach to available DUTs"):
- env = infamy.Env(False)
- ctrl = env.ptop.get_ctrl()
-
- duts = {}
- for ix in env.ptop.get_infixen():
- cport, ixport = env.ptop.get_mgmt_link(ctrl, ix)
- print(f"Attaching to {ix}:{ixport} via {ctrl}:{cport}")
- duts[ix] = env.attach(ix, ixport)
-
- with test.step("Log running software versions"):
- for name, tgt in duts.items():
- sys = tgt.get_data("/ietf-system:system-state")
- sw = sys["system-state"]["software"]
- plt = sys["system-state"]["platform"]
-
- print(f"{name}:")
- for k,v in plt.items():
- print(f" {k:<16s} {v}")
-
- for k in ("compatible", "booted"):
- print(f" {k:<16s} {sw[k]}")
-
test.succeed()
diff --git a/test/env b/test/env
index 10eed98fe..b5e651a5d 100755
--- a/test/env
+++ b/test/env
@@ -184,6 +184,7 @@ while getopts "b:cCDf:hiKp:q:rt:" opt; do
kvm=
;;
p)
+ version=$(unsquashfs -cat $OPTARG manifest.raucm | awk -F'=' '/^version=/ {print $2}')
INFAMY_ARGS="$INFAMY_ARGS -p $OPTARG"
;;
q)
@@ -234,6 +235,7 @@ if [ "$containerize" ]; then
--env NINEPM_PROJ_CONFIG="$NINEPM_PROJ_CONFIG" \
--env QENETH_PATH="$testdir/templates:$testdir" \
--env PS1="$(build_ps1)" \
+ --env VERSION="$version" \
$extra_env \
--expose 9001-9010 --publish-all \
-v "$HOME/.infix/.ash_history":/root/.ash_history \
diff --git a/test/infamy/topology.py b/test/infamy/topology.py
index 00f3efcfb..2332945ea 100644
--- a/test/infamy/topology.py
+++ b/test/infamy/topology.py
@@ -131,6 +131,13 @@ def get_password(self, node):
return qstrip(password) if password is not None else "admin"
+ def get_expected_boot(self, node):
+ n = self.dotg.get_node(node)
+ b = n[0] if n else {}
+ boot = b.get("expected_boot")
+
+ return _qstrip(boot)
+
def get_link(self, src, dst, flt=lambda _: True):
es = self.g.get_edge_data(src, dst)
for e in es.values():
@@ -150,7 +157,6 @@ def get_ctrl(self):
def get_infixen(self):
return self.get_nodes(lambda _, attrs: compatible(attrs, {"requires": {"infix"}}))
-
def get_attr(self, name, default=None):
return _qstrip(self.dotg.get_attributes().get(name, default))
diff --git a/test/test.mk b/test/test.mk
index 53c3391d6..0d75fb182 100644
--- a/test/test.mk
+++ b/test/test.mk
@@ -14,8 +14,7 @@ mode-qeneth := -q $(test-dir)/virt/quad
mode-host := -t $(or $(TOPOLOGY),/etc/infamy.dot)
mode-run := -t $(BINARIES_DIR)/qemu.dot
mode := $(mode-$(TEST_MODE))
-
-INFIX_IMAGE_ID := $(call qstrip,$(INFIX_IMAGE_ID))
+INFIX_IMAGE_ID := $(call qstrip,$(INFIX_IMAGE_ID))
binaries-$(ARCH) := $(addprefix $(INFIX_IMAGE_ID),.img -disk.qcow2)
pkg-$(ARCH) := -p $(O)/images/$(addprefix $(INFIX_IMAGE_ID),.pkg)
binaries-x86_64 += OVMF.fd
diff --git a/test/virt/dual/topology.dot.in b/test/virt/dual/topology.dot.in
index 3ce0fab1b..736f818c0 100644
--- a/test/virt/dual/topology.dot.in
+++ b/test/virt/dual/topology.dot.in
@@ -25,6 +25,7 @@ graph "dual" {
label="{ e1 | e2 | e3 } | dut1 | { e4 | e5 | e6 }",
pos="10,18!",
provides="infix",
+ expected_boot="primary",
qn_console=9001,
qn_mem="384M",
qn_usb="dut1.usb"
@@ -33,6 +34,7 @@ graph "dual" {
label="{ e1 | e2 | e3 } | dut2 | { e4 | e5 | e6 }",
pos="10,12!",
provides="infix",
+ expected_boot="primary",
qn_console=9002,
qn_mem="384M",
qn_usb="dut2.usb"
diff --git a/test/virt/quad/topology.dot.in b/test/virt/quad/topology.dot.in
index a2b3fb100..549456d49 100644
--- a/test/virt/quad/topology.dot.in
+++ b/test/virt/quad/topology.dot.in
@@ -24,6 +24,7 @@ graph "quad" {
label="{ e1 | e2 | e3 | e4 } | dut1 | { e5 | e6 | e7 | e8}",
pos="10,30!",
provides="infix",
+ expected_boot="primary",
qn_console=9001,
qn_mem="384M",
qn_usb="dut1.usb"
@@ -32,6 +33,7 @@ graph "quad" {
label="{ e1 | e2 | e3 | e4 } | dut2 | { e5 | e6 | e7 | e8}",
pos="0,20!",
provides="infix",
+ expected_boot="primary",
qn_console=9002,
qn_mem="384M",
qn_usb="dut2.usb"
@@ -40,6 +42,7 @@ graph "quad" {
label="{ e1 | e2 | e3 | e4 } | dut3 | { e5 | e6 | e7 | e8}",
pos="0,10!",
provides="infix",
+ expected_boot="primary",
qn_console=9003,
qn_mem="384M",
qn_usb="dut3.usb"
@@ -49,6 +52,7 @@ graph "quad" {
label="{ e1 | e2 | e3 | e4 } | dut4 | { e5 | e6 | e7 | e8}",
pos="10,0!",
provides="infix",
+ expected_boot="primary",
qn_console=9004,
qn_mem="384M",
qn_usb="dut4.usb"