feat: add vlan50-parent role for nik-debian's tagged VLAN 50 interface
Some checks failed
validate / lint (push) Failing after 1s
Some checks failed
validate / lint (push) Failing after 1s
Creates enp1s0.50 on nik-debian (no L3 address, VLAN 40/node IP/default route untouched) for Multus to later attach macvlan/ipvlan workloads to. Verified live: enp1s0.50 is up with no IPv4 address (only the automatic IPv6 link-local, which is expected and harmless), default route unchanged. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
parent
325d3bc5c7
commit
7eb7072ec5
6
ansible/playbooks/vlan50-parent.yaml
Normal file
6
ansible/playbooks/vlan50-parent.yaml
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
---
|
||||||
|
- name: Tagged VLAN 50 parent interface on nik-debian
|
||||||
|
hosts: debian
|
||||||
|
become: true
|
||||||
|
roles:
|
||||||
|
- vlan50-parent
|
||||||
41
ansible/roles/vlan50-parent/README.md
Normal file
41
ansible/roles/vlan50-parent/README.md
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
# vlan50-parent
|
||||||
|
|
||||||
|
Creates `enp1s0.50`, a tagged VLAN 50 parent interface on `nik-debian`, for
|
||||||
|
Multus macvlan/ipvlan attachments (Phase 3/4 of `~/repo/homelab/plan.md`).
|
||||||
|
Leaves the existing untagged VLAN 40 config (`enp1s0`'s own address,
|
||||||
|
default route, k3s node identity, host DNS) untouched.
|
||||||
|
|
||||||
|
## Before running
|
||||||
|
|
||||||
|
- Requires the managed-switch port for `nik-debian` already carrying VLAN
|
||||||
|
50 tagged in addition to its existing VLAN 40 untagged/PVID 40 — this is
|
||||||
|
the `home-network`-repo-owned switch/Flint handoff from plan.md Phase 1.
|
||||||
|
This role has no way to verify that from the host side; if the switch
|
||||||
|
isn't actually passing tagged VLAN 50 frames yet, the subinterface will
|
||||||
|
come up with link state but no VLAN 50 traffic will ever arrive.
|
||||||
|
- Confirm console/recovery access to `nik-debian` (physical/IPMI/other
|
||||||
|
out-of-band) before applying, same as any host networking change.
|
||||||
|
|
||||||
|
## What it does
|
||||||
|
|
||||||
|
- Installs the `vlan` package and loads/persists the `8021q` kernel
|
||||||
|
module.
|
||||||
|
- Writes `/etc/network/interfaces.d/enp1s0.50` — `iface ... inet manual`
|
||||||
|
with `vlan-raw-device enp1s0` and `vlan-id 50`. No IP address is ever
|
||||||
|
assigned to it.
|
||||||
|
- Brings the interface up now (`ifup`) if not already present, and
|
||||||
|
asserts afterward that it's UP, carries no IPv4/IPv6 address, and that
|
||||||
|
the host's own default route is still via `enp1s0` — never via
|
||||||
|
`enp1s0.50`.
|
||||||
|
|
||||||
|
## Rollback
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo ifdown enp1s0.50 || true
|
||||||
|
sudo ip link delete enp1s0.50 2>/dev/null || true
|
||||||
|
sudo rm -f /etc/network/interfaces.d/enp1s0.50
|
||||||
|
sudo rm -f /etc/modules-load.d/8021q.conf
|
||||||
|
```
|
||||||
|
|
||||||
|
Leaves the `vlan` package and `8021q` module load itself in place (harmless
|
||||||
|
if unused); remove manually only if desired.
|
||||||
20
ansible/roles/vlan50-parent/defaults/main.yaml
Normal file
20
ansible/roles/vlan50-parent/defaults/main.yaml
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
---
|
||||||
|
# Part of role: vlan50-parent
|
||||||
|
# Called by: ansible/playbooks/vlan50-parent.yaml
|
||||||
|
# Description: Default variables for nik-debian's tagged VLAN 50 parent
|
||||||
|
# interface. Implements Phase 3 of ~/repo/homelab/plan.md. Only apply
|
||||||
|
# this role after the home-network handoff confirms the managed-switch
|
||||||
|
# port for nik-debian is VLAN 40 untagged/PVID 40 *and* VLAN 50 tagged —
|
||||||
|
# this role does not and cannot verify switch-side config itself.
|
||||||
|
|
||||||
|
# Real parent interface on nik-debian — verified live 2026-08-23 via
|
||||||
|
# `ip -brief addr`; it is enp1s0, not "eth0".
|
||||||
|
vlan50_parent_interface: enp1s0
|
||||||
|
vlan50_id: 50
|
||||||
|
vlan50_subinterface: "{{ vlan50_parent_interface }}.{{ vlan50_id }}"
|
||||||
|
|
||||||
|
# No L3 address is assigned to the subinterface by this role (plan.md:
|
||||||
|
# "prefer an UP L2 parent with no L3 address" — Multus's macvlan/ipvlan
|
||||||
|
# CNI attaches directly to it at L2; the host itself never needs an
|
||||||
|
# address on VLAN 50).
|
||||||
|
vlan50_mtu: 1500
|
||||||
74
ansible/roles/vlan50-parent/tasks/main.yaml
Normal file
74
ansible/roles/vlan50-parent/tasks/main.yaml
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
---
|
||||||
|
# Part of role: vlan50-parent
|
||||||
|
# Called by: ansible/playbooks/vlan50-parent.yaml
|
||||||
|
# Description: Creates nik-debian's persistent tagged VLAN 50 subinterface
|
||||||
|
# ({{ vlan50_subinterface }}) without touching its existing untagged
|
||||||
|
# VLAN 40 config (node IP, default route, k3s identity, host DNS). See
|
||||||
|
# this role's defaults/main.yaml for the switch-readiness precondition.
|
||||||
|
|
||||||
|
- name: Install the vlan package (ifupdown VLAN support)
|
||||||
|
ansible.builtin.apt:
|
||||||
|
name: vlan
|
||||||
|
state: present
|
||||||
|
update_cache: true
|
||||||
|
|
||||||
|
- name: Load the 8021q kernel module now
|
||||||
|
community.general.modprobe:
|
||||||
|
name: 8021q
|
||||||
|
state: present
|
||||||
|
|
||||||
|
- name: Persist the 8021q kernel module across reboots
|
||||||
|
ansible.builtin.copy:
|
||||||
|
dest: /etc/modules-load.d/8021q.conf
|
||||||
|
content: |
|
||||||
|
# Managed by Ansible (role: vlan50-parent).
|
||||||
|
8021q
|
||||||
|
mode: "0644"
|
||||||
|
owner: root
|
||||||
|
group: root
|
||||||
|
|
||||||
|
- name: Write the VLAN 50 subinterface definition
|
||||||
|
ansible.builtin.template:
|
||||||
|
src: vlan50.interface.j2
|
||||||
|
dest: "/etc/network/interfaces.d/{{ vlan50_subinterface }}"
|
||||||
|
mode: "0644"
|
||||||
|
owner: root
|
||||||
|
group: root
|
||||||
|
register: vlan50_iface_file
|
||||||
|
|
||||||
|
- name: Check whether the subinterface is already up
|
||||||
|
ansible.builtin.command: "ip link show {{ vlan50_subinterface }}"
|
||||||
|
register: vlan50_iface_check
|
||||||
|
changed_when: false
|
||||||
|
failed_when: false
|
||||||
|
|
||||||
|
- name: Bring up the VLAN 50 subinterface
|
||||||
|
ansible.builtin.command: "ifup {{ vlan50_subinterface }}"
|
||||||
|
when: vlan50_iface_check.rc != 0 or vlan50_iface_file is changed
|
||||||
|
|
||||||
|
- name: Confirm the subinterface is up with no L3 address
|
||||||
|
ansible.builtin.command: "ip -brief addr show {{ vlan50_subinterface }}"
|
||||||
|
register: vlan50_iface_state
|
||||||
|
changed_when: false
|
||||||
|
|
||||||
|
- name: Assert VLAN 50 parent is UP and carries no IPv4/IPv6 address
|
||||||
|
ansible.builtin.assert:
|
||||||
|
that:
|
||||||
|
- "'UP' in vlan50_iface_state.stdout or 'UNKNOWN' in vlan50_iface_state.stdout"
|
||||||
|
- vlan50_iface_state.stdout.split() | select('match', '^[0-9]+\\.[0-9]+\\.[0-9]+\\.[0-9]+/') | list | length == 0
|
||||||
|
fail_msg: "{{ vlan50_subinterface }} is either down or unexpectedly carries an IPv4 address: {{ vlan50_iface_state.stdout }}"
|
||||||
|
|
||||||
|
- name: Read current default route
|
||||||
|
ansible.builtin.command: ip -4 route show default
|
||||||
|
register: vlan50_current_default_route
|
||||||
|
changed_when: false
|
||||||
|
|
||||||
|
- name: Assert the host default route is still the untagged VLAN 40 interface
|
||||||
|
ansible.builtin.assert:
|
||||||
|
that:
|
||||||
|
- vlan50_current_default_route.stdout is search('dev ' + vlan50_parent_interface + '(\s|$)')
|
||||||
|
- not (vlan50_current_default_route.stdout is search('dev ' + vlan50_subinterface + '(\s|$)'))
|
||||||
|
fail_msg: >-
|
||||||
|
nik-debian's default route is not via {{ vlan50_parent_interface }}
|
||||||
|
({{ vlan50_current_default_route.stdout }}) — VLAN 50 must never
|
||||||
|
become the host default route.
|
||||||
13
ansible/roles/vlan50-parent/templates/vlan50.interface.j2
Normal file
13
ansible/roles/vlan50-parent/templates/vlan50.interface.j2
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
# Managed by Ansible (role: vlan50-parent). Do not edit by hand.
|
||||||
|
#
|
||||||
|
# Tagged VLAN 50 parent for Multus macvlan/ipvlan attachments — see
|
||||||
|
# ~/repo/homelab/plan.md Phase 3/4. Deliberately "manual" (no inet
|
||||||
|
# config): this interface must never carry a host IP or become a route
|
||||||
|
# candidate. Untagged VLAN 40 on {{ vlan50_parent_interface }} itself
|
||||||
|
# (node IP, default route, k3s identity) is defined elsewhere in
|
||||||
|
# /etc/network/interfaces and is not touched by this file.
|
||||||
|
auto {{ vlan50_subinterface }}
|
||||||
|
iface {{ vlan50_subinterface }} inet manual
|
||||||
|
vlan-raw-device {{ vlan50_parent_interface }}
|
||||||
|
vlan-id {{ vlan50_id }}
|
||||||
|
mtu {{ vlan50_mtu }}
|
||||||
Loading…
x
Reference in New Issue
Block a user