From 7e5342674d1bd2ea051dd5e3c42172dd59a76cd3 Mon Sep 17 00:00:00 2001 From: Paul Spooren Date: Sat, 25 Apr 2026 18:37:13 +0800 Subject: [PATCH 1/3] labnet: split maintainers from access, fetch keys from GitHub Maintainers are notified via @user mentions in healthcheck issue bodies; access lists which GitHub users get SSH keys fetched from github.com/.keys and authorized on the lab host. Both are stored without the @ prefix; the coordinator authorizes the union of all access entries. Co-Authored-By: Claude Opus 4.7 (1M context) Signed-off-by: Paul Spooren --- .github/workflows/healthcheck.yml | 3 +- ansible/coordinator.yml | 10 +++-- ansible/lab.yml | 10 ++--- ansible/playbook_labgrid.yml | 10 ++--- docs/sharing-target-files.md | 4 +- labnet.yaml | 73 +++++++++++++------------------ 6 files changed, 51 insertions(+), 59 deletions(-) diff --git a/.github/workflows/healthcheck.yml b/.github/workflows/healthcheck.yml index 180f52777..6581ee88c 100644 --- a/.github/workflows/healthcheck.yml +++ b/.github/workflows/healthcheck.yml @@ -178,6 +178,7 @@ jobs: const issueTitle = "Healthcheck ${{ matrix.proxy }}/${{ matrix.device }}"; const runUrl = "https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}"; const isSuccess = "${{ job.status }}" === "success"; + const maintainers = ${{ toJSON(matrix.maintainers) }}.map(m => '@' + m).join(', '); const failureBody = ` ### ❌ Healthcheck Failed @@ -186,7 +187,7 @@ jobs: - **Proxy**: \`${{ matrix.proxy }}\` - **Device**: \`${{ matrix.device }}\` - - **Maintainers**: ${{ join(matrix.maintainers, ', ') }} + - **Maintainers**: ${maintainers} has **failed**. Please review the logs and investigate the issue. diff --git a/ansible/coordinator.yml b/ansible/coordinator.yml index 8b0a6e90c..14b2548db 100644 --- a/ansible/coordinator.yml +++ b/ansible/coordinator.yml @@ -1,18 +1,20 @@ - hosts: coordinator become: true + vars: + labnet: "{{ lookup('file', '../labnet.yaml') | from_yaml }}" tasks: - name: Create a user account called labgrid-dev user: name: labgrid-dev state: present - - name: Add SSH keys to labgrid-dev based on labnet.yaml + - name: Add SSH keys to labgrid-dev from GitHub authorized_key: user: labgrid-dev state: present - key: "{{ item.value.sshkey }}" - comment: "{{ item.key }}" - loop: "{{ hostvars[inventory_hostname]['developers'] | dict2items | default([]) }}" + key: "{{ lookup('url', 'https://github.com/' + item + '.keys', split_lines=false) }}" + comment: "{{ item }}" + loop: "{{ ((labnet.labs.values() | map(attribute='maintainers') | flatten | list) + (labnet.labs.values() | map(attribute='access') | flatten | list)) | unique | list }}" - name: Add public SSH key of CI runner authorized_key: diff --git a/ansible/lab.yml b/ansible/lab.yml index 0a301e6be..91c7181d8 100644 --- a/ansible/lab.yml +++ b/ansible/lab.yml @@ -1,5 +1,7 @@ - hosts: labs become: true + vars: + labnet: "{{ lookup('file', '../labnet.yaml') | from_yaml }}" tasks: - name: Set hostname hostname: @@ -24,13 +26,13 @@ - dialout - plugdev - - name: Add SSH keys to labgrid-dev based on labnet.yaml + - name: Add SSH keys to labgrid-dev from GitHub authorized_key: user: labgrid-dev state: present - key: "{{ hostvars[inventory_hostname]['developers'][item]['sshkey'] }}" + key: "{{ lookup('url', 'https://github.com/' + item + '.keys', split_lines=false) }}" comment: "{{ item }}" - loop: "{{ hostvars[inventory_hostname]['labs'][inventory_hostname]['developers'] | default([]) }}" + loop: "{{ ((labnet.labs[inventory_hostname].maintainers | default([])) + (labnet.labs[inventory_hostname].access | default([]))) | unique | list }}" - name: Set /etc/hosts lineinfile: @@ -289,8 +291,6 @@ mode: "0755" owner: labgrid-dev group: labgrid-dev - vars: - labnet: "{{ lookup('file', '../labnet.yaml') | from_yaml }}" notify: - Restart labgrid-coordinator tags: diff --git a/ansible/playbook_labgrid.yml b/ansible/playbook_labgrid.yml index 0a301e6be..91c7181d8 100644 --- a/ansible/playbook_labgrid.yml +++ b/ansible/playbook_labgrid.yml @@ -1,5 +1,7 @@ - hosts: labs become: true + vars: + labnet: "{{ lookup('file', '../labnet.yaml') | from_yaml }}" tasks: - name: Set hostname hostname: @@ -24,13 +26,13 @@ - dialout - plugdev - - name: Add SSH keys to labgrid-dev based on labnet.yaml + - name: Add SSH keys to labgrid-dev from GitHub authorized_key: user: labgrid-dev state: present - key: "{{ hostvars[inventory_hostname]['developers'][item]['sshkey'] }}" + key: "{{ lookup('url', 'https://github.com/' + item + '.keys', split_lines=false) }}" comment: "{{ item }}" - loop: "{{ hostvars[inventory_hostname]['labs'][inventory_hostname]['developers'] | default([]) }}" + loop: "{{ ((labnet.labs[inventory_hostname].maintainers | default([])) + (labnet.labs[inventory_hostname].access | default([]))) | unique | list }}" - name: Set /etc/hosts lineinfile: @@ -289,8 +291,6 @@ mode: "0755" owner: labgrid-dev group: labgrid-dev - vars: - labnet: "{{ lookup('file', '../labnet.yaml') | from_yaml }}" notify: - Restart labgrid-coordinator tags: diff --git a/docs/sharing-target-files.md b/docs/sharing-target-files.md index 06b86a503..17cac7014 100644 --- a/docs/sharing-target-files.md +++ b/docs/sharing-target-files.md @@ -10,7 +10,9 @@ labs: proxy: labgrid-example hostkey: ssh-ed25519 AAAA... maintainers: - - "@maintainer" + - maintainer + access: + - maintainer devices: - linksys_e8450 device_instances: diff --git a/labnet.yaml b/labnet.yaml index a3cc7f43f..1f8dec6e1 100644 --- a/labnet.yaml +++ b/labnet.yaml @@ -3,7 +3,11 @@ labs: proxy: labgrid-aparcar hostkey: ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPRn1GsHlmQ02c8aOsQrSKz0qtK6IEO7uTRjrrDOKai7 maintainers: - - "@aparcar" + - aparcar + access: + - aparcar + - dangowrt + - jonasjelonek devices: - openwrt_one - bananapi_bpi-r4-lite @@ -14,44 +18,43 @@ labs: # - glinet_gl-mt6000 # - enterasys_ws-ap3710i # - ignitenet_ss-w2-ac2600 - developers: - - aparcar - - dangowrt - - jonasjelonek labgrid-bastian: proxy: labgrid-bastian hostkey: ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGxdB8NmJRjrS9lztZDKld8OprqY5ELjZ7koQ3b5atLd maintainers: - - "@bittorf" + - bittorf + access: + - aparcar devices: - cznic_turris-omnia - tplink_tl-wdr4300-v1 - developers: - - aparcar labgrid-blocktrron: proxy: labgrid-blocktrron hostkey: ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGLL+T3zunt13cl9ZSZZkc/W+tnvIENEB28x/VoU2QnV maintainers: - - "@blocktrron" + - blocktrron + access: + - aparcar devices: - hpe_msm460 - tplink_tl-wr842n-v3 - developers: - - aparcar labgrid-leinelab: proxy: labgrid-leinelab hostkey: ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAILDEM6JDEorz8cURSX7fXOdKsLKMJZQpzccpSEKytjAf maintainers: - - "@lemoer" + - lemoer + access: + - aparcar devices: - tplink_tl-wr1043nd-v3 - developers: - - aparcar labgrid-hsn: proxy: labgrid-hsn hostkey: ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIP5kAZ/gVDTHp2rOWmybFSf+JFtoQJPH4KFE+lUGKH0c maintainers: - - "@jonasjelonek" + - jonasjelonek + access: + - aparcar + - jonasjelonek devices: - genexis_pulse-ex400 - bananapi_bpi-r64 @@ -59,14 +62,13 @@ labs: - glinet_gl-mt1300 - linksys_e8450 - tplink_tl-wr1043nd-v4 - developers: - - aparcar - - jonasjelonek labgrid-wigyori: proxy: labgrid-wigyori hostkey: ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAINoXHDKDcWBZ5mj2GSiWomYzaqjaElFQiqmqM4c8K8Q3 maintainers: - - "@wigyori" + - wigyori + access: + - wigyori devices: - linksprite_a10-pcduino - olimex_a10-olinuxino-lime @@ -74,24 +76,25 @@ labs: - pine64_pine64-plus - xunlong_orangepi-2 - xunlong_orangepi-zero2 - developers: - - wigyori labgrid-hauke: proxy: labgrid-hauke hostkey: ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIHuOU1Tc2XIpd8B3Oy+Hg1aHYL+L+IznbVvdCJ04wizX maintainers: - - "@hauke" + - hauke + access: + - hauke devices: - openwrt_one - developers: - - hauke labgrid-fcefyn: proxy: labgrid-fcefyn hostkey: ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIK0ANF89u6x2DjGv8wEZ6Yc07WjcV7QM4izgy4EwafYo maintainers: - - "@francoriba" - - "@ccasanueva7" - - "@javierbrk" + - francoriba + - ccasanueva7 + - javierbrk + access: + - francoriba + - aparcar devices: - linksys_e8450 - openwrt_one @@ -104,19 +107,3 @@ labs: - belkin_rt3200_3 librerouter_librerouter-v1: - librerouter_1 - developers: - - francoriba - - aparcar -developers: - aparcar: - sshkey: ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIDZTxjQ3/KTefKvsPlVBWz+ITD7dGWcOt8/C55ekd2VE - dangowrt: - sshkey: ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIDe6tUbVJW3bYUVCYyQQFpNrJ1wP5kzvEkCruSjyA6TM - jonasjelonek: - sshkey: ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIDy4T0osxR8yS4RjJcsMn3rSacKvNzu+ZRNfe2lXTSHS - wigyori: - sshkey: ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIG8vcHMKcl6W6OuzSdjOhQ0ZehtH+McjLB7WzgP9P4Ls - hauke: - sshkey: ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIDhbJDeGBfpn8vkd1wjHgOWvN9WGWS3mRgqqFqNqgpNE - francoriba: - sshkey: ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPaNY9LyGgQlwlMaWxSWq5xD++UGCJtdqxHPHDADFACi From 3fcfd2cf75d73d4e4b7b4de4ebef5434570c1bc4 Mon Sep 17 00:00:00 2001 From: Paul Spooren Date: Sat, 25 Apr 2026 19:01:46 +0800 Subject: [PATCH 2/3] labnet: make Bryan maintainer of HSN Signed-off-by: Paul Spooren --- labnet.yaml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/labnet.yaml b/labnet.yaml index 1f8dec6e1..ca25b8bad 100644 --- a/labnet.yaml +++ b/labnet.yaml @@ -51,10 +51,11 @@ labs: proxy: labgrid-hsn hostkey: ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIP5kAZ/gVDTHp2rOWmybFSf+JFtoQJPH4KFE+lUGKH0c maintainers: - - jonasjelonek + - bryansancheZ199 access: - aparcar - jonasjelonek + - bryansancheZ199 devices: - genexis_pulse-ex400 - bananapi_bpi-r64 From be4de9f2364cef097e7fbeea1a36d348d7eb0468 Mon Sep 17 00:00:00 2001 From: Paul Spooren Date: Sun, 26 Apr 2026 20:54:30 +0800 Subject: [PATCH 3/3] ansible: drop duplicate playbook_labgrid.yml It was an exact duplicate of lab.yml. Co-Authored-By: Claude Opus 4.7 (1M context) Signed-off-by: Paul Spooren --- ansible/playbook_labgrid.yml | 400 ----------------------------------- 1 file changed, 400 deletions(-) delete mode 100644 ansible/playbook_labgrid.yml diff --git a/ansible/playbook_labgrid.yml b/ansible/playbook_labgrid.yml deleted file mode 100644 index 91c7181d8..000000000 --- a/ansible/playbook_labgrid.yml +++ /dev/null @@ -1,400 +0,0 @@ -- hosts: labs - become: true - vars: - labnet: "{{ lookup('file', '../labnet.yaml') | from_yaml }}" - tasks: - - name: Set hostname - hostname: - name: "{{ inventory_hostname }}" - - - name: Set /etc/hosts - lineinfile: - path: /etc/hosts - regexp: "^127.0.0.1" - line: "127.0.0.1 localhost" - state: present - - - name: Create a user account called labgrid-dev - user: - name: labgrid-dev - state: present - shell: /bin/bash - home: /home/labgrid-dev - create_home: yes - append: true - groups: - - dialout - - plugdev - - - name: Add SSH keys to labgrid-dev from GitHub - authorized_key: - user: labgrid-dev - state: present - key: "{{ lookup('url', 'https://github.com/' + item + '.keys', split_lines=false) }}" - comment: "{{ item }}" - loop: "{{ ((labnet.labs[inventory_hostname].maintainers | default([])) + (labnet.labs[inventory_hostname].access | default([]))) | unique | list }}" - - - name: Set /etc/hosts - lineinfile: - path: /etc/hosts - regexp: "^127.0.1.1" - line: "127.0.1.1 {{ inventory_hostname }}" - state: present - - - name: Add public SSH key of coordinator - authorized_key: - user: "labgrid-dev" - key: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIP0ZVlD9TmfAXL53Vq7V9WKE3KPomOa1jINyflrPWAlJ" - - - name: Generate SSH key pair for labgrid - community.crypto.openssh_keypair: - path: /home/labgrid-dev/.ssh/labgrid - type: ed25519 - comment: labgrid - state: present - owner: labgrid-dev - group: labgrid-dev - - register: labgrid_key - - - name: Add public SSH key of labgrid - authorized_key: - user: "labgrid-dev" - key: "{{ labgrid_key.public_key }}" - state: present - - - name: Install required packages - apt: - name: - - pipx - - microcom - - ser2net - - rsync - - socat - - iptables - - iptables-persistent - - git - - sudo - - pkg-config - - libsystemd-dev - - build-essential - - python3-dev - state: present - update_cache: yes - - - name: Create pipx folder - file: - path: /opt/pipx/ - state: directory - owner: "root" - group: "root" - mode: "0755" - - - name: Install labgrid packages - community.general.pipx: - name: labgrid - source: git+https://github.com/aparcar/labgrid.git@aparcar/staging - state: latest - environment: - PIPX_HOME: /opt/pipx/ - PIPX_BIN_DIR: /usr/local/bin - notify: - - Restart labgrid-exporter - - Restart labgrid-coordinator - - - name: Install usbsdmux packages - community.general.pipx: - name: usbsdmux - state: latest - environment: - PIPX_HOME: /opt/pipx/ - PIPX_BIN_DIR: /usr/local/bin - - - name: Create labgrid folder - file: - path: /etc/labgrid - state: directory - - - - name: Create tftp folder - file: - path: /srv/tftp/ - state: directory - owner: "labgrid-dev" - group: "labgrid-dev" - recurse: yes - mode: "0755" - - - - name: Build TFTP device instance mapping - set_fact: - tftp_device_instances: "{{ tftp_device_instances | default([]) + [{ - 'device': item, - 'instances': hostvars[inventory_hostname]['labs'][inventory_hostname].get('device_instances', {}).get(item, [item]) - }] }}" - loop: "{{ hostvars[inventory_hostname]['labs'][inventory_hostname]['devices'] }}" - tags: - - export - - - name: Create tftp subfolders (one per device instance) - file: - path: /srv/tftp/{{ item[1] }} - state: directory - owner: "labgrid-dev" - group: "labgrid-dev" - mode: "0755" - loop: "{{ tftp_device_instances | subelements('instances') }}" - loop_control: - label: "{{ item[1] }}" - tags: - - export - - - name: Copy exporter configuration - template: - src: files/exporter/{{ inventory_hostname }}/exporter.yaml - dest: /etc/labgrid/exporter.yaml - notify: - - Restart labgrid-exporter - tags: - - export - - - name: Setup pdudaemon - block: - - name: Install pdudaemon via pipx - community.general.pipx: - name: pdudaemon - source: git+https://github.com/jonasjelonek/pdudaemon.git@main - state: latest - environment: - PIPX_HOME: /opt/pipx/ - PIPX_BIN_DIR: /usr/local/bin - become: true - - - name: Create pdudaemon directory - ansible.builtin.file: - path: /etc/pdudaemon - state: directory - mode: 0755 - - - name: Deploy pdudaemon systemd unit - ansible.builtin.template: - src: files/exporter/pdudaemon.service - dest: /etc/systemd/system/pdudaemon.service - - - name: Stat host specific pdudaemon configuration - ansible.builtin.stat: - path: files/exporter/{{ inventory_hostname }}/pdudaemon.conf - register: host_specific_pdudaemon_conf - delegate_to: localhost - become: false - - - name: Configure pdudaemon using host specific config - template: - src: files/exporter/{{ inventory_hostname }}/pdudaemon.conf - dest: /etc/pdudaemon/pdudaemon.conf - notify: - - Restart pdudaemon - when: host_specific_pdudaemon_conf.stat.exists - - - name: Otherwise use general config - template: - src: files/exporter/pdudaemon.conf - dest: /etc/pdudaemon/pdudaemon.conf - notify: - - Restart pdudaemon - when: not host_specific_pdudaemon_conf.stat.exists - - - name: Start and enable pdudaemon - ansible.builtin.systemd_service: - name: pdudaemon - state: restarted - enabled: yes - daemon_reload: yes - - - name: Install dnsmasq - apt: - name: - - dnsmasq - state: present - update_cache: yes - - notify: - - Restart dnsmasq - - - name: Configure network with netplan - block: - - name: Stat host specific netplan configuration - ansible.builtin.stat: - path: files/exporter/{{ inventory_hostname }}/netplan.yaml - register: host_specific_netplan_conf - delegate_to: localhost - become: false - - - name: Configure netplan using host specific config - ansible.builtin.template: - src: files/exporter/{{ inventory_hostname }}/netplan.yaml - dest: /etc/netplan/labnet.yaml - mode: 0600 - owner: root - group: root - when: host_specific_netplan_conf.stat.exists - - - name: Configure netplan using default config - template: - src: files/exporter/netplan.yaml - dest: /etc/netplan/labnet.yaml - notify: - - Apply netplan - when: not host_specific_netplan_conf.stat.exists - - - name: Stat host specific dnsmasq configuration - ansible.builtin.stat: - path: files/exporter/{{ inventory_hostname }}/dnsmasq.conf - register: host_specific_dnsmasq_conf - delegate_to: localhost - become: false - - - name: Configure dnsmasq using host specific config - template: - src: files/exporter/{{ inventory_hostname }}/dnsmasq.conf - dest: /etc/dnsmasq.conf - - notify: - - Restart dnsmasq - when: host_specific_dnsmasq_conf.stat.exists - - - name: Configure dnsmasq using default config - template: - src: files/exporter/dnsmasq.conf - dest: /etc/dnsmasq.conf - notify: - - Restart dnsmasq - when: not host_specific_dnsmasq_conf.stat.exists - - - name: Start and enable dnsmasq - service: - name: dnsmasq - state: started - enabled: yes - - - name: Copy labgrid-coordinator.service - template: - src: files/coordinator/labgrid-coordinator.service - dest: /etc/systemd/system/labgrid-coordinator.service - notify: - - Restart labgrid-coordinator - - - name: Add places.yaml based on labnet.yaml - template: - src: files/coordinator/places.yaml.j2 - dest: /etc/labgrid/places.yaml - mode: "0755" - owner: labgrid-dev - group: labgrid-dev - notify: - - Restart labgrid-coordinator - tags: - - export - - - name: Start and enable labgrid-coordinator - service: - name: labgrid-coordinator - state: started - enabled: yes - - - name: Copy labgrid-exporter.service - template: - src: files/exporter/labgrid-exporter.service - dest: /etc/systemd/system/labgrid-exporter.service - notify: - - Restart labgrid-exporter - - - name: Start and enable labgrid-exporter - service: - name: labgrid-exporter - state: started - enabled: yes - - - name: Add labgrid-bound-connect to sudoers - lineinfile: - path: /etc/sudoers - line: "ALL ALL = NOPASSWD: /usr/local/sbin/labgrid-bound-connect" - validate: "visudo -cf %s" - - - name: Install labgrid-bound-connect - copy: - src: files/labgrid-bound-connect - dest: /usr/local/sbin/labgrid-bound-connect - mode: "0755" - - - name: Create /var/cache/labgrid/labgrid-dev - file: - path: /var/cache/labgrid/labgrid-dev - state: directory - owner: "labgrid-dev" - group: "labgrid-dev" - recurse: yes - mode: "0755" - - - name: Add cronjob to clean old cache files - cron: - name: "Clean labgrid cache files older than 7 days" - minute: "0" - hour: "2" - job: "find /var/cache/labgrid -type f -mtime +7 -delete" - user: "labgrid-dev" - - - name: Enable IP forwarding - sysctl: - name: net.ipv4.ip_forward - value: "1" - state: present - sysctl_set: yes - reload: yes - - - name: Make IP forwarding persistent - copy: - content: "net.ipv4.ip_forward = 1" - dest: /etc/sysctl.d/99-ip-forward.conf - mode: "0644" - - - name: Add udev rules for USB-SD-Mux - copy: - src: files/exporter/usbsdmux.rules - dest: /etc/udev/rules.d/99-usbsdmux.rules - mode: "0644" - notify: Reload udev rules - tags: - - udev - - handlers: - - name: Restart labgrid-exporter - systemd: - daemon_reload: yes - name: labgrid-exporter - state: restarted - - - name: Restart labgrid-coordinator - systemd: - daemon_reload: yes - name: labgrid-coordinator - state: restarted - - - name: Restart pdudaemon - systemd: - daemon_reload: yes - name: pdudaemon - state: restarted - - - name: Restart dnsmasq - systemd: - daemon_reload: yes - name: dnsmasq - state: restarted - - - name: Reload udev rules - command: udevadm control --reload-rules - - - name: Apply netplan - ansible.builtin.command: - cmd: netplan apply