feat(bootstrap): accept extra_addresses on network interfaces
This commit is contained in:
@@ -213,7 +213,7 @@ Uniform package content source, family-resolved. `source: ''` defaults to `dvd`
|
|||||||
| `dns.search` | list | `[]` | Search domains (must be a YAML list) |
|
| `dns.search` | list | `[]` | Search domains (must be a YAML list) |
|
||||||
| `interfaces` | list | `[]` | Multi-NIC config (overrides flat fields above) |
|
| `interfaces` | list | `[]` | Multi-NIC config (overrides flat fields above) |
|
||||||
|
|
||||||
When `interfaces` is empty, the flat fields (`bridge`, `ip`, `prefix`, `gateway`, `vlan`) are auto-wrapped into a single-entry list. When `interfaces` is set, it takes precedence. Each entry supports: `name`, `bridge` (required), `vlan`, `ip`, `prefix`, `gateway`.
|
When `interfaces` is empty, the flat fields (`bridge`, `ip`, `prefix`, `gateway`, `vlan`) are auto-wrapped into a single-entry list. When `interfaces` is set, it takes precedence. Each entry supports: `name`, `bridge` (required), `vlan`, `ip`, `prefix`, `gateway`, and `extra_addresses`: further IPv4 CIDRs (`192.0.2.10/24`) on the same NIC beside `ip`, written by every network backend. Proxmox `ipconfigN` carries one address per NIC, so on Proxmox the extras arrive with the installed network config, not cloud-init.
|
||||||
|
|
||||||
#### `system.users`
|
#### `system.users`
|
||||||
|
|
||||||
|
|||||||
@@ -19,6 +19,9 @@ interface-name=en*;eth*;
|
|||||||
{% set search_list = configuration_dns_search %}
|
{% set search_list = configuration_dns_search %}
|
||||||
{% if iface.ip | default('') | string | length %}
|
{% if iface.ip | default('') | string | length %}
|
||||||
address1={{ iface.ip }}/{{ iface.prefix }}{{ (',' ~ iface.gateway) if (iface.gateway | default('') | string | length) else '' }}
|
address1={{ iface.ip }}/{{ iface.prefix }}{{ (',' ~ iface.gateway) if (iface.gateway | default('') | string | length) else '' }}
|
||||||
|
{% for address in iface.extra_addresses | default([]) %}
|
||||||
|
address{{ loop.index + 1 }}={{ address }}
|
||||||
|
{% endfor %}
|
||||||
method=manual
|
method=manual
|
||||||
{% else %}
|
{% else %}
|
||||||
method=auto
|
method=auto
|
||||||
|
|||||||
@@ -16,6 +16,11 @@ iface {{ ifname }} inet static
|
|||||||
{% if loop.index0 == 0 and configuration_dns_search %}
|
{% if loop.index0 == 0 and configuration_dns_search %}
|
||||||
dns-search {{ configuration_dns_search | join(' ') }}
|
dns-search {{ configuration_dns_search | join(' ') }}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
{# One stanza per extra address: classic ifupdown takes one address per stanza, ifupdown2 merges them. #}
|
||||||
|
{% for address in iface.extra_addresses | default([]) %}
|
||||||
|
iface {{ ifname }} inet static
|
||||||
|
address {{ address }}
|
||||||
|
{% endfor %}
|
||||||
{% else %}
|
{% else %}
|
||||||
iface {{ ifname }} inet dhcp
|
iface {{ ifname }} inet dhcp
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|||||||
@@ -9,6 +9,9 @@ network:
|
|||||||
{% if iface.ip | default('') | string | length %}
|
{% if iface.ip | default('') | string | length %}
|
||||||
addresses:
|
addresses:
|
||||||
- {{ iface.ip }}/{{ iface.prefix }}
|
- {{ iface.ip }}/{{ iface.prefix }}
|
||||||
|
{% for address in iface.extra_addresses | default([]) %}
|
||||||
|
- {{ address }}
|
||||||
|
{% endfor %}
|
||||||
{% if iface.gateway | default('') | string | length %}
|
{% if iface.gateway | default('') | string | length %}
|
||||||
routes:
|
routes:
|
||||||
- to: default
|
- to: default
|
||||||
|
|||||||
@@ -11,6 +11,9 @@ Type=ether
|
|||||||
[Network]
|
[Network]
|
||||||
{% if iface.ip | default('') | string | length %}
|
{% if iface.ip | default('') | string | length %}
|
||||||
Address={{ iface.ip }}/{{ iface.prefix }}
|
Address={{ iface.ip }}/{{ iface.prefix }}
|
||||||
|
{% for address in iface.extra_addresses | default([]) %}
|
||||||
|
Address={{ address }}
|
||||||
|
{% endfor %}
|
||||||
{% if iface.gateway | default('') | string | length %}
|
{% if iface.gateway | default('') | string | length %}
|
||||||
Gateway={{ iface.gateway }}
|
Gateway={{ iface.gateway }}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|||||||
@@ -467,6 +467,21 @@
|
|||||||
loop_control:
|
loop_control:
|
||||||
label: "{{ item | to_json }}"
|
label: "{{ item | to_json }}"
|
||||||
|
|
||||||
|
# A string here would iterate into one address per character in every network renderer.
|
||||||
|
- name: Validate network interfaces extra_addresses
|
||||||
|
when: item.extra_addresses is defined
|
||||||
|
ansible.builtin.assert:
|
||||||
|
that:
|
||||||
|
- item.extra_addresses is not string
|
||||||
|
- item.extra_addresses is iterable
|
||||||
|
- item.ip | default('') | string | length > 0
|
||||||
|
- item.extra_addresses | reject('match', '^(\\d{1,3}\\.){3}\\d{1,3}/\\d{1,2}$') | list | length == 0
|
||||||
|
fail_msg: "system.network.interfaces[].extra_addresses must be a list of IPv4 CIDRs (192.0.2.10/24) on an entry that also sets 'ip'."
|
||||||
|
quiet: true
|
||||||
|
loop: "{{ system_cfg.network.interfaces | default([]) }}"
|
||||||
|
loop_control:
|
||||||
|
label: "{{ item | to_json }}"
|
||||||
|
|
||||||
- name: Validate hostname format
|
- name: Validate hostname format
|
||||||
ansible.builtin.assert:
|
ansible.builtin.assert:
|
||||||
that:
|
that:
|
||||||
|
|||||||
@@ -12,6 +12,9 @@ network:
|
|||||||
{% if has_static %}
|
{% if has_static %}
|
||||||
addresses:
|
addresses:
|
||||||
- "{{ iface.ip }}/{{ iface.prefix }}"
|
- "{{ iface.ip }}/{{ iface.prefix }}"
|
||||||
|
{% for address in iface.extra_addresses | default([]) %}
|
||||||
|
- "{{ address }}"
|
||||||
|
{% endfor %}
|
||||||
{% if iface.gateway | default('') | string | length %}
|
{% if iface.gateway | default('') | string | length %}
|
||||||
routes:
|
routes:
|
||||||
- to: default
|
- to: default
|
||||||
|
|||||||
Reference in New Issue
Block a user