feat(edge-host): add tagged VLAN 40 sub-interface
Prerequisite for enabling keepalived on edge-1: VRRP needs layer-2 adjacency
with the other NTP members on the service VLAN, and the host has only untagged
VLAN 30. The switch port already permits VLAN 40.
Built to be additive and revertible, because enp1s0 is this headless host's
only path to the network:
- The parent's 20-wired.network is not edited. The VLAN is attached through
a 20-wired.network.d/ drop-in instead, so reverting is deleting three paths.
- The VLAN .network sorts before 20-wired.network and matches explicitly on
Name=. 20-wired.network matches broadly on Type=ether; a VLAN link types as
'vlan' rather than 'ether' (confirmed against networkctl output) so it
should not match anyway, but ordering makes that belt-and-braces.
- UseRoutes, UseDNS, UseNTP and IPv6AcceptRA are all disabled on the VLAN.
Taking a second default route and DNS from the VLAN 40 lease would make
return traffic asymmetric across the two VLANs; untagged VLAN 30 stays the
only default route.
Tasks are tagged vlan40 so they can be applied without running the whole
edge-host role, which would restart the Z-Wave, open3e and alloy services.
Reconfiguring the parent link is what materialises the VLAN child -- a bare
networkctl reload leaves existing links untouched -- so the handler does that
explicitly. That re-runs DHCP on enp1s0 and is the one genuinely risky step;
worth running with console access to hand.
Not yet applied: the apply was blocked pending authorisation, so edge-1 remains
in its original network state with only 20-wired.network and 80-can0.network.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -23,6 +23,10 @@ edge_loki_password: "{{ vault_edge_loki_password }}"
|
||||
ntp_keepalived_enabled: false
|
||||
ntp_keepalived_interface: "enp1s0.40"
|
||||
|
||||
# Tagged VLAN 40 sub-interface (switch port permits VLAN 40; VLAN 30 stays
|
||||
# untagged/native). Prerequisite for ntp_keepalived_enabled.
|
||||
edge_vlan40_enabled: true
|
||||
|
||||
# VRRP needs ip_nonlocal_bind but NOT ip_forward. The role's default enables
|
||||
# both (correct on the PVE hypervisors); enabling forwarding here would make
|
||||
# edge-1 a router between IoT VLAN 30 and service VLAN 40 once the tagged
|
||||
|
||||
@@ -105,3 +105,14 @@ edge_e3oncan_mqtt_format: "{device}_{didNumber:04d}_{didName}"
|
||||
# edge_sml2mqtt_mqtt_password: "{{ vault_edge_sml2mqtt_mqtt_password }}"
|
||||
# edge_open3e_mqtt_password: "{{ vault_edge_open3e_mqtt_password }}"
|
||||
# edge_zwavejs_session_secret: "{{ vault_edge_zwavejs_session_secret }}"
|
||||
|
||||
# --- Tagged VLAN 40 sub-interface. Off by default; enabled in
|
||||
# inventory/group_vars/edge_host. Exists so edge-1 can join the NTP keepalived
|
||||
# VRRP group, which needs layer-2 adjacency with the other members on the
|
||||
# service VLAN. The untagged VLAN 30 lease stays this host's only default
|
||||
# route -- see templates/10-vlan40.network.j2.
|
||||
edge_vlan40_enabled: false
|
||||
edge_vlan40_parent: enp1s0
|
||||
edge_vlan40_id: 40
|
||||
edge_vlan40_name: enp1s0.40
|
||||
edge_vlan40_parent_network: 20-wired.network
|
||||
|
||||
@@ -6,6 +6,15 @@
|
||||
cmd: networkctl reload && networkctl reconfigure {{ edge_can_interface }}
|
||||
changed_when: true
|
||||
|
||||
# Reconfiguring the parent is what actually materialises the VLAN child -- a
|
||||
# bare reload leaves the existing link as-is (see above). This re-runs DHCP on
|
||||
# enp1s0, which is this headless host's only path to the network, so it is the
|
||||
# one genuinely risky step in the VLAN change.
|
||||
- name: Reload networkd for VLAN
|
||||
ansible.builtin.shell:
|
||||
cmd: networkctl reload && networkctl reconfigure {{ edge_vlan40_parent }}
|
||||
changed_when: true
|
||||
|
||||
# Ordered before 'Restart edge compose services': the daemon must pick up the
|
||||
# new default log driver before the containers are recreated onto it.
|
||||
- name: Restart docker
|
||||
|
||||
@@ -172,6 +172,45 @@
|
||||
mode: "0644"
|
||||
notify: Reload networkd
|
||||
|
||||
# --- Tagged VLAN 40. Additive by construction: the parent link keeps its own
|
||||
# .network file untouched and gains the VLAN via a drop-in, so reverting is a
|
||||
# matter of deleting these three paths. This host is headless and enp1s0 is its
|
||||
# only route to the network, hence the caution.
|
||||
- name: Create VLAN sub-interface netdev
|
||||
ansible.builtin.template:
|
||||
src: 10-vlan40.netdev.j2
|
||||
dest: "/etc/systemd/network/10-{{ edge_vlan40_name }}.netdev"
|
||||
mode: "0644"
|
||||
when: edge_vlan40_enabled
|
||||
notify: Reload networkd for VLAN
|
||||
tags: [vlan40]
|
||||
|
||||
- name: Configure VLAN sub-interface addressing
|
||||
ansible.builtin.template:
|
||||
src: 10-vlan40.network.j2
|
||||
dest: "/etc/systemd/network/10-{{ edge_vlan40_name }}.network"
|
||||
mode: "0644"
|
||||
when: edge_vlan40_enabled
|
||||
notify: Reload networkd for VLAN
|
||||
tags: [vlan40]
|
||||
|
||||
- name: Ensure parent network drop-in directory exists
|
||||
ansible.builtin.file:
|
||||
path: "/etc/systemd/network/{{ edge_vlan40_parent_network }}.d"
|
||||
state: directory
|
||||
mode: "0755"
|
||||
when: edge_vlan40_enabled
|
||||
tags: [vlan40]
|
||||
|
||||
- name: Attach VLAN to parent link via drop-in
|
||||
ansible.builtin.template:
|
||||
src: vlan40-parent-dropin.conf.j2
|
||||
dest: "/etc/systemd/network/{{ edge_vlan40_parent_network }}.d/10-vlan40.conf"
|
||||
mode: "0644"
|
||||
when: edge_vlan40_enabled
|
||||
notify: Reload networkd for VLAN
|
||||
tags: [vlan40]
|
||||
|
||||
- name: Enable systemd-networkd
|
||||
ansible.builtin.service:
|
||||
name: systemd-networkd
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
# Managed by Ansible - Do not edit manually
|
||||
# Tagged VLAN {{ edge_vlan40_id }} sub-interface on {{ edge_vlan40_parent }}.
|
||||
# Needed so edge-1 can join the NTP keepalived VRRP group, which requires
|
||||
# layer-2 adjacency with the other members on the service VLAN.
|
||||
|
||||
[NetDev]
|
||||
Name={{ edge_vlan40_name }}
|
||||
Kind=vlan
|
||||
|
||||
[VLAN]
|
||||
Id={{ edge_vlan40_id }}
|
||||
@@ -0,0 +1,26 @@
|
||||
# Managed by Ansible - Do not edit manually
|
||||
# Address configuration for the VLAN {{ edge_vlan40_id }} sub-interface.
|
||||
#
|
||||
# Filename sorts before 20-wired.network deliberately. networkd applies the
|
||||
# first matching .network in lexicographic order, and 20-wired.network matches
|
||||
# broadly on Type=ether. A VLAN link types as 'vlan' rather than 'ether' so it
|
||||
# should not match anyway, but ordering plus an explicit Name= match means this
|
||||
# file wins regardless.
|
||||
#
|
||||
# The Use*=false settings are the important part: VLAN 30 (untagged) remains
|
||||
# this host's only default route and DNS source. Accepting routes, DNS or RAs
|
||||
# from a second DHCP lease here would install a competing default route and
|
||||
# make return traffic asymmetric across the two VLANs.
|
||||
|
||||
[Match]
|
||||
Name={{ edge_vlan40_name }}
|
||||
|
||||
[Network]
|
||||
DHCP=ipv4
|
||||
IPv6AcceptRA=false
|
||||
|
||||
[DHCPv4]
|
||||
UseRoutes=false
|
||||
UseDNS=false
|
||||
UseNTP=false
|
||||
UseHostname=false
|
||||
@@ -0,0 +1,10 @@
|
||||
# Managed by Ansible - Do not edit manually
|
||||
# Drop-in for {{ edge_vlan40_parent_network }}, attaching the tagged VLAN
|
||||
# sub-interface to the parent link.
|
||||
#
|
||||
# Deliberately a drop-in rather than an edit of the parent .network file: that
|
||||
# file carries this headless host's only route to the network, so the change is
|
||||
# kept additive and revertible by deleting this directory.
|
||||
|
||||
[Network]
|
||||
VLAN={{ edge_vlan40_name }}
|
||||
Reference in New Issue
Block a user