You've already forked armbian.github.io
mirror of
https://github.com/armbian/armbian.github.io.git
synced 2026-01-06 11:42:20 -08:00
Update all workflow display names to match their file categories, making them easier to identify in the GitHub Actions UI.
396 lines
15 KiB
YAML
396 lines
15 KiB
YAML
name: "Testing: WiFi performance"
|
|
on:
|
|
workflow_dispatch:
|
|
schedule:
|
|
- cron: '0 0 * * SAT'
|
|
|
|
permissions:
|
|
contents: write
|
|
pull-requests: write
|
|
|
|
jobs:
|
|
|
|
power-on:
|
|
name: "Power system on"
|
|
outputs:
|
|
DEPLOYMENT_MATRIX: "${{ steps.json.outputs.DEPLOYMENT_MATRIX }}"
|
|
runs-on: "ubuntu-24.04"
|
|
steps:
|
|
|
|
# step for actual powering on / off TBD
|
|
|
|
- name: Get devices from database
|
|
id: json
|
|
run: |
|
|
|
|
delimiter="$(openssl rand -hex 8)"
|
|
echo "DEPLOYMENT_MATRIX<<${delimiter}" >> "${GITHUB_OUTPUT}"
|
|
curl -H "Authorization: Token ${{ secrets.NETBOX_TOKEN }}" -H "Accept: application/json; indent=4" \
|
|
"${{ secrets.NETBOX_API }}/dcim/devices/?limit=500&name__empty=false&status=active" | \
|
|
jq '.results[] | select(.device_role.slug == "wifi-dut") | {
|
|
name: .name,
|
|
serial: .serial,
|
|
site_id: (.site.id // empty),
|
|
device_type: .device_type.model,
|
|
device_class: .custom_fields.class,
|
|
device_ip: .primary_ip.address
|
|
}' | \
|
|
jq -s 'sort_by([.device_class, .name])' >> $GITHUB_OUTPUT
|
|
echo "${delimiter}" >> "${GITHUB_OUTPUT}"
|
|
|
|
gradle:
|
|
name: "${{ matrix.json.name }} (${{ matrix.json.device_class }})"
|
|
runs-on: "ubuntu-24.04"
|
|
needs: power-on
|
|
if: ${{ needs.power-on.outputs.DEPLOYMENT_MATRIX != '[]' }}
|
|
timeout-minutes: 20
|
|
strategy:
|
|
max-parallel: 1
|
|
fail-fast: false
|
|
matrix:
|
|
json: ${{ fromJSON(needs.power-on.outputs.DEPLOYMENT_MATRIX) }}
|
|
steps:
|
|
|
|
- name: "Connect to Tailscale network"
|
|
uses: tailscale/github-action@v4
|
|
with:
|
|
oauth-client-id: ${{ secrets.TS_OAUTH_CLIENT_ID }}
|
|
oauth-secret: ${{ secrets.TS_OAUTH_SECRET }}
|
|
tags: tag:ci
|
|
|
|
- name: "Install SSH key"
|
|
uses: shimataro/ssh-key-action@v2
|
|
with:
|
|
key: ${{ secrets.KEY_CI }}
|
|
known_hosts: ${{ secrets.KNOWN_HOSTS_ARMBIAN_CI }}
|
|
if_key_exists: replace
|
|
|
|
- name: "Prepare device variables"
|
|
timeout-minutes: 2
|
|
id: vars
|
|
shell: bash
|
|
run: |
|
|
|
|
# extract site ID
|
|
SITE_ID=${{ matrix.json.site_id }}
|
|
|
|
# Fetch JSON from NetBox
|
|
DATA=$(curl -s -H "Authorization: Token ${{ secrets.NETBOX_TOKEN }}" \
|
|
-H "Accept: application/json; indent=4" \
|
|
"${{ secrets.NETBOX_API }}/dcim/sites/?limit=500&name__empty=false&id=${SITE_ID}")
|
|
|
|
count=$(echo "$DATA" | jq '.results | length')
|
|
if [[ "$count" -eq 0 ]]; then
|
|
echo "::error::Site id $SITE_ID not found in NetBox"
|
|
exit 1
|
|
fi
|
|
|
|
# Extract fields from the first (and only) result
|
|
name=$(echo "$DATA" | jq -r '.results[0].name')
|
|
location=$(echo "$DATA" | jq -r '.results[0].region.name')
|
|
access_point=$(echo "$DATA" | jq -r '.results[0].custom_fields.access_point')
|
|
access_point_password=$(echo "$DATA" | jq -r '.results[0].custom_fields.access_point_password')
|
|
iperf_server=$(echo "$DATA" | jq -r '.results[0].custom_fields.iperf_server')
|
|
|
|
# Export to GitHub environment
|
|
echo "SITE_NAME=$name" >> "$GITHUB_ENV"
|
|
echo "SITE_LOCATION=$location" >> "$GITHUB_ENV"
|
|
echo "ACCESS_POINT=$access_point" >> "$GITHUB_ENV"
|
|
echo "ACCESS_POINT_PASSWORD=$access_point_password" >> "$GITHUB_ENV"
|
|
echo "IPERF_SERVER=$iperf_server" >> "$GITHUB_ENV"
|
|
echo "SITE_ID=$SITE_ID" >> "$GITHUB_ENV"
|
|
|
|
# extract IP address and device name
|
|
DEVICE=wlx$(echo '${{ matrix.json.serial }}' | sed 's/://g')
|
|
IP_ADDR=$(echo '${{ matrix.json.device_ip }}' | cut -d'/' -f1)
|
|
|
|
# clean leftovers from previous runs
|
|
ssh ci@${IP_ADDR} "sudo rm -f /etc/netplan/wireless.yaml"
|
|
ssh ci@${IP_ADDR} "sudo netplan apply"
|
|
|
|
# read fixed network parameters
|
|
DEFAULT_DEVICE=$(ssh ci@${IP_ADDR} "ip route show default | grep -Eo 'dev (en[^ ]+|eth[^ ]+|wan[^ ]+|lan[^ ])' | cut -d' ' -f2 | head -1")
|
|
DEFAULT_DEVICE_IP=$(ssh ci@${IP_ADDR} "ip route get 9.9.9.9 | grep -oP 'src \K[\d.]+'")
|
|
DEFAULT_DEVICE_GW=$(ssh ci@${IP_ADDR} "ip route show default dev ${DEFAULT_DEVICE} | awk '/default/ {print \$3}'")
|
|
DEFAULT_DEVICE_CIDR=$(ssh ci@${IP_ADDR} "ip route show | grep -oP '\d+\.\d+\.\d+\.\d+/\d+'")
|
|
|
|
# Store variables for further use
|
|
echo "IP_ADDR=${IP_ADDR}" >> $GITHUB_ENV
|
|
echo "DEVICE=${DEVICE}" >> $GITHUB_ENV
|
|
echo "DEFAULT_DEVICE=${DEFAULT_DEVICE}" >> $GITHUB_ENV
|
|
echo "DEFAULT_DEVICE_IP=${DEFAULT_DEVICE_IP}" >> $GITHUB_ENV
|
|
echo "DEFAULT_DEVICE_GW=${DEFAULT_DEVICE_GW}" >> $GITHUB_ENV
|
|
echo "DEFAULT_DEVICE_CIDR=${DEFAULT_DEVICE_CIDR}" >> $GITHUB_ENV
|
|
|
|
- name: "Generate YAML for wireless device"
|
|
shell: bash
|
|
run: |
|
|
|
|
ACCESS_POINT="GOSTJE60"
|
|
|
|
# generate the YAML file
|
|
# for the wireless device
|
|
# the file is used to configure
|
|
# the wireless device
|
|
# to connect to the access point
|
|
# and to set the IP address
|
|
# and the MAC address
|
|
cat > wireless.yaml <<- EOT
|
|
network:
|
|
version: 2
|
|
renderer: networkd
|
|
wifis:
|
|
${{ env.DEVICE }}:
|
|
dhcp4: true
|
|
dhcp6: true
|
|
macaddress: "${{ matrix.json.serial }}"
|
|
access-points:
|
|
"${{ env.ACCESS_POINT }}":
|
|
auth:
|
|
key-management: "psk"
|
|
password: "${{ env.ACCESS_POINT_PASSWORD }}"
|
|
EOT
|
|
|
|
- name: "Enable wireless adapter on ${{ env.IP_ADDR }}"
|
|
timeout-minutes: 2
|
|
shell: bash
|
|
run: |
|
|
|
|
scp wireless.yaml ci@${{ env.IP_ADDR }}:/tmp
|
|
ssh ci@${{ env.IP_ADDR }} "sudo mv /tmp/wireless.yaml /etc/netplan/"
|
|
ssh ci@${{ env.IP_ADDR }} "sudo chmod 600 /etc/netplan/*"
|
|
ssh ci@${{ env.IP_ADDR }} "sudo netplan apply"
|
|
|
|
- name: "Drop default route on ${{ env.IP_ADDR }}"
|
|
timeout-minutes: 2
|
|
shell: bash
|
|
run: |
|
|
|
|
ssh ci@${{ env.IP_ADDR }} "sudo ip route del ${{ env.DEFAULT_DEVICE_CIDR }} dev ${{ env.DEFAULT_DEVICE }}"
|
|
ssh ci@${{ env.IP_ADDR }} "sudo ip route del default dev ${{ env.DEFAULT_DEVICE }}"
|
|
|
|
- name: "Make sure device is connected"
|
|
timeout-minutes: 2
|
|
shell: bash
|
|
run: |
|
|
|
|
while true; do
|
|
# nmcli -t -f NAME connection show --active | grep ${DEVICE}
|
|
CONNECTION=$(ssh ci@${{ env.IP_ADDR }} "networkctl | grep ${{ env.DEVICE }} | grep routable || true")
|
|
CLIENTIP=$(ssh ci@${{ env.IP_ADDR }} "ip -4 addr show ${{ env.DEVICE }} | grep -oP '(?<=inet\s)\d+(\.\d+){3}' || true")
|
|
# check if the connection is routable
|
|
if [[ -n "${CONNECTION}" && -n "${CLIENTIP}" ]]; then
|
|
break
|
|
fi
|
|
sleep 2
|
|
done
|
|
# store client IP for further usage
|
|
echo "CLIENTIP=${CLIENTIP}" >> $GITHUB_ENV
|
|
|
|
- name: "Do some perf testing"
|
|
timeout-minutes: 2
|
|
shell: bash
|
|
run: |
|
|
|
|
mkdir -p test
|
|
# read info about connection and store it
|
|
ssh ci@${{ env.IP_ADDR }} "sudo iw dev ${{ env.DEVICE }} link | tail -n +2 | sed 's/^\t*//' | head -n -3 | sed '1d'" > test/${{ env.DEVICE }}-info.log
|
|
|
|
# Run iperf test in both directions
|
|
ssh ci@${{ env.IP_ADDR }} "iperf3 -R -c ${{ env.IPERF_SERVER }} -B ${{ env.CLIENTIP }}" > test/${{ env.DEVICE }}-tx.log
|
|
ssh ci@${{ env.IP_ADDR }} "iperf3 -c ${{ env.IPERF_SERVER }} -B ${{ env.CLIENTIP }}" > test/${{ env.DEVICE }}-rx.log
|
|
|
|
# Extract the sender and receiver speeds
|
|
tx_speed=$(cat test/${{ env.DEVICE }}-tx.log | grep "sender" | awk '{print $7}')
|
|
rx_speed=$(cat test/${{ env.DEVICE }}-rx.log | grep "sender" | awk '{print $7}')
|
|
|
|
# get Armbian version and kernel
|
|
echo "$(ssh ci@${{ env.IP_ADDR }} "cat /etc/armbian-release | grep VERSION")" >> test/${{ env.DEVICE }}.sysinfo
|
|
echo "$(ssh ci@${{ env.IP_ADDR }} "cat /etc/armbian-release | grep ^ARCH")" >> test/${{ env.DEVICE }}.sysinfo
|
|
echo "KERNEL=\"$(ssh ci@${{ env.IP_ADDR }} "uname -r")\"" >> test/${{ env.DEVICE }}.sysinfo
|
|
echo "DEVICE_NAME=\"${{ matrix.json.name }}\"" >> test/${{ env.DEVICE }}.sysinfo
|
|
echo "DEVICE_TYPE=\"${{ matrix.json.device_type }}\"" >> test/${{ env.DEVICE }}.sysinfo
|
|
echo "DEVICE_CLASS=\"${{ matrix.json.device_class }}\"" >> test/${{ env.DEVICE }}.sysinfo
|
|
echo "DEVICE_TX=\"${tx_speed}\"" >> test/${{ env.DEVICE }}.sysinfo
|
|
echo "DEVICE_RX=\"${rx_speed}\"" >> test/${{ env.DEVICE }}.sysinfo
|
|
|
|
- name: "Enable default route on ${{ env.IP_ADDR }}"
|
|
timeout-minutes: 1
|
|
shell: bash
|
|
run: |
|
|
|
|
ssh ci@${{ env.IP_ADDR }} "sudo ip route add ${{ env.DEFAULT_DEVICE_CIDR }} dev ${{ env.DEFAULT_DEVICE }} src ${{ env.DEFAULT_DEVICE_IP }}"
|
|
ssh ci@${{ env.IP_ADDR }} "sudo ip route add default via ${{ env.DEFAULT_DEVICE_GW }} dev ${{ env.DEFAULT_DEVICE }} metric 200"
|
|
|
|
- name: "Remove adapter"
|
|
timeout-minutes: 2
|
|
shell: bash
|
|
run: |
|
|
|
|
ssh ci@${{ env.IP_ADDR }} "sudo rm /etc/netplan/wireless.yaml"
|
|
ssh ci@${{ env.IP_ADDR }} "sudo netplan apply"
|
|
|
|
- name: "Upload test summary"
|
|
timeout-minutes: 3
|
|
uses: actions/upload-artifact@v6
|
|
with:
|
|
name: test-${{ env.DEVICE }}
|
|
path: test
|
|
if-no-files-found: ignore
|
|
|
|
merge:
|
|
name: "Merge test artifacts"
|
|
if: always()
|
|
needs: gradle
|
|
runs-on: ubuntu-24.04
|
|
steps:
|
|
|
|
- name: Checkout main documentation
|
|
uses: actions/checkout@v6
|
|
with:
|
|
repository: 'armbian/documentation'
|
|
path: 'documentation'
|
|
|
|
- name: "Connect to Tailscale network"
|
|
uses: tailscale/github-action@v4
|
|
with:
|
|
oauth-client-id: ${{ secrets.TS_OAUTH_CLIENT_ID }}
|
|
oauth-secret: ${{ secrets.TS_OAUTH_SECRET }}
|
|
tags: tag:ci
|
|
|
|
- name: "Install SSH key"
|
|
uses: shimataro/ssh-key-action@v2
|
|
with:
|
|
key: ${{ secrets.KEY_CI }}
|
|
known_hosts: ${{ secrets.KNOWN_HOSTS_ARMBIAN_CI }}
|
|
if_key_exists: replace
|
|
|
|
- name: Download All Artifacts
|
|
uses: actions/download-artifact@v7
|
|
with:
|
|
path: test
|
|
pattern: test-*
|
|
merge-multiple: true
|
|
|
|
- name: Install
|
|
run: |
|
|
|
|
FILENAME=output.md
|
|
cat > "$FILENAME" <<- EOT
|
|
## Devices Under Tests
|
|
This section presents the performance test results, including key metrics and technical details from the test execution.
|
|
**Test Date:** [$(date -u '+%Y-%m-%d %H:%M UTC')](${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}/actions/runs/${GITHUB_RUN_ID})
|
|
EOT
|
|
|
|
# Step 1: Get all device identifiers
|
|
device_ids=$(ls test | grep -oP 'wlx\w+' | sort -u)
|
|
|
|
# Step 2: Collect all metadata and sort by class, then name
|
|
sorted_devices=$(for device in $device_ids; do
|
|
source test/$device.sysinfo
|
|
echo "$DEVICE_CLASS|$DEVICE_NAME|$device"
|
|
done | sort)
|
|
|
|
# Step 3: Output by class and device
|
|
current_class=""
|
|
while IFS='|' read -r class name device; do
|
|
if [ "$class" != "$current_class" ]; then
|
|
echo "### $class" >> "$FILENAME"
|
|
current_class="$class"
|
|
fi
|
|
|
|
source test/$device.sysinfo
|
|
|
|
cat >> "$FILENAME" <<- EOT
|
|
|
|
#### $DEVICE_NAME
|
|
|
|
<img src=${{ secrets.NETBOX_API }}/media/devicetype-images/$DEVICE_TYPE.png>
|
|
<span style="font-size: 0.5rem;">OS: Armbian v${VERSION}, ${KERNEL}</span>
|
|
|
|
| Chipset | Class | Average forward speed | Average reverse speed |
|
|
|:-----|------|-------:|-------:|
|
|
|<span style="font-size: 1.5rem;">$DEVICE_TYPE</span> | <span style="font-size: 1.5rem;">$DEVICE_CLASS</span> | <span style="font-size: 1.5rem;">$DEVICE_TX</span> Mbits/sec | <span style="font-size: 1.5rem;">$DEVICE_RX</span> Mbits/sec |
|
|
|
|
=== "Forward mode (client to server)"
|
|
\`\`\`
|
|
$(sed 's/^/ /' test/$device-tx.log)
|
|
\`\`\`
|
|
=== "Reverse mode (server to client)"
|
|
\`\`\`
|
|
$(sed 's/^/ /' test/$device-rx.log)
|
|
\`\`\`
|
|
=== "Wireless link info"
|
|
\`\`\`
|
|
$(sed 's/^/ /' test/$device-info.log)
|
|
\`\`\`
|
|
EOT
|
|
|
|
done <<< "$sorted_devices"
|
|
|
|
cat "$FILENAME" >> "$GITHUB_STEP_SUMMARY"
|
|
|
|
- name: Add section for failed devices
|
|
run: |
|
|
|
|
echo >> output.md
|
|
echo "## Failed Devices" >> output.md
|
|
echo >> output.md
|
|
echo "| Commercial Name | Chip | Class |" >> output.md
|
|
echo "|:-----|:--------|:------|" >> output.md
|
|
|
|
curl -s -H "Authorization: Token ${{ secrets.NETBOX_TOKEN }}" \
|
|
-H "Accept: application/json; indent=4" \
|
|
"${{ secrets.NETBOX_API }}/dcim/devices/?limit=500&name__empty=false&status=failed" | \
|
|
jq -r '.results[] | select(.device_role.slug == "wifi-dut") |
|
|
[.name, .device_type.model, .custom_fields.class] |
|
|
@tsv' | \
|
|
while IFS=$'\t' read -r name model class; do
|
|
echo "| $name | $model | $class |"
|
|
done >> output.md
|
|
|
|
- name: Replace content in markdown document
|
|
run: |
|
|
|
|
FILE="documentation/docs/WifiPerformance.md"
|
|
NEW_CONTENT=$(cat output.md)
|
|
|
|
# Delete content between <!-- DUT-START --> and <!-- DUT-STOP -->
|
|
sed -i '/<!-- DUT-START -->/,/<!-- DUT-STOP -->/{
|
|
/<!-- DUT-START -->/!{/<!-- DUT-STOP -->/!d}
|
|
}' "$FILE"
|
|
|
|
# Insert new content between markers using printf to handle multiline correctly
|
|
printf "\n$NEW_CONTENT\n" | sed -i "/<!-- DUT-START -->/r /dev/stdin" "$FILE"
|
|
|
|
- name: Create Pull Request to documentation
|
|
uses: peter-evans/create-pull-request@v8
|
|
with:
|
|
token: ${{ secrets.ACCESS_TOKEN_ARMBIANWORKER }}
|
|
path: documentation
|
|
commit-message: '`Automatic` wireless performance tests'
|
|
signoff: false
|
|
branch: wireless-tests
|
|
delete-branch: true
|
|
title: '`Automatic` wireless performance tests'
|
|
body: |
|
|
Generate documentation.
|
|
|
|
labels: |
|
|
Needs review
|
|
draft: false
|
|
|
|
- uses: geekyeggo/delete-artifact@v5
|
|
with:
|
|
name: |
|
|
test-*
|
|
|
|
power-off:
|
|
name: "Power system off"
|
|
runs-on: ubuntu-24.04
|
|
needs: merge
|
|
steps:
|
|
- name: "Power off system"
|
|
run: |
|
|
echo "This job is left blank for power-off actions."
|