Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 4 additions & 9 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ env:
jobs:
test:
name: Regression Test ${{ github.event_name == 'workflow_dispatch' && github.event.inputs.name || inputs.name }} ${{ github.event_name == 'workflow_dispatch' && github.event.inputs.target || inputs.target }}
runs-on: [ self-hosted, regression ]
runs-on: [self-hosted, regression]
steps:
- name: Checkout infix repo
uses: actions/checkout@v4
Expand Down Expand Up @@ -87,7 +87,7 @@ jobs:
fi
make test

- name: Publish Test Result for ${{ env.TARGET }}
- name: Publish Test Result for ${{ env.TARGET }}
# Ensure this runs even if Regression Test fails
if: always()
run: cat $TEST_PATH/.log/last/result-gh.md >> $GITHUB_STEP_SUMMARY
Expand All @@ -96,15 +96,10 @@ jobs:
# Ensure this runs even if Regression Test fails
if: always()
run: |
asciidoctor-pdf \
--theme $TEST_PATH/9pm/report/theme.yml \
-a pdf-fontsdir=$TEST_PATH/9pm/report/fonts \
$TEST_PATH/.log/last/report.adoc \
-o $TEST_PATH/.log/last/report.pdf
make test-dir="$(pwd)/$TEST_PATH" test-report

- name: Upload Test Report as Artifact
uses: actions/upload-artifact@v4
with:
name: test-report
path: ${{ env.TEST_PATH }}/.log/last/report.pdf

path: output/images/test-report.pdf
2 changes: 1 addition & 1 deletion .github/workflows/trigger.yml
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,6 @@ jobs:
name: "infix"

test-publish-x86_64:
if: startsWith(github.repository, 'kernelkit/')
if: ${{ github.repository_owner == 'kernelkit' && github.ref_name == 'main' }}
needs: test-run-x86_64
uses: ./.github/workflows/publish.yml
4 changes: 2 additions & 2 deletions board/common/mkdisk.sh
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ genboot()
{
if [ -d "$bootdata" ]; then
bootimg=$(cat <<EOF
image efi-part.vfat {
image $BINARIES_DIR/efi-part.vfat {
size = $bootsize
vfat {
file EFI {
Expand All @@ -98,7 +98,7 @@ EOF
offset = $bootoffs
partition-type-uuid = U
bootable = true
image = efi-part.vfat
image = $BINARIES_DIR/efi-part.vfat
}
EOF
)
Expand Down
6 changes: 3 additions & 3 deletions doc/ChangeLog.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ Change Log

All notable changes to the project are documented in this file.

[v25.08.1][] - 2025-09-30
[v25.08.1][] - 2025-10-03
-------------------------

### Changes
Expand All @@ -12,8 +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
- Fix #1169: Expected OSPF neighbors not shown in `sysrepocfg` when the
system has at least one non-OSPF interface

[v25.08.0][] - 2025-09-01
-------------------------
Expand Down
11 changes: 9 additions & 2 deletions test/case/ietf_routing/ospf_basic/ospf_basic.adoc
Original file line number Diff line number Diff line change
@@ -1,7 +1,13 @@
=== OSPF Basic
==== Description
Very basic OSPF test just test that OSPF sends HELLO packets between the DUTs
and that they exchange routes, ending with a simple connectivity check.
Verifies basic OSPF functionality by configuring two routers (R1 and R2)
with OSPF on their interconnecting link. The test ensures OSPF
neighbors are established, routes are exchanged between the routers, and
end-to-end connectivity is achieved.

An end-device (HOST) is connected to R2 on an interface without OSPF enabled.
This verifies that OSPF status information remains accessible when a router
has non-OSPF interfaces.

==== Topology
ifdef::topdoc[]
Expand All @@ -19,6 +25,7 @@ endif::topdoc[]
. Set up topology and attach to target DUTs
. Configure targets
. Wait for OSPF routes
. Verify R2 OSPF neighbors with non-OSPF interface
. Test connectivity from PC:data to 192.168.200.1


Expand Down
168 changes: 105 additions & 63 deletions test/case/ietf_routing/ospf_basic/test.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,24 @@
#!/usr/bin/env python3
"""
OSPF Basic
"""OSPF Basic

Verifies basic OSPF functionality by configuring two routers (R1 and R2)
with OSPF on their interconnecting link. The test ensures OSPF
neighbors are established, routes are exchanged between the routers, and
end-to-end connectivity is achieved.

An end-device (HOST) is connected to R2 on an interface without OSPF enabled.
This verifies that OSPF status information remains accessible when a router
has non-OSPF interfaces.

Very basic OSPF test just test that OSPF sends HELLO packets between the DUTs
and that they exchange routes, ending with a simple connectivity check.
"""

# TODO: Remove HOST node once Infamy supports unconnected ports in topologies

import infamy
import infamy.route as route
from infamy.util import until, parallel


def config_target1(target, data, link):
target.put_config_dict("ietf-interfaces", {
"interfaces": {
Expand All @@ -30,8 +39,8 @@ def config_target1(target, data, link):
"ipv4": {
"forwarding": True,
"address": [{
"ip": "192.168.50.1",
"prefix-length": 24
"ip": "192.168.50.1",
"prefix-length": 24
}]
}
},
Expand All @@ -40,8 +49,8 @@ def config_target1(target, data, link):
"enabled": True,
"ipv4": {
"address": [{
"ip": "192.168.100.1",
"prefix-length": 32
"ip": "192.168.100.1",
"prefix-length": 32
}]
}
}
Expand All @@ -56,38 +65,34 @@ def config_target1(target, data, link):
target.put_config_dict("ietf-routing", {
"routing": {
"control-plane-protocols": {
"control-plane-protocol": [
{
"type": "infix-routing:static",
"name": "default",
"static-routes": {
"ipv4": {
"route": [{
"destination-prefix": "192.168.33.1/32",
"next-hop": {
"special-next-hop": "blackhole"
}
}]
}
"control-plane-protocol": [{
"type": "infix-routing:static",
"name": "default",
"static-routes": {
"ipv4": {
"route": [{
"destination-prefix": "192.168.33.1/32",
"next-hop": {
"special-next-hop": "blackhole"
}
}]
}
},
{
}
}, {
"type": "infix-routing:ospfv2",
"name": "default",
"ospf": {
"redistribute": {
"redistribute": [{
"protocol": "static"
},
{
}, {
"protocol": "connected"
}]
},
"areas": {
"area": [{
"area-id": "0.0.0.0",
"interfaces":
{
"interfaces": {
"interface": [{
"enabled": True,
"name": link,
Expand All @@ -103,35 +108,43 @@ def config_target1(target, data, link):
}
})

def config_target2(target, link):

def config_target2(target, link, data):
target.put_config_dict("ietf-interfaces", {
"interfaces": {
"interface": [
{
"name": link,
"enabled": True,
"ipv4": {
"forwarding": True,
"address": [{
"ip": "192.168.50.2",
"prefix-length": 24
}]
}
},
{
"name": "lo",
"enabled": True,
"forwarding": True,
"ipv4": {
"address": [{
"ip": "192.168.200.1",
"prefix-length": 32
}]
}
}
]
}
})
"interfaces": {
"interface": [{
"name": link,
"enabled": True,
"ipv4": {
"forwarding": True,
"address": [{
"ip": "192.168.50.2",
"prefix-length": 24
}]
}
}, {
"name": data,
"enabled": True,
"ipv4": {
"forwarding": True,
"address": [{
"ip": "192.168.60.1",
"prefix-length": 24
}]
}
}, {
"name": "lo",
"enabled": True,
"forwarding": True,
"ipv4": {
"address": [{
"ip": "192.168.200.1",
"prefix-length": 32
}]
}
}]
}
})

target.put_config_dict("ietf-system", {
"system": {
Expand All @@ -141,8 +154,7 @@ def config_target2(target, link):
target.put_config_dict("ietf-routing", {
"routing": {
"control-plane-protocols": {
"control-plane-protocol": [
{
"control-plane-protocol": [{
"type": "infix-routing:ospfv2",
"name": "default",
"ospf": {
Expand All @@ -154,7 +166,7 @@ def config_target2(target, link):
"areas": {
"area": [{
"area-id": "0.0.0.0",
"interfaces":{
"interfaces": {
"interface": [{
"enabled": True,
"name": link,
Expand All @@ -165,31 +177,61 @@ def config_target2(target, link):
}]
}
}
}
]
}]
}
}
})


def config_host(target, link):
target.put_config_dict("ietf-interfaces", {
"interfaces": {
"interface": [{
"name": link,
"enabled": True,
"ipv4": {
"address": [{
"ip": "192.168.60.2",
"prefix-length": 24
}]
}
}]
}
})

target.put_config_dict("ietf-system", {
"system": {
"hostname": "HOST"
}
})


with infamy.Test() as test:
with test.step("Set up topology and attach to target DUTs"):
env = infamy.Env()
R1 = env.attach("R1", "mgmt")
R2 = env.attach("R2", "mgmt")
HOST = env.attach("HOST", "mgmt")

with test.step("Configure targets"):
_, R1data = env.ltop.xlate("R1", "data")
_, R2link = env.ltop.xlate("R2", "link")
_, R1link= env.ltop.xlate("R1", "link")
_, R1link = env.ltop.xlate("R1", "link")
_, R2data = env.ltop.xlate("R2", "data")
_, HOSTlink = env.ltop.xlate("HOST", "link")

parallel(config_target1(R1, R1data, R1link),
config_target2(R2, R2link))
config_target2(R2, R2link, R2data),
config_host(HOST, HOSTlink))
with test.step("Wait for OSPF routes"):
print("Waiting for OSPF routes..")
until(lambda: route.ipv4_route_exist(R1, "192.168.200.1/32", proto="ietf-ospf:ospfv2"), attempts=200)
until(lambda: route.ipv4_route_exist(R2, "192.168.100.1/32", proto="ietf-ospf:ospfv2"), attempts=200)
until(lambda: route.ipv4_route_exist(R2, "192.168.33.1/32", proto="ietf-ospf:ospfv2"), attempts=200)

with test.step("Verify R2 OSPF neighbors with non-OSPF interface"):
# Regression test for #1169
assert route.ospf_has_neighbors(R2)

with test.step("Test connectivity from PC:data to 192.168.200.1"):
_, hport0 = env.ltop.xlate("PC", "data")
with infamy.IsolatedMacVlan(hport0) as ns0:
Expand Down
Loading