Merge pull request #1184 from kernelkit/releng

Release prep for v25.08.1
This commit is contained in:
Joachim Wiberg
2025-10-03 16:22:07 +02:00
committed by GitHub
14 changed files with 286 additions and 149 deletions
+3 -8
View File
@@ -39,7 +39,7 @@ env:
jobs: jobs:
test: 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 }} 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: steps:
- name: Checkout infix repo - name: Checkout infix repo
uses: actions/checkout@v4 uses: actions/checkout@v4
@@ -96,15 +96,10 @@ jobs:
# Ensure this runs even if Regression Test fails # Ensure this runs even if Regression Test fails
if: always() if: always()
run: | run: |
asciidoctor-pdf \ make test-dir="$(pwd)/$TEST_PATH" test-report
--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
- name: Upload Test Report as Artifact - name: Upload Test Report as Artifact
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v4
with: with:
name: test-report name: test-report
path: ${{ env.TEST_PATH }}/.log/last/report.pdf path: output/images/test-report.pdf
+1 -1
View File
@@ -37,6 +37,6 @@ jobs:
name: "infix" name: "infix"
test-publish-x86_64: test-publish-x86_64:
if: startsWith(github.repository, 'kernelkit/') if: ${{ github.repository_owner == 'kernelkit' && github.ref_name == 'main' }}
needs: test-run-x86_64 needs: test-run-x86_64
uses: ./.github/workflows/publish.yml uses: ./.github/workflows/publish.yml
+2 -2
View File
@@ -83,7 +83,7 @@ genboot()
{ {
if [ -d "$bootdata" ]; then if [ -d "$bootdata" ]; then
bootimg=$(cat <<EOF bootimg=$(cat <<EOF
image efi-part.vfat { image $BINARIES_DIR/efi-part.vfat {
size = $bootsize size = $bootsize
vfat { vfat {
file EFI { file EFI {
@@ -98,7 +98,7 @@ EOF
offset = $bootoffs offset = $bootoffs
partition-type-uuid = U partition-type-uuid = U
bootable = true bootable = true
image = efi-part.vfat image = $BINARIES_DIR/efi-part.vfat
} }
EOF EOF
) )
+3 -3
View File
@@ -3,7 +3,7 @@ Change Log
All notable changes to the project are documented in this file. All notable changes to the project are documented in this file.
[v25.08.1][] - 2025-09-30 [v25.08.1][] - 2025-10-03
------------------------- -------------------------
### Changes ### Changes
@@ -12,8 +12,8 @@ All notable changes to the project are documented in this file.
### Fixes ### Fixes
- Fix #1150: `show-legacy` wrapper permissions - Fix #1150: `show-legacy` wrapper permissions
- Fix #1155: `show ospf` commands regression - Fix #1155: `show ospf` commands regression
- Fix #1169: Expected neighbors not shown in sysrepocfg - Fix #1169: Expected OSPF neighbors not shown in `sysrepocfg` when the
- Various test stability improvments system has at least one non-OSPF interface
[v25.08.0][] - 2025-09-01 [v25.08.0][] - 2025-09-01
------------------------- -------------------------
@@ -1,7 +1,13 @@
=== OSPF Basic === OSPF Basic
==== Description ==== Description
Very basic OSPF test just test that OSPF sends HELLO packets between the DUTs Verifies basic OSPF functionality by configuring two routers (R1 and R2)
and that they exchange routes, ending with a simple connectivity check. 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 ==== Topology
ifdef::topdoc[] ifdef::topdoc[]
@@ -19,6 +25,7 @@ endif::topdoc[]
. Set up topology and attach to target DUTs . Set up topology and attach to target DUTs
. Configure targets . Configure targets
. Wait for OSPF routes . Wait for OSPF routes
. Verify R2 OSPF neighbors with non-OSPF interface
. Test connectivity from PC:data to 192.168.200.1 . Test connectivity from PC:data to 192.168.200.1
+105 -63
View File
@@ -1,15 +1,24 @@
#!/usr/bin/env python3 #!/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
import infamy.route as route import infamy.route as route
from infamy.util import until, parallel from infamy.util import until, parallel
def config_target1(target, data, link): def config_target1(target, data, link):
target.put_config_dict("ietf-interfaces", { target.put_config_dict("ietf-interfaces", {
"interfaces": { "interfaces": {
@@ -30,8 +39,8 @@ def config_target1(target, data, link):
"ipv4": { "ipv4": {
"forwarding": True, "forwarding": True,
"address": [{ "address": [{
"ip": "192.168.50.1", "ip": "192.168.50.1",
"prefix-length": 24 "prefix-length": 24
}] }]
} }
}, },
@@ -40,8 +49,8 @@ def config_target1(target, data, link):
"enabled": True, "enabled": True,
"ipv4": { "ipv4": {
"address": [{ "address": [{
"ip": "192.168.100.1", "ip": "192.168.100.1",
"prefix-length": 32 "prefix-length": 32
}] }]
} }
} }
@@ -56,38 +65,34 @@ def config_target1(target, data, link):
target.put_config_dict("ietf-routing", { target.put_config_dict("ietf-routing", {
"routing": { "routing": {
"control-plane-protocols": { "control-plane-protocols": {
"control-plane-protocol": [ "control-plane-protocol": [{
{ "type": "infix-routing:static",
"type": "infix-routing:static", "name": "default",
"name": "default", "static-routes": {
"static-routes": { "ipv4": {
"ipv4": { "route": [{
"route": [{ "destination-prefix": "192.168.33.1/32",
"destination-prefix": "192.168.33.1/32", "next-hop": {
"next-hop": { "special-next-hop": "blackhole"
"special-next-hop": "blackhole" }
} }]
}]
}
} }
}, }
{ }, {
"type": "infix-routing:ospfv2", "type": "infix-routing:ospfv2",
"name": "default", "name": "default",
"ospf": { "ospf": {
"redistribute": { "redistribute": {
"redistribute": [{ "redistribute": [{
"protocol": "static" "protocol": "static"
}, }, {
{
"protocol": "connected" "protocol": "connected"
}] }]
}, },
"areas": { "areas": {
"area": [{ "area": [{
"area-id": "0.0.0.0", "area-id": "0.0.0.0",
"interfaces": "interfaces": {
{
"interface": [{ "interface": [{
"enabled": True, "enabled": True,
"name": link, "name": link,
@@ -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", { target.put_config_dict("ietf-interfaces", {
"interfaces": { "interfaces": {
"interface": [ "interface": [{
{ "name": link,
"name": link, "enabled": True,
"enabled": True, "ipv4": {
"ipv4": { "forwarding": True,
"forwarding": True, "address": [{
"address": [{ "ip": "192.168.50.2",
"ip": "192.168.50.2", "prefix-length": 24
"prefix-length": 24 }]
}] }
} }, {
}, "name": data,
{ "enabled": True,
"name": "lo", "ipv4": {
"enabled": True, "forwarding": True,
"forwarding": True, "address": [{
"ipv4": { "ip": "192.168.60.1",
"address": [{ "prefix-length": 24
"ip": "192.168.200.1", }]
"prefix-length": 32 }
}] }, {
} "name": "lo",
} "enabled": True,
] "forwarding": True,
} "ipv4": {
}) "address": [{
"ip": "192.168.200.1",
"prefix-length": 32
}]
}
}]
}
})
target.put_config_dict("ietf-system", { target.put_config_dict("ietf-system", {
"system": { "system": {
@@ -141,8 +154,7 @@ def config_target2(target, link):
target.put_config_dict("ietf-routing", { target.put_config_dict("ietf-routing", {
"routing": { "routing": {
"control-plane-protocols": { "control-plane-protocols": {
"control-plane-protocol": [ "control-plane-protocol": [{
{
"type": "infix-routing:ospfv2", "type": "infix-routing:ospfv2",
"name": "default", "name": "default",
"ospf": { "ospf": {
@@ -154,7 +166,7 @@ def config_target2(target, link):
"areas": { "areas": {
"area": [{ "area": [{
"area-id": "0.0.0.0", "area-id": "0.0.0.0",
"interfaces":{ "interfaces": {
"interface": [{ "interface": [{
"enabled": True, "enabled": True,
"name": link, "name": link,
@@ -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 infamy.Test() as test:
with test.step("Set up topology and attach to target DUTs"): with test.step("Set up topology and attach to target DUTs"):
env = infamy.Env() env = infamy.Env()
R1 = env.attach("R1", "mgmt") R1 = env.attach("R1", "mgmt")
R2 = env.attach("R2", "mgmt") R2 = env.attach("R2", "mgmt")
HOST = env.attach("HOST", "mgmt")
with test.step("Configure targets"): with test.step("Configure targets"):
_, R1data = env.ltop.xlate("R1", "data") _, R1data = env.ltop.xlate("R1", "data")
_, R2link = env.ltop.xlate("R2", "link") _, 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), 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"): 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(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.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) 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"): with test.step("Test connectivity from PC:data to 192.168.200.1"):
_, hport0 = env.ltop.xlate("PC", "data") _, hport0 = env.ltop.xlate("PC", "data")
with infamy.IsolatedMacVlan(hport0) as ns0: with infamy.IsolatedMacVlan(hport0) as ns0:
+13 -5
View File
@@ -8,26 +8,34 @@ graph "2x2" {
edge [color="cornflowerblue", penwidth="2", fontname="DejaVu Serif, Book"]; edge [color="cornflowerblue", penwidth="2", fontname="DejaVu Serif, Book"];
PC [ PC [
label="PC | { <mgmt1> mgmt1 | <data> data | <mgmt2> mgmt2 }", label="PC | { <mgmt1> mgmt1 | <data> data | <mgmt2> mgmt2 | <mgmt3> mgmt3 }",
pos="20,80!", pos="20,30!",
requires="controller", requires="controller",
]; ];
R1 [ R1 [
label="{ <mgmt> mgmt | <data> data | <link> link} | R1 \n 192.168.100.1/32 \n(lo)", label="{ <mgmt> mgmt | <data> data | <link> link} | R1 \n 192.168.100.1/32 \n(lo)",
pos="250,80!", pos="160,60!",
requires="infix", requires="infix",
]; ];
R2 [ R2 [
label="{ <link> link | <mgmt> mgmt | <data> data } | R2 \n 192.168.200.1/32 \n(lo)", label="{ <link> link | <mgmt> mgmt | <data> data } | R2 \n 192.168.200.1/32 \n(lo)",
pos="250,30!", pos="160,30!",
requires="infix",
];
HOST [
label="{ <link> link | <mgmt> mgmt } | HOST \n end-device",
pos="153,0!",
requires="infix", requires="infix",
]; ];
PC:mgmt1 -- R1:mgmt [requires="mgmt", color="lightgray"] PC:mgmt1 -- R1:mgmt [requires="mgmt", color="lightgray"]
PC:mgmt2 -- R2:mgmt [requires="mgmt", color="lightgray"] PC:mgmt2 -- R2:mgmt [requires="mgmt", color="lightgray"]
PC:data -- R1:data [color="black", headlabel="192.168.10.1/24", taillabel="192.168.10.2/24", fontcolor="black"] PC:mgmt3 -- HOST:mgmt [requires="mgmt", color="lightgray"]
PC:data -- R1:data [color="black", headlabel="192.168.10.1/24", taillabel="192.168.10.2/24", labeldistance=6, fontcolor="black"]
R1:link -- R2:link [headlabel="192.168.50.2/24", taillabel="192.168.50.1/24", labeldistance=1, fontcolor="black", color="black"] R1:link -- R2:link [headlabel="192.168.50.2/24", taillabel="192.168.50.1/24", labeldistance=1, fontcolor="black", color="black"]
R2:data -- HOST:link [headlabel="192.168.60.2/24", taillabel="192.168.60.1/24", labeldistance=1, fontcolor="black", color="black"]
} }
+62 -37
View File
@@ -3,74 +3,99 @@
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<!-- Title: 2x2 Pages: 1 --> <!-- Title: 2x2 Pages: 1 -->
<svg width="713pt" height="198pt" <svg width="720pt" height="306pt"
viewBox="0.00 0.00 713.06 198.01" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"> viewBox="0.00 0.00 720.00 306.08" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<g id="graph0" class="graph" transform="scale(1 1) rotate(0) translate(4 194.01)"> <g id="graph0" class="graph" transform="scale(1 1) rotate(0) translate(4 302.52)">
<title>2x2</title> <title>2x2</title>
<polygon fill="white" stroke="transparent" points="-4,4 -4,-194.01 709.06,-194.01 709.06,4 -4,4"/> <polygon fill="white" stroke="transparent" points="-4,4 -4,-302.52 717.06,-302.52 717.06,4 -4,4"/>
<!-- PC --> <!-- PC -->
<g id="node1" class="node"> <g id="node1" class="node">
<title>PC</title> <title>PC</title>
<polygon fill="none" stroke="black" points="0,-120.51 0,-189.51 91,-189.51 91,-120.51 0,-120.51"/> <polygon fill="none" stroke="black" points="0,-97.51 0,-189.51 91,-189.51 91,-97.51 0,-97.51"/>
<text text-anchor="middle" x="16.5" y="-151.31" font-family="DejaVu Sans Mono, Book" font-size="14.00">PC</text> <text text-anchor="middle" x="16.5" y="-139.81" font-family="DejaVu Sans Mono, Book" font-size="14.00">PC</text>
<polyline fill="none" stroke="black" points="33,-120.51 33,-189.51 "/> <polyline fill="none" stroke="black" points="33,-97.51 33,-189.51 "/>
<text text-anchor="middle" x="62" y="-174.31" font-family="DejaVu Sans Mono, Book" font-size="14.00">mgmt1</text> <text text-anchor="middle" x="62" y="-174.31" font-family="DejaVu Sans Mono, Book" font-size="14.00">mgmt1</text>
<polyline fill="none" stroke="black" points="33,-166.51 91,-166.51 "/> <polyline fill="none" stroke="black" points="33,-166.51 91,-166.51 "/>
<text text-anchor="middle" x="62" y="-151.31" font-family="DejaVu Sans Mono, Book" font-size="14.00">data</text> <text text-anchor="middle" x="62" y="-151.31" font-family="DejaVu Sans Mono, Book" font-size="14.00">data</text>
<polyline fill="none" stroke="black" points="33,-143.51 91,-143.51 "/> <polyline fill="none" stroke="black" points="33,-143.51 91,-143.51 "/>
<text text-anchor="middle" x="62" y="-128.31" font-family="DejaVu Sans Mono, Book" font-size="14.00">mgmt2</text> <text text-anchor="middle" x="62" y="-128.31" font-family="DejaVu Sans Mono, Book" font-size="14.00">mgmt2</text>
<polyline fill="none" stroke="black" points="33,-120.51 91,-120.51 "/>
<text text-anchor="middle" x="62" y="-105.31" font-family="DejaVu Sans Mono, Book" font-size="14.00">mgmt3</text>
</g> </g>
<!-- R1 --> <!-- R1 -->
<g id="node2" class="node"> <g id="node2" class="node">
<title>R1</title> <title>R1</title>
<polygon fill="none" stroke="black" points="490.06,-120.51 490.06,-189.51 705.06,-189.51 705.06,-120.51 490.06,-120.51"/> <polygon fill="none" stroke="black" points="498.06,-229.02 498.06,-298.02 713.06,-298.02 713.06,-229.02 498.06,-229.02"/>
<text text-anchor="middle" x="515.06" y="-174.31" font-family="DejaVu Sans Mono, Book" font-size="14.00">mgmt</text> <text text-anchor="middle" x="523.06" y="-282.82" font-family="DejaVu Sans Mono, Book" font-size="14.00">mgmt</text>
<polyline fill="none" stroke="black" points="490.06,-166.51 540.06,-166.51 "/> <polyline fill="none" stroke="black" points="498.06,-275.02 548.06,-275.02 "/>
<text text-anchor="middle" x="515.06" y="-151.31" font-family="DejaVu Sans Mono, Book" font-size="14.00">data</text> <text text-anchor="middle" x="523.06" y="-259.82" font-family="DejaVu Sans Mono, Book" font-size="14.00">data</text>
<polyline fill="none" stroke="black" points="490.06,-143.51 540.06,-143.51 "/> <polyline fill="none" stroke="black" points="498.06,-252.02 548.06,-252.02 "/>
<text text-anchor="middle" x="515.06" y="-128.31" font-family="DejaVu Sans Mono, Book" font-size="14.00">link</text> <text text-anchor="middle" x="523.06" y="-236.82" font-family="DejaVu Sans Mono, Book" font-size="14.00">link</text>
<polyline fill="none" stroke="black" points="540.06,-120.51 540.06,-189.51 "/> <polyline fill="none" stroke="black" points="548.06,-229.02 548.06,-298.02 "/>
<text text-anchor="middle" x="622.56" y="-166.31" font-family="DejaVu Sans Mono, Book" font-size="14.00">R1 </text> <text text-anchor="middle" x="630.56" y="-274.82" font-family="DejaVu Sans Mono, Book" font-size="14.00">R1 </text>
<text text-anchor="middle" x="622.56" y="-151.31" font-family="DejaVu Sans Mono, Book" font-size="14.00"> 192.168.100.1/32 </text> <text text-anchor="middle" x="630.56" y="-259.82" font-family="DejaVu Sans Mono, Book" font-size="14.00"> 192.168.100.1/32 </text>
<text text-anchor="middle" x="622.56" y="-136.31" font-family="DejaVu Sans Mono, Book" font-size="14.00">(lo)</text> <text text-anchor="middle" x="630.56" y="-244.82" font-family="DejaVu Sans Mono, Book" font-size="14.00">(lo)</text>
</g> </g>
<!-- PC&#45;&#45;R1 --> <!-- PC&#45;&#45;R1 -->
<g id="edge1" class="edge"> <g id="edge1" class="edge">
<title>PC:mgmt1&#45;&#45;R1:mgmt</title> <title>PC:mgmt1&#45;&#45;R1:mgmt</title>
<path fill="none" stroke="lightgray" stroke-width="2" d="M91.5,-178.01C91.5,-178.01 489.56,-178.01 489.56,-178.01"/> <path fill="none" stroke="lightgray" stroke-width="2" d="M91.5,-178.51C91.5,-178.51 497.56,-286.52 497.56,-286.52"/>
</g> </g>
<!-- PC&#45;&#45;R1 --> <!-- PC&#45;&#45;R1 -->
<g id="edge3" class="edge"> <g id="edge4" class="edge">
<title>PC:data&#45;&#45;R1:data</title> <title>PC:data&#45;&#45;R1:data</title>
<path fill="none" stroke="black" stroke-width="2" d="M91.5,-155.01C91.5,-155.01 489.56,-155.01 489.56,-155.01"/> <path fill="none" stroke="black" stroke-width="2" d="M91.5,-155.51C91.5,-155.51 497.56,-263.52 497.56,-263.52"/>
<text text-anchor="middle" x="430.56" y="-158.81" font-family="DejaVu Serif, Book" font-size="14.00">192.168.10.1/24</text> <text text-anchor="middle" x="438.49" y="-270.35" font-family="DejaVu Serif, Book" font-size="14.00">192.168.10.1/24</text>
<text text-anchor="middle" x="150.5" y="-158.81" font-family="DejaVu Serif, Book" font-size="14.00">192.168.10.2/24</text> <text text-anchor="middle" x="150.57" y="-141.29" font-family="DejaVu Serif, Book" font-size="14.00">192.168.10.2/24</text>
</g> </g>
<!-- R2 --> <!-- R2 -->
<g id="node3" class="node"> <g id="node3" class="node">
<title>R2</title> <title>R2</title>
<polygon fill="none" stroke="black" points="490.06,-0.5 490.06,-69.5 705.06,-69.5 705.06,-0.5 490.06,-0.5"/> <polygon fill="none" stroke="black" points="498.06,-109.01 498.06,-178.01 713.06,-178.01 713.06,-109.01 498.06,-109.01"/>
<text text-anchor="middle" x="515.06" y="-54.3" font-family="DejaVu Sans Mono, Book" font-size="14.00">link</text> <text text-anchor="middle" x="523.06" y="-162.81" font-family="DejaVu Sans Mono, Book" font-size="14.00">link</text>
<polyline fill="none" stroke="black" points="490.06,-46.5 540.06,-46.5 "/> <polyline fill="none" stroke="black" points="498.06,-155.01 548.06,-155.01 "/>
<text text-anchor="middle" x="515.06" y="-31.3" font-family="DejaVu Sans Mono, Book" font-size="14.00">mgmt</text> <text text-anchor="middle" x="523.06" y="-139.81" font-family="DejaVu Sans Mono, Book" font-size="14.00">mgmt</text>
<polyline fill="none" stroke="black" points="490.06,-23.5 540.06,-23.5 "/> <polyline fill="none" stroke="black" points="498.06,-132.01 548.06,-132.01 "/>
<text text-anchor="middle" x="515.06" y="-8.3" font-family="DejaVu Sans Mono, Book" font-size="14.00">data</text> <text text-anchor="middle" x="523.06" y="-116.81" font-family="DejaVu Sans Mono, Book" font-size="14.00">data</text>
<polyline fill="none" stroke="black" points="540.06,-0.5 540.06,-69.5 "/> <polyline fill="none" stroke="black" points="548.06,-109.01 548.06,-178.01 "/>
<text text-anchor="middle" x="622.56" y="-46.3" font-family="DejaVu Sans Mono, Book" font-size="14.00">R2 </text> <text text-anchor="middle" x="630.56" y="-154.81" font-family="DejaVu Sans Mono, Book" font-size="14.00">R2 </text>
<text text-anchor="middle" x="622.56" y="-31.3" font-family="DejaVu Sans Mono, Book" font-size="14.00"> 192.168.200.1/32 </text> <text text-anchor="middle" x="630.56" y="-139.81" font-family="DejaVu Sans Mono, Book" font-size="14.00"> 192.168.200.1/32 </text>
<text text-anchor="middle" x="622.56" y="-16.3" font-family="DejaVu Sans Mono, Book" font-size="14.00">(lo)</text> <text text-anchor="middle" x="630.56" y="-124.81" font-family="DejaVu Sans Mono, Book" font-size="14.00">(lo)</text>
</g> </g>
<!-- PC&#45;&#45;R2 --> <!-- PC&#45;&#45;R2 -->
<g id="edge2" class="edge"> <g id="edge2" class="edge">
<title>PC:mgmt2&#45;&#45;R2:mgmt</title> <title>PC:mgmt2&#45;&#45;R2:mgmt</title>
<path fill="none" stroke="lightgray" stroke-width="2" d="M91.5,-132.01C91.5,-132.01 489.56,-35 489.56,-35"/> <path fill="none" stroke="lightgray" stroke-width="2" d="M91.5,-131.51C91.5,-131.51 497.56,-143.51 497.56,-143.51"/>
</g>
<!-- HOST -->
<g id="node4" class="node">
<title>HOST</title>
<polygon fill="none" stroke="black" points="499.05,-0.5 499.05,-46.5 656.05,-46.5 656.05,-0.5 499.05,-0.5"/>
<text text-anchor="middle" x="524.05" y="-31.3" font-family="DejaVu Sans Mono, Book" font-size="14.00">link</text>
<polyline fill="none" stroke="black" points="499.05,-23.5 549.05,-23.5 "/>
<text text-anchor="middle" x="524.05" y="-8.3" font-family="DejaVu Sans Mono, Book" font-size="14.00">mgmt</text>
<polyline fill="none" stroke="black" points="549.05,-0.5 549.05,-46.5 "/>
<text text-anchor="middle" x="602.55" y="-27.3" font-family="DejaVu Sans Mono, Book" font-size="14.00">HOST </text>
<text text-anchor="middle" x="602.55" y="-12.3" font-family="DejaVu Sans Mono, Book" font-size="14.00"> end&#45;device</text>
</g>
<!-- PC&#45;&#45;HOST -->
<g id="edge3" class="edge">
<title>PC:mgmt3&#45;&#45;HOST:mgmt</title>
<path fill="none" stroke="lightgray" stroke-width="2" d="M91.5,-108.51C91.5,-108.51 498.55,-11.5 498.55,-11.5"/>
</g> </g>
<!-- R1&#45;&#45;R2 --> <!-- R1&#45;&#45;R2 -->
<g id="edge4" class="edge"> <g id="edge5" class="edge">
<title>R1:link&#45;&#45;R2:link</title> <title>R1:link&#45;&#45;R2:link</title>
<path fill="none" stroke="black" stroke-width="2" d="M514.56,-120.01C514.56,-120.01 514.56,-70 514.56,-70"/> <path fill="none" stroke="black" stroke-width="2" d="M522.56,-228.52C522.56,-228.52 522.56,-178.51 522.56,-178.51"/>
<text text-anchor="middle" x="518.78" y="-75.36" font-family="DejaVu Serif, Book" font-size="14.00">192.168.50.2/24</text> <text text-anchor="middle" x="526.78" y="-183.88" font-family="DejaVu Serif, Book" font-size="14.00">192.168.50.2/24</text>
<text text-anchor="middle" x="510.33" y="-107.25" font-family="DejaVu Serif, Book" font-size="14.00">192.168.50.1/24</text> <text text-anchor="middle" x="518.33" y="-215.76" font-family="DejaVu Serif, Book" font-size="14.00">192.168.50.1/24</text>
</g>
<!-- R2&#45;&#45;HOST -->
<g id="edge6" class="edge">
<title>R2:data&#45;&#45;HOST:link</title>
<path fill="none" stroke="black" stroke-width="2" d="M522.56,-108.51C522.56,-108.51 523.55,-46.5 523.55,-46.5"/>
<text text-anchor="middle" x="527.63" y="-51.93" font-family="DejaVu Serif, Book" font-size="14.00">192.168.60.2/24</text>
<text text-anchor="middle" x="518.48" y="-95.68" font-family="DejaVu Serif, Book" font-size="14.00">192.168.60.1/24</text>
</g> </g>
</g> </g>
</svg> </svg>

Before

Width:  |  Height:  |  Size: 4.8 KiB

After

Width:  |  Height:  |  Size: 6.5 KiB

@@ -26,7 +26,6 @@ endif::topdoc[]
. Set hostname to 'container-host' . Set hostname to 'container-host'
. Create enabled container from bundled OCI image . Create enabled container from bundled OCI image
. Verify container has started . Verify container has started
. Let container settle
. Disable container . Disable container
. Verify container has stopped . Verify container has stopped
. Re-enable container . Re-enable container
@@ -1,14 +1,11 @@
=== Container environment variables === Container environment variables
==== Description ==== Description
Verify that environment variables can be set in container configuration Verify that environment variables can be set in container configuration
and are available inside the running container. Tests the 'env' list and are available inside the running container.
functionality by:
1. Creating a container with multiple environment variables 1 Set up a container config with multiple environment variables
2. Using a custom script to extract env vars and serve them via HTTP 2. Serve variables back to host using a CGI script in container
3. Fetching the served content to verify environment variables are set correctly 3. Verify served content against environment variables
Uses the nftables container image with custom rc.local script.
==== Topology ==== Topology
ifdef::topdoc[] ifdef::topdoc[]
@@ -25,11 +22,10 @@ endif::topdoc[]
==== Test sequence ==== Test sequence
. Set up topology and attach to target DUT . Set up topology and attach to target DUT
. Configure data interface with static IPv4 . Configure data interface with static IPv4
. Create container with environment variables . Set up container with environment variables
. Verify container has started . Verify container has started
. Verify environment variables are available via HTTP
. Verify basic connectivity to data interface . Verify basic connectivity to data interface
. Verify environment variables in HTTP response . Verify environment variables in CGI response
<<< <<<
+55 -9
View File
@@ -1,26 +1,72 @@
"""Fugly URL fetcher""" """Simple HTTP URL fetcher and content checker
This module provides the a lightweight wrapper around urllib for testing
HTTP endpoints. It's designed specifically for test scenarios where you
need to:
- Fetch HTTP content and verify it contains expected strings
- Perform checks from within network namespaces
- Validate multiple content patterns in a single request
- Handle network errors gracefully in test environments
Example usage:
# Single needle check
url = Furl("http://example.com/api")
if url.check("success"):
print("API returned success")
# Multiple needle check (all must match)
if url.check(["user: alice", "status: active", "role: admin"]):
print("All user details found")
# Check from network namespace
with IsolatedMacVlan("eth0") as ns:
if url.nscheck(ns, "expected content"):
print("Content verified from namespace")
"""
import urllib.error import urllib.error
import urllib.parse
import urllib.request import urllib.request
class Furl: class Furl:
"""Furl wraps urllib in a way similar to curl""" """Furl wraps urllib in a way similar to curl"""
def __init__(self, url): def __init__(self, url):
"""Create new URL checker""" """Create new URL checker"""
self.url = urllib.parse.quote(url, safe='/:') self.url = urllib.parse.quote(url, safe='/:')
def check(self, needle, timeout=10): def check(self, needles, timeout=10):
"""Connect to web server URL, fetch body and check for needle""" """Connect to web server URL, fetch body and check for needle(s)
Args:
needles: String or list of strings to search for in response
timeout: Request timeout in seconds
Returns:
bool: True if all needles found in response, False otherwise
"""
# Backwards compat, make needles a list for uniform processing
if isinstance(needles, str):
needles = [needles]
try: try:
with urllib.request.urlopen(self.url, timeout=timeout) as response: with urllib.request.urlopen(self.url, timeout=timeout) as response:
text = response.read().decode('utf-8') text = response.read().decode('utf-8')
#print(text) return all(needle in text for needle in needles)
return needle in text except urllib.error.URLError:
except urllib.error.URLError as _:
return False return False
except ConnectionResetError: except ConnectionResetError:
return False return False
def nscheck(self, netns, needle): def nscheck(self, netns, needles):
""""Call check() from netns""" """"Call check() from netns
return netns.call(lambda: self.check(needle))
Args:
netns: Network namespace to call from
needles: String or list of strings to search for in response
Returns:
bool: True if all needles found in response, False otherwise
"""
return netns.call(lambda: self.check(needles))
-1
View File
@@ -150,7 +150,6 @@ class Device(Transport):
url = f"{self.yang_url}/{schema_name}" url = f"{self.yang_url}/{schema_name}"
data = self._get_raw(url=url, parse=False) data = self._get_raw(url=url, parse=False)
sys.stdout.write(f"Downloading YANG model {schema_name} ...\r\033[K")
data = data.decode('utf-8') data = data.decode('utf-8')
with open(f"{yangdir}/{schema_name}", 'w') as json_file: with open(f"{yangdir}/{schema_name}", 'w') as json_file:
json_file.write(data) json_file.write(data)
+10
View File
@@ -114,3 +114,13 @@ def ospf_is_area_nssa(target, area_id):
return True return True
return False return False
def ospf_has_neighbors(target):
ospf = _get_ospf_status(target)
for area in ospf.get("areas", {}).get("area", []):
for interface in area.get("interfaces", {}).get("interface", []):
if interface.get("neighbors"):
return True
return False
+15 -5
View File
@@ -1,9 +1,10 @@
base-dir := $(lastword $(subst :, ,$(BR2_EXTERNAL))) base-dir := $(lastword $(subst :, ,$(BR2_EXTERNAL)))
test-dir := $(BR2_EXTERNAL_INFIX_PATH)/test test-dir ?= $(BR2_EXTERNAL_INFIX_PATH)/test
ninepm := $(BR2_EXTERNAL_INFIX_PATH)/test/9pm/9pm.py ninepm := $(BR2_EXTERNAL_INFIX_PATH)/test/9pm/9pm.py
NINEPM_PROJ_CONF ?= $(BR2_EXTERNAL_INFIX_PATH)/test/9pm-proj.yaml
spec-dir := $(test-dir)/spec spec-dir := $(test-dir)/spec
test-specification := $(O)/images/test-specification.pdf test-specification := $(BINARIES_DIR)/test-specification.pdf
test-report := $(BINARIES_DIR)/test-report.pdf
UNIT_TESTS ?= $(test-dir)/case/all-repo.yaml $(test-dir)/case/all-unit.yaml UNIT_TESTS ?= $(test-dir)/case/all-repo.yaml $(test-dir)/case/all-unit.yaml
TESTS ?= $(test-dir)/case/all.yaml TESTS ?= $(test-dir)/case/all.yaml
@@ -33,9 +34,18 @@ test-sh:
test-spec: test-spec:
@esc_infix_name="$(echo $(INFIX_NAME) | sed 's/\//\\\//g')"; \ @esc_infix_name="$(echo $(INFIX_NAME) | sed 's/\//\\\//g')"; \
sed 's/{REPLACE}/$(subst ",,$(esc_infix_name)) $(INFIX_VERSION)/' $(spec-dir)/Readme.adoc.in > $(spec-dir)/Readme.adoc sed 's/{REPLACE}/$(subst ",,$(esc_infix_name)) $(INFIX_VERSION)/' \
$(spec-dir)/Readme.adoc.in > $(spec-dir)/Readme.adoc
@$(spec-dir)/generate_spec.py -s $(test-dir)/case/all.yaml -r $(BR2_EXTERNAL_INFIX_PATH) @$(spec-dir)/generate_spec.py -s $(test-dir)/case/all.yaml -r $(BR2_EXTERNAL_INFIX_PATH)
@asciidoctor-pdf --failure-level INFO --theme $(spec-dir)/theme.yml -a pdf-fontsdir=$(spec-dir)/fonts -o $(test-specification) $(spec-dir)/Readme.adoc @asciidoctor-pdf --failure-level INFO --theme $(spec-dir)/theme.yml \
-a pdf-fontsdir=$(spec-dir)/fonts \
-o $(test-specification) $(spec-dir)/Readme.adoc
test-report:
asciidoctor-pdf --theme $(spec-dir)/theme.yml \
-a logo="image:$(LOGO)" \
-a pdf-fontsdir=$(spec-dir)/fonts \
-o $(test-report) $(test-dir)/.log/last/report.adoc
# Unit tests run with random (-r) hostname and container name to # Unit tests run with random (-r) hostname and container name to
# prevent race conditions when running in CI environments. # prevent race conditions when running in CI environments.