feat(network): make interfaces[] canonical, normalize flat fields as AWX compat
Flat network fields (bridge, ip, prefix, gateway, vlan) are now converted into a single-entry interfaces[] list during normalization. All virtualization tasks (proxmox, vmware, libvirt, xen) and configuration (NM, Alpine, Void) now consume system_cfg.network.interfaces exclusively for multi-NIC support. Also fixes: user.key -> user.keys in system_cfg output, strict list-only DNS in example inventories, removes legacy single-MAC virtualization_mac_address default.
This commit is contained in:
@@ -45,7 +45,8 @@ all:
|
|||||||
user:
|
user:
|
||||||
name: "ops"
|
name: "ops"
|
||||||
password: "CHANGE_ME"
|
password: "CHANGE_ME"
|
||||||
key: "ssh-ed25519 AAAA..."
|
keys:
|
||||||
|
- "ssh-ed25519 AAAA..."
|
||||||
root:
|
root:
|
||||||
password: "CHANGE_ME"
|
password: "CHANGE_ME"
|
||||||
packages:
|
packages:
|
||||||
@@ -89,7 +90,9 @@ all:
|
|||||||
prefix: 24
|
prefix: 24
|
||||||
gateway: 10.0.0.1
|
gateway: 10.0.0.1
|
||||||
dns:
|
dns:
|
||||||
servers: "1.1.1.1,1.0.0.1"
|
servers:
|
||||||
|
- "1.1.1.1"
|
||||||
|
- "1.0.0.1"
|
||||||
disks:
|
disks:
|
||||||
- size: 80
|
- size: 80
|
||||||
- size: 200
|
- size: 200
|
||||||
@@ -99,7 +102,8 @@ all:
|
|||||||
user:
|
user:
|
||||||
name: "dbadmin"
|
name: "dbadmin"
|
||||||
password: "CHANGE_ME"
|
password: "CHANGE_ME"
|
||||||
key: "ssh-ed25519 AAAA..."
|
keys:
|
||||||
|
- "ssh-ed25519 AAAA..."
|
||||||
root:
|
root:
|
||||||
password: "CHANGE_ME"
|
password: "CHANGE_ME"
|
||||||
luks:
|
luks:
|
||||||
|
|||||||
@@ -42,7 +42,8 @@ all:
|
|||||||
user:
|
user:
|
||||||
name: "web"
|
name: "web"
|
||||||
password: "CHANGE_ME"
|
password: "CHANGE_ME"
|
||||||
key: "ssh-ed25519 AAAA..."
|
keys:
|
||||||
|
- "ssh-ed25519 AAAA..."
|
||||||
root:
|
root:
|
||||||
password: "CHANGE_ME"
|
password: "CHANGE_ME"
|
||||||
packages:
|
packages:
|
||||||
@@ -83,7 +84,8 @@ all:
|
|||||||
user:
|
user:
|
||||||
name: "db"
|
name: "db"
|
||||||
password: "CHANGE_ME"
|
password: "CHANGE_ME"
|
||||||
key: "ssh-ed25519 AAAA..."
|
keys:
|
||||||
|
- "ssh-ed25519 AAAA..."
|
||||||
root:
|
root:
|
||||||
password: "CHANGE_ME"
|
password: "CHANGE_ME"
|
||||||
luks:
|
luks:
|
||||||
@@ -111,7 +113,9 @@ all:
|
|||||||
prefix: 24
|
prefix: 24
|
||||||
gateway: 192.168.122.1
|
gateway: 192.168.122.1
|
||||||
dns:
|
dns:
|
||||||
servers: "1.1.1.1,1.0.0.1"
|
servers:
|
||||||
|
- "1.1.1.1"
|
||||||
|
- "1.0.0.1"
|
||||||
disks:
|
disks:
|
||||||
- size: 80
|
- size: 80
|
||||||
- size: 200
|
- size: 200
|
||||||
@@ -121,7 +125,8 @@ all:
|
|||||||
user:
|
user:
|
||||||
name: "compute"
|
name: "compute"
|
||||||
password: "CHANGE_ME"
|
password: "CHANGE_ME"
|
||||||
key: "ssh-ed25519 AAAA..."
|
keys:
|
||||||
|
- "ssh-ed25519 AAAA..."
|
||||||
root:
|
root:
|
||||||
password: "CHANGE_ME"
|
password: "CHANGE_ME"
|
||||||
features:
|
features:
|
||||||
|
|||||||
6
main.yml
6
main.yml
@@ -44,9 +44,9 @@
|
|||||||
'name': (
|
'name': (
|
||||||
(system_user_input.name | default('') | string | length) > 0
|
(system_user_input.name | default('') | string | length) > 0
|
||||||
) | ternary(system_user_input.name | string, prompt_user_name),
|
) | ternary(system_user_input.name | string, prompt_user_name),
|
||||||
'key': (
|
'keys': (
|
||||||
system_user_input.key
|
system_user_input.keys
|
||||||
if (system_user_input.key is iterable and system_user_input.key is not string and system_user_input.key | length > 0)
|
if (system_user_input.keys is iterable and system_user_input.keys is not string and system_user_input.keys | length > 0)
|
||||||
else (
|
else (
|
||||||
[prompt_user_key]
|
[prompt_user_key]
|
||||||
if (prompt_user_key | length > 0)
|
if (prompt_user_key | length > 0)
|
||||||
|
|||||||
@@ -1,9 +1,4 @@
|
|||||||
---
|
---
|
||||||
- name: Generate UUID for Network Profile
|
|
||||||
ansible.builtin.set_fact:
|
|
||||||
configuration_net_uuid: "{{ ('LAN-' ~ hostname) | ansible.builtin.to_uuid }}"
|
|
||||||
changed_when: false
|
|
||||||
|
|
||||||
- name: Read network interfaces
|
- name: Read network interfaces
|
||||||
ansible.builtin.command:
|
ansible.builtin.command:
|
||||||
argv:
|
argv:
|
||||||
@@ -15,81 +10,41 @@
|
|||||||
changed_when: false
|
changed_when: false
|
||||||
failed_when: false
|
failed_when: false
|
||||||
|
|
||||||
- name: Resolve network interface and MAC address
|
- name: Detect available network interface names
|
||||||
vars:
|
vars:
|
||||||
configuration_net_inf_from_facts: "{{ (ansible_default_ipv4 | default({})).get('interface', '') }}"
|
configuration_detected_interfaces: >-
|
||||||
configuration_net_inf_from_ip: >-
|
|
||||||
{{
|
{{
|
||||||
(
|
|
||||||
configuration_ip_link.stdout
|
configuration_ip_link.stdout
|
||||||
| default('')
|
| default('')
|
||||||
| regex_findall('^[0-9]+: ([^:]+):', multiline=True)
|
| regex_findall('^[0-9]+: ([^:]+):', multiline=True)
|
||||||
| reject('equalto', 'lo')
|
| reject('equalto', 'lo')
|
||||||
| list
|
| list
|
||||||
| first
|
|
||||||
)
|
|
||||||
| default('')
|
|
||||||
}}
|
|
||||||
configuration_net_inf_detected: >-
|
|
||||||
{{ configuration_net_inf_from_facts | default(configuration_net_inf_from_ip, true) }}
|
|
||||||
configuration_net_inf_regex: "{{ configuration_net_inf_detected | ansible.builtin.regex_escape }}"
|
|
||||||
configuration_net_mac_from_virtualization: "{{ virtualization_mac_address | default('') }}"
|
|
||||||
configuration_net_mac_from_facts: >-
|
|
||||||
{{
|
|
||||||
(
|
|
||||||
(ansible_facts | default({})).get(configuration_net_inf_detected, {}).get('macaddress', '')
|
|
||||||
)
|
|
||||||
| default(
|
|
||||||
(ansible_facts | default({})).get('ansible_' + configuration_net_inf_detected, {}).get('macaddress', ''),
|
|
||||||
true
|
|
||||||
)
|
|
||||||
}}
|
|
||||||
configuration_net_mac_from_ip: >-
|
|
||||||
{{
|
|
||||||
(
|
|
||||||
configuration_ip_link.stdout
|
|
||||||
| default('')
|
|
||||||
| regex_findall(
|
|
||||||
'^\\d+: ' ~ configuration_net_inf_regex ~ ':.*?link/ether\\s+([0-9A-Fa-f:]{17})',
|
|
||||||
multiline=True
|
|
||||||
)
|
|
||||||
| first
|
|
||||||
)
|
|
||||||
| default('')
|
|
||||||
}}
|
}}
|
||||||
ansible.builtin.set_fact:
|
ansible.builtin.set_fact:
|
||||||
configuration_net_inf: "{{ configuration_net_inf_detected }}"
|
configuration_detected_interfaces: "{{ configuration_detected_interfaces }}"
|
||||||
configuration_net_mac: >-
|
|
||||||
{{
|
|
||||||
(
|
|
||||||
configuration_net_mac_from_virtualization
|
|
||||||
| default(configuration_net_mac_from_facts, true)
|
|
||||||
| default(configuration_net_mac_from_ip, true)
|
|
||||||
)
|
|
||||||
| upper
|
|
||||||
}}
|
|
||||||
changed_when: false
|
|
||||||
|
|
||||||
- name: Validate Network Interface Name
|
- name: Validate at least one network interface detected
|
||||||
ansible.builtin.assert:
|
ansible.builtin.assert:
|
||||||
that:
|
that:
|
||||||
- configuration_net_inf | length > 0
|
- configuration_detected_interfaces | length > 0
|
||||||
fail_msg: Failed to detect an active network interface.
|
fail_msg: Failed to detect any network interfaces.
|
||||||
|
|
||||||
- name: Validate Network Interface MAC Address
|
- name: Configure NetworkManager profiles
|
||||||
ansible.builtin.assert:
|
|
||||||
that:
|
|
||||||
- configuration_net_mac | length > 0
|
|
||||||
fail_msg: Failed to detect the MAC address for network interface {{ configuration_net_inf }}.
|
|
||||||
|
|
||||||
- name: Configure NetworkManager profile
|
|
||||||
when: os | lower not in ["alpine", "void"]
|
when: os | lower not in ["alpine", "void"]
|
||||||
block:
|
block:
|
||||||
- name: Copy NetworkManager keyfile
|
- name: Copy NetworkManager keyfile per interface
|
||||||
|
vars:
|
||||||
|
configuration_iface: "{{ item }}"
|
||||||
|
configuration_iface_name: "{{ configuration_detected_interfaces[idx] | default('eth' ~ idx) }}"
|
||||||
|
configuration_net_uuid: "{{ ('LAN-' ~ idx ~ '-' ~ hostname) | ansible.builtin.to_uuid }}"
|
||||||
ansible.builtin.template:
|
ansible.builtin.template:
|
||||||
src: network.j2
|
src: network.j2
|
||||||
dest: /mnt/etc/NetworkManager/system-connections/LAN.nmconnection
|
dest: "/mnt/etc/NetworkManager/system-connections/LAN-{{ idx }}.nmconnection"
|
||||||
mode: "0600"
|
mode: "0600"
|
||||||
|
loop: "{{ system_cfg.network.interfaces }}"
|
||||||
|
loop_control:
|
||||||
|
index_var: idx
|
||||||
|
label: "LAN-{{ idx }}"
|
||||||
|
|
||||||
- name: Fix Ubuntu unmanaged devices
|
- name: Fix Ubuntu unmanaged devices
|
||||||
when: os | lower in ["ubuntu", "ubuntu-lts"]
|
when: os | lower in ["ubuntu", "ubuntu-lts"]
|
||||||
@@ -102,13 +57,6 @@
|
|||||||
when: os | lower == "alpine"
|
when: os | lower == "alpine"
|
||||||
vars:
|
vars:
|
||||||
configuration_dns_list: "{{ system_cfg.network.dns.servers | default([]) }}"
|
configuration_dns_list: "{{ system_cfg.network.dns.servers | default([]) }}"
|
||||||
configuration_alpine_static: >-
|
|
||||||
{{
|
|
||||||
system_cfg.network.ip is defined
|
|
||||||
and system_cfg.network.ip | string | length > 0
|
|
||||||
and system_cfg.network.prefix is defined
|
|
||||||
and (system_cfg.network.prefix | string | length) > 0
|
|
||||||
}}
|
|
||||||
block:
|
block:
|
||||||
- name: Write Alpine network interfaces
|
- name: Write Alpine network interfaces
|
||||||
ansible.builtin.copy:
|
ansible.builtin.copy:
|
||||||
@@ -117,15 +65,19 @@
|
|||||||
content: |
|
content: |
|
||||||
auto lo
|
auto lo
|
||||||
iface lo inet loopback
|
iface lo inet loopback
|
||||||
|
{% for iface in system_cfg.network.interfaces %}
|
||||||
|
{% set iface_name = configuration_detected_interfaces[loop.index0] | default(iface.name | default('eth' ~ loop.index0)) %}
|
||||||
|
{% set has_static = (iface.ip | default('') | string | length) > 0 %}
|
||||||
|
|
||||||
auto {{ configuration_net_inf }}
|
auto {{ iface_name }}
|
||||||
iface {{ configuration_net_inf }} inet {{ 'static' if configuration_alpine_static | bool else 'dhcp' }}
|
iface {{ iface_name }} inet {{ 'static' if has_static else 'dhcp' }}
|
||||||
{% if configuration_alpine_static | bool %}
|
{% if has_static %}
|
||||||
address {{ system_cfg.network.ip }}/{{ system_cfg.network.prefix }}
|
address {{ iface.ip }}/{{ iface.prefix }}
|
||||||
{% if system_cfg.network.gateway is defined and system_cfg.network.gateway | string | length %}
|
{% if iface.gateway | default('') | string | length %}
|
||||||
gateway {{ system_cfg.network.gateway }}
|
gateway {{ iface.gateway }}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
{% endfor %}
|
||||||
|
|
||||||
- name: Set Alpine DNS resolvers
|
- name: Set Alpine DNS resolvers
|
||||||
when: configuration_dns_list | length > 0
|
when: configuration_dns_list | length > 0
|
||||||
@@ -141,25 +93,24 @@
|
|||||||
when: os | lower == "void"
|
when: os | lower == "void"
|
||||||
vars:
|
vars:
|
||||||
configuration_dns_list: "{{ system_cfg.network.dns.servers | default([]) }}"
|
configuration_dns_list: "{{ system_cfg.network.dns.servers | default([]) }}"
|
||||||
configuration_void_static: >-
|
|
||||||
{{
|
|
||||||
system_cfg.network.ip is defined
|
|
||||||
and system_cfg.network.ip | string | length > 0
|
|
||||||
and system_cfg.network.prefix is defined
|
|
||||||
and (system_cfg.network.prefix | string | length) > 0
|
|
||||||
}}
|
|
||||||
block:
|
block:
|
||||||
- name: Write dhcpcd configuration for static networking
|
- name: Write dhcpcd configuration
|
||||||
when: configuration_void_static | bool
|
|
||||||
ansible.builtin.copy:
|
ansible.builtin.copy:
|
||||||
dest: /mnt/etc/dhcpcd.conf
|
dest: /mnt/etc/dhcpcd.conf
|
||||||
mode: "0644"
|
mode: "0644"
|
||||||
content: |
|
content: |
|
||||||
interface {{ configuration_net_inf }}
|
{% for iface in system_cfg.network.interfaces %}
|
||||||
static ip_address={{ system_cfg.network.ip }}/{{ system_cfg.network.prefix }}
|
{% set iface_name = configuration_detected_interfaces[loop.index0] | default(iface.name | default('eth' ~ loop.index0)) %}
|
||||||
{% if system_cfg.network.gateway is defined and system_cfg.network.gateway | string | length %}
|
{% set has_static = (iface.ip | default('') | string | length) > 0 %}
|
||||||
static routers={{ system_cfg.network.gateway }}
|
{% if has_static %}
|
||||||
|
interface {{ iface_name }}
|
||||||
|
static ip_address={{ iface.ip }}/{{ iface.prefix }}
|
||||||
|
{% if iface.gateway | default('') | string | length %}
|
||||||
|
static routers={{ iface.gateway }}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if configuration_dns_list | length > 0 %}
|
{% if loop.index0 == 0 and configuration_dns_list | length > 0 %}
|
||||||
static domain_name_servers={{ configuration_dns_list | join(' ') }}
|
static domain_name_servers={{ configuration_dns_list | join(' ') }}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
|
{% endif %}
|
||||||
|
{% endfor %}
|
||||||
|
|||||||
@@ -18,7 +18,7 @@
|
|||||||
changed_when: configuration_user_result.rc == 0
|
changed_when: configuration_user_result.rc == 0
|
||||||
|
|
||||||
- name: Ensure .ssh directory exists
|
- name: Ensure .ssh directory exists
|
||||||
when: system_cfg.user.key | length > 0
|
when: system_cfg.user.keys | length > 0
|
||||||
ansible.builtin.file:
|
ansible.builtin.file:
|
||||||
path: /mnt/home/{{ system_cfg.user.name }}/.ssh
|
path: /mnt/home/{{ system_cfg.user.name }}/.ssh
|
||||||
state: directory
|
state: directory
|
||||||
@@ -27,7 +27,7 @@
|
|||||||
mode: "0700"
|
mode: "0700"
|
||||||
|
|
||||||
- name: Add SSH public keys to authorized_keys
|
- name: Add SSH public keys to authorized_keys
|
||||||
when: system_cfg.user.key | length > 0
|
when: system_cfg.user.keys | length > 0
|
||||||
ansible.builtin.lineinfile:
|
ansible.builtin.lineinfile:
|
||||||
path: /mnt/home/{{ system_cfg.user.name }}/.ssh/authorized_keys
|
path: /mnt/home/{{ system_cfg.user.name }}/.ssh/authorized_keys
|
||||||
line: "{{ item }}"
|
line: "{{ item }}"
|
||||||
@@ -35,4 +35,4 @@
|
|||||||
group: 1000
|
group: 1000
|
||||||
mode: "0600"
|
mode: "0600"
|
||||||
create: true
|
create: true
|
||||||
loop: "{{ system_cfg.user.key }}"
|
loop: "{{ system_cfg.user.keys }}"
|
||||||
|
|||||||
@@ -1,24 +1,26 @@
|
|||||||
[connection]
|
[connection]
|
||||||
id=LAN
|
id=LAN-{{ idx }}
|
||||||
uuid={{ configuration_net_uuid }}
|
uuid={{ configuration_net_uuid }}
|
||||||
type=ethernet
|
type=ethernet
|
||||||
|
interface-name={{ configuration_iface_name }}
|
||||||
|
|
||||||
[ipv4]
|
[ipv4]
|
||||||
|
{% set iface = configuration_iface %}
|
||||||
{% set dns_list = system_cfg.network.dns.servers | default([]) %}
|
{% set dns_list = system_cfg.network.dns.servers | default([]) %}
|
||||||
{% set search_list = system_cfg.network.dns.search | default([]) %}
|
{% set search_list = system_cfg.network.dns.search | default([]) %}
|
||||||
{% if system_cfg.network.ip is defined and system_cfg.network.ip | string | length %}
|
{% if iface.ip | default('') | string | length %}
|
||||||
address1={{ system_cfg.network.ip }}/{{ system_cfg.network.prefix }}{{ (',' ~ system_cfg.network.gateway) if (system_cfg.network.gateway is defined and system_cfg.network.gateway | string | length) else '' }}
|
address1={{ iface.ip }}/{{ iface.prefix }}{{ (',' ~ iface.gateway) if (iface.gateway | default('') | string | length) else '' }}
|
||||||
method=manual
|
method=manual
|
||||||
{% else %}
|
{% else %}
|
||||||
method=auto
|
method=auto
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if dns_list %}
|
{% if idx | int == 0 and dns_list %}
|
||||||
dns={{ dns_list | join(';') }}
|
dns={{ dns_list | join(';') }}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if dns_list %}
|
{% if idx | int == 0 and dns_list %}
|
||||||
ignore-auto-dns=true
|
ignore-auto-dns=true
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if search_list %}
|
{% if idx | int == 0 and search_list %}
|
||||||
dns-search={{ search_list | join(';') }}
|
dns-search={{ search_list | join(';') }}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
|
|||||||
@@ -36,13 +36,14 @@ system_defaults:
|
|||||||
dns:
|
dns:
|
||||||
servers: []
|
servers: []
|
||||||
search: []
|
search: []
|
||||||
|
interfaces: []
|
||||||
path: ""
|
path: ""
|
||||||
packages: []
|
packages: []
|
||||||
disks: []
|
disks: []
|
||||||
user:
|
user:
|
||||||
name: ""
|
name: ""
|
||||||
password: ""
|
password: ""
|
||||||
key: []
|
keys: []
|
||||||
root:
|
root:
|
||||||
password: ""
|
password: ""
|
||||||
luks:
|
luks:
|
||||||
|
|||||||
@@ -16,7 +16,7 @@
|
|||||||
fail_msg: "system and its nested keys (network, user, root, luks, features) must be dictionaries."
|
fail_msg: "system and its nested keys (network, user, root, luks, features) must be dictionaries."
|
||||||
quiet: true
|
quiet: true
|
||||||
|
|
||||||
- name: Validate DNS and user.key are lists (not strings)
|
- name: Validate DNS and user.keys are lists (not strings)
|
||||||
when: system.network is defined and system.network.dns is defined
|
when: system.network is defined and system.network.dns is defined
|
||||||
ansible.builtin.assert:
|
ansible.builtin.assert:
|
||||||
that:
|
that:
|
||||||
@@ -25,12 +25,12 @@
|
|||||||
fail_msg: "system.network.dns.servers and system.network.dns.search must be lists, not strings."
|
fail_msg: "system.network.dns.servers and system.network.dns.search must be lists, not strings."
|
||||||
quiet: true
|
quiet: true
|
||||||
|
|
||||||
- name: Validate user.key is a list
|
- name: Validate user.keys is a list
|
||||||
when: system.user is defined and system.user.key is defined
|
when: system.user is defined and system.user.keys is defined
|
||||||
ansible.builtin.assert:
|
ansible.builtin.assert:
|
||||||
that:
|
that:
|
||||||
- system.user.key is iterable and system.user.key is not string
|
- system.user.keys is iterable and system.user.keys is not string
|
||||||
fail_msg: "system.user.key must be a list of SSH public key strings."
|
fail_msg: "system.user.keys must be a list of SSH public key strings."
|
||||||
quiet: true
|
quiet: true
|
||||||
|
|
||||||
- name: Validate system features input types
|
- name: Validate system features input types
|
||||||
@@ -88,6 +88,27 @@
|
|||||||
dns:
|
dns:
|
||||||
servers: "{{ system_raw.network.dns.servers | default([]) }}"
|
servers: "{{ system_raw.network.dns.servers | default([]) }}"
|
||||||
search: "{{ system_raw.network.dns.search | default([]) }}"
|
search: "{{ system_raw.network.dns.search | default([]) }}"
|
||||||
|
interfaces: >-
|
||||||
|
{{
|
||||||
|
system_raw.network.interfaces
|
||||||
|
if (system_raw.network.interfaces | default([]) | length > 0)
|
||||||
|
else (
|
||||||
|
[{
|
||||||
|
'name': 'eth0',
|
||||||
|
'bridge': system_raw.network.bridge | default('') | string,
|
||||||
|
'vlan': system_raw.network.vlan | default('') | string,
|
||||||
|
'ip': system_raw.network.ip | default('') | string,
|
||||||
|
'prefix': (
|
||||||
|
(system_raw.network.prefix | int | string)
|
||||||
|
if (system_raw.network.prefix | default('') | string | length) > 0
|
||||||
|
else ''
|
||||||
|
),
|
||||||
|
'gateway': system_raw.network.gateway | default('') | string
|
||||||
|
}]
|
||||||
|
if (system_raw.network.bridge | default('') | string | length > 0)
|
||||||
|
else []
|
||||||
|
)
|
||||||
|
}}
|
||||||
path: "{{ system_raw.path | default('') | string }}"
|
path: "{{ system_raw.path | default('') | string }}"
|
||||||
packages: >-
|
packages: >-
|
||||||
{{
|
{{
|
||||||
@@ -104,7 +125,7 @@
|
|||||||
user:
|
user:
|
||||||
name: "{{ system_raw.user.name | string }}"
|
name: "{{ system_raw.user.name | string }}"
|
||||||
password: "{{ system_raw.user.password | string }}"
|
password: "{{ system_raw.user.password | string }}"
|
||||||
key: "{{ system_raw.user.key | default([]) }}"
|
keys: "{{ system_raw.user.keys | default([]) }}"
|
||||||
root:
|
root:
|
||||||
password: "{{ system_raw.root.password | string }}"
|
password: "{{ system_raw.root.password | string }}"
|
||||||
luks:
|
luks:
|
||||||
@@ -150,6 +171,25 @@
|
|||||||
os: "{{ system_os_input if system_os_input | length > 0 else ('archlinux' if system_type == 'physical' else '') }}"
|
os: "{{ system_os_input if system_os_input | length > 0 else ('archlinux' if system_type == 'physical' else '') }}"
|
||||||
os_version: "{{ system_raw.version | default('') | string }}"
|
os_version: "{{ system_raw.version | default('') | string }}"
|
||||||
|
|
||||||
|
- name: Populate primary network fields from first interface
|
||||||
|
when:
|
||||||
|
- system_cfg.network.interfaces | length > 0
|
||||||
|
- system_cfg.network.bridge | default('') | string | length == 0
|
||||||
|
vars:
|
||||||
|
_primary: "{{ system_cfg.network.interfaces[0] }}"
|
||||||
|
ansible.builtin.set_fact:
|
||||||
|
system_cfg: >-
|
||||||
|
{{
|
||||||
|
system_cfg | combine({
|
||||||
|
'network': system_cfg.network | combine({
|
||||||
|
'bridge': _primary.bridge | default(''),
|
||||||
|
'vlan': _primary.vlan | default(''),
|
||||||
|
'ip': _primary.ip | default(''),
|
||||||
|
'prefix': _primary.prefix | default(''),
|
||||||
|
'gateway': _primary.gateway | default('')
|
||||||
|
})
|
||||||
|
}, recursive=True)
|
||||||
|
}}
|
||||||
|
|
||||||
- name: Normalize system disks input
|
- name: Normalize system disks input
|
||||||
vars:
|
vars:
|
||||||
|
|||||||
@@ -171,8 +171,12 @@
|
|||||||
- hypervisor_cfg.host | string | length > 0
|
- hypervisor_cfg.host | string | length > 0
|
||||||
- hypervisor_cfg.storage | string | length > 0
|
- hypervisor_cfg.storage | string | length > 0
|
||||||
- system_cfg.id | string | length > 0
|
- system_cfg.id | string | length > 0
|
||||||
- system_cfg.network.bridge | string | length > 0
|
- >-
|
||||||
fail_msg: "Missing required Proxmox inputs. Define hypervisor.(url,username,password,host,storage), system.id, and system.network.bridge."
|
(system_cfg.network.bridge | default('') | string | length > 0)
|
||||||
|
or (system_cfg.network.interfaces | default([]) | length > 0)
|
||||||
|
fail_msg: >-
|
||||||
|
Missing required Proxmox inputs. Define hypervisor.(url,username,password,host,storage),
|
||||||
|
system.id, and system.network.bridge (or system.network.interfaces[]).
|
||||||
quiet: true
|
quiet: true
|
||||||
|
|
||||||
- name: Validate VMware hypervisor inputs
|
- name: Validate VMware hypervisor inputs
|
||||||
@@ -187,8 +191,12 @@
|
|||||||
- hypervisor_cfg.datacenter | string | length > 0
|
- hypervisor_cfg.datacenter | string | length > 0
|
||||||
- hypervisor_cfg.cluster | string | length > 0
|
- hypervisor_cfg.cluster | string | length > 0
|
||||||
- hypervisor_cfg.storage | string | length > 0
|
- hypervisor_cfg.storage | string | length > 0
|
||||||
- system_cfg.network.bridge | string | length > 0
|
- >-
|
||||||
fail_msg: "Missing required VMware inputs. Define hypervisor.(url,username,password,datacenter,cluster,storage) and system.network.bridge."
|
(system_cfg.network.bridge | default('') | string | length > 0)
|
||||||
|
or (system_cfg.network.interfaces | default([]) | length > 0)
|
||||||
|
fail_msg: >-
|
||||||
|
Missing required VMware inputs. Define hypervisor.(url,username,password,datacenter,cluster,storage)
|
||||||
|
and system.network.bridge (or system.network.interfaces[]).
|
||||||
quiet: true
|
quiet: true
|
||||||
|
|
||||||
- name: Validate Xen hypervisor inputs
|
- name: Validate Xen hypervisor inputs
|
||||||
@@ -197,8 +205,10 @@
|
|||||||
- hypervisor_type == "xen"
|
- hypervisor_type == "xen"
|
||||||
ansible.builtin.assert:
|
ansible.builtin.assert:
|
||||||
that:
|
that:
|
||||||
- system_cfg.network.bridge | string | length > 0
|
- >-
|
||||||
fail_msg: "Missing required Xen inputs. Define system.network.bridge."
|
(system_cfg.network.bridge | default('') | string | length > 0)
|
||||||
|
or (system_cfg.network.interfaces | default([]) | length > 0)
|
||||||
|
fail_msg: "Missing required Xen inputs. Define system.network.bridge (or system.network.interfaces[])."
|
||||||
quiet: true
|
quiet: true
|
||||||
|
|
||||||
- name: Validate virtual installer ISO requirement
|
- name: Validate virtual installer ISO requirement
|
||||||
@@ -329,3 +339,15 @@
|
|||||||
- (system_cfg.network.prefix | int) > 0
|
- (system_cfg.network.prefix | int) > 0
|
||||||
fail_msg: "system.network.prefix is required when system.network.ip is set."
|
fail_msg: "system.network.prefix is required when system.network.ip is set."
|
||||||
quiet: true
|
quiet: true
|
||||||
|
|
||||||
|
- name: Validate network interfaces entries
|
||||||
|
when: system_cfg.network.interfaces | default([]) | length > 0
|
||||||
|
ansible.builtin.assert:
|
||||||
|
that:
|
||||||
|
- item is mapping
|
||||||
|
- item.bridge is defined and (item.bridge | string | length) > 0
|
||||||
|
fail_msg: "Each system.network.interfaces[] entry must be a dict with at least a 'bridge' key."
|
||||||
|
quiet: true
|
||||||
|
loop: "{{ system_cfg.network.interfaces }}"
|
||||||
|
loop_control:
|
||||||
|
label: "{{ item | to_json }}"
|
||||||
|
|||||||
@@ -9,8 +9,6 @@ virtualization_libvirt_disk_path: >-
|
|||||||
{{ [virtualization_libvirt_image_dir, hostname ~ '.qcow2'] | ansible.builtin.path_join }}
|
{{ [virtualization_libvirt_image_dir, hostname ~ '.qcow2'] | ansible.builtin.path_join }}
|
||||||
virtualization_libvirt_cloudinit_path: >-
|
virtualization_libvirt_cloudinit_path: >-
|
||||||
{{ [virtualization_libvirt_image_dir, hostname ~ '-cloudinit.iso'] | ansible.builtin.path_join }}
|
{{ [virtualization_libvirt_image_dir, hostname ~ '-cloudinit.iso'] | ansible.builtin.path_join }}
|
||||||
virtualization_mac_address: >-
|
|
||||||
{{ '52:54:00' | community.general.random_mac(seed=hostname) }}
|
|
||||||
virtualization_xen_disk_path: /var/lib/xen/images
|
virtualization_xen_disk_path: /var/lib/xen/images
|
||||||
|
|
||||||
virtualization_tpm2_enabled: >-
|
virtualization_tpm2_enabled: >-
|
||||||
|
|||||||
@@ -8,6 +8,28 @@
|
|||||||
{%- set _ = out.update({ 'scsi' ~ loop.index0: hypervisor_cfg.storage ~ ':' ~ (disk.size | int) }) -%}
|
{%- set _ = out.update({ 'scsi' ~ loop.index0: hypervisor_cfg.storage ~ ':' ~ (disk.size | int) }) -%}
|
||||||
{%- endfor -%}
|
{%- endfor -%}
|
||||||
{{ out }}
|
{{ out }}
|
||||||
|
virtualization_proxmox_net: >-
|
||||||
|
{%- set out = {} -%}
|
||||||
|
{%- for iface in system_cfg.network.interfaces -%}
|
||||||
|
{%- set val = 'virtio,bridge=' ~ iface.bridge -%}
|
||||||
|
{%- if iface.vlan | default('') | string | length > 0 -%}
|
||||||
|
{%- set val = val ~ ',tag=' ~ iface.vlan -%}
|
||||||
|
{%- endif -%}
|
||||||
|
{%- set _ = out.update({ 'net' ~ loop.index0: val }) -%}
|
||||||
|
{%- endfor -%}
|
||||||
|
{{ out }}
|
||||||
|
virtualization_proxmox_ipconfig: >-
|
||||||
|
{%- set out = {} -%}
|
||||||
|
{%- for iface in system_cfg.network.interfaces -%}
|
||||||
|
{%- if iface.ip | default('') | string | length > 0 -%}
|
||||||
|
{%- set val = 'ip=' ~ iface.ip ~ '/' ~ iface.prefix
|
||||||
|
~ ((',gw=' ~ iface.gateway) if (iface.gateway | default('') | length > 0) else '') -%}
|
||||||
|
{%- else -%}
|
||||||
|
{%- set val = 'ip=dhcp' -%}
|
||||||
|
{%- endif -%}
|
||||||
|
{%- set _ = out.update({ 'ipconfig' ~ loop.index0: val }) -%}
|
||||||
|
{%- endfor -%}
|
||||||
|
{{ out }}
|
||||||
community.proxmox.proxmox_kvm:
|
community.proxmox.proxmox_kvm:
|
||||||
api_host: "{{ hypervisor_cfg.url }}"
|
api_host: "{{ hypervisor_cfg.url }}"
|
||||||
api_user: "{{ hypervisor_cfg.username }}"
|
api_user: "{{ hypervisor_cfg.username }}"
|
||||||
@@ -46,20 +68,8 @@
|
|||||||
ide0: "{{ boot_iso }},media=cdrom"
|
ide0: "{{ boot_iso }},media=cdrom"
|
||||||
ide1: "{{ rhel_iso + ',media=cdrom' if rhel_iso is defined and rhel_iso | length > 0 else omit }}"
|
ide1: "{{ rhel_iso + ',media=cdrom' if rhel_iso is defined and rhel_iso | length > 0 else omit }}"
|
||||||
ide2: "{{ hypervisor_cfg.storage }}:cloudinit"
|
ide2: "{{ hypervisor_cfg.storage }}:cloudinit"
|
||||||
net:
|
net: "{{ virtualization_proxmox_net }}"
|
||||||
net0: >-
|
ipconfig: "{{ virtualization_proxmox_ipconfig }}"
|
||||||
virtio,bridge={{ system_cfg.network.bridge
|
|
||||||
}}{%- if system_cfg.network.vlan is defined
|
|
||||||
and system_cfg.network.vlan | string | length > 0
|
|
||||||
%},tag={{ system_cfg.network.vlan }}{% endif %}
|
|
||||||
ipconfig:
|
|
||||||
ipconfig0: >-
|
|
||||||
{{
|
|
||||||
'ip=' ~ system_cfg.network.ip ~ '/' ~ system_cfg.network.prefix
|
|
||||||
~ (',gw=' ~ system_cfg.network.gateway if system_cfg.network.gateway is defined and system_cfg.network.gateway | length else '')
|
|
||||||
if system_cfg.network.ip is defined and system_cfg.network.ip | string | length
|
|
||||||
else 'ip=dhcp'
|
|
||||||
}}
|
|
||||||
nameservers: "{{ system_cfg.network.dns.servers if system_cfg.network.dns.servers | length else omit }}"
|
nameservers: "{{ system_cfg.network.dns.servers if system_cfg.network.dns.servers | length else omit }}"
|
||||||
searchdomains: "{{ system_cfg.network.dns.search if system_cfg.network.dns.search | length else omit }}"
|
searchdomains: "{{ system_cfg.network.dns.search if system_cfg.network.dns.search | length else omit }}"
|
||||||
onboot: true
|
onboot: true
|
||||||
|
|||||||
@@ -14,6 +14,17 @@
|
|||||||
|
|
||||||
- name: Create VM in vCenter
|
- name: Create VM in vCenter
|
||||||
delegate_to: localhost
|
delegate_to: localhost
|
||||||
|
vars:
|
||||||
|
virtualization_vmware_networks: >-
|
||||||
|
{%- set ns = namespace(out=[]) -%}
|
||||||
|
{%- for iface in system_cfg.network.interfaces -%}
|
||||||
|
{%- set entry = {'name': iface.bridge, 'type': 'dhcp'} -%}
|
||||||
|
{%- if (iface.vlan | default('') | string | length) > 0 -%}
|
||||||
|
{%- set entry = entry | combine({'vlan': iface.vlan | int}) -%}
|
||||||
|
{%- endif -%}
|
||||||
|
{%- set ns.out = ns.out + [entry] -%}
|
||||||
|
{%- endfor -%}
|
||||||
|
{{ ns.out }}
|
||||||
community.vmware.vmware_guest:
|
community.vmware.vmware_guest:
|
||||||
hostname: "{{ hypervisor_cfg.url }}"
|
hostname: "{{ hypervisor_cfg.url }}"
|
||||||
username: "{{ hypervisor_cfg.username }}"
|
username: "{{ hypervisor_cfg.username }}"
|
||||||
@@ -53,10 +64,7 @@
|
|||||||
"iso_path": rhel_iso
|
"iso_path": rhel_iso
|
||||||
} ] if rhel_iso is defined and rhel_iso | length > 0 else [] )
|
} ] if rhel_iso is defined and rhel_iso | length > 0 else [] )
|
||||||
}}
|
}}
|
||||||
networks:
|
networks: "{{ virtualization_vmware_networks }}"
|
||||||
- name: "{{ system_cfg.network.bridge }}"
|
|
||||||
type: dhcp
|
|
||||||
vlan: "{{ system_cfg.network.vlan if system_cfg.network.vlan is defined and system_cfg.network.vlan | string | length > 0 else omit }}"
|
|
||||||
register: virtualization_vmware_create_result
|
register: virtualization_vmware_create_result
|
||||||
|
|
||||||
- name: Set VM created fact when VM was powered on during creation
|
- name: Set VM created fact when VM was powered on during creation
|
||||||
|
|||||||
@@ -1,21 +1,23 @@
|
|||||||
network:
|
network:
|
||||||
version: 2
|
version: 2
|
||||||
ethernets:
|
ethernets:
|
||||||
id0:
|
|
||||||
match:
|
|
||||||
macaddress: "{{ virtualization_mac_address }}"
|
|
||||||
{% set has_static = system_cfg.network.ip is defined and system_cfg.network.ip | string | length %}
|
|
||||||
{% set dns_list = system_cfg.network.dns.servers | default([]) %}
|
{% set dns_list = system_cfg.network.dns.servers | default([]) %}
|
||||||
{% set search_list = system_cfg.network.dns.search | default([]) %}
|
{% set search_list = system_cfg.network.dns.search | default([]) %}
|
||||||
|
{% for iface in system_cfg.network.interfaces %}
|
||||||
|
{% set iface_mac = '52:54:00' | community.general.random_mac(seed=hostname if loop.index0 == 0 else hostname ~ '-nic' ~ loop.index0) %}
|
||||||
|
{% set has_static = (iface.ip | default('') | string | length) > 0 %}
|
||||||
|
id{{ loop.index0 }}:
|
||||||
|
match:
|
||||||
|
macaddress: "{{ iface_mac }}"
|
||||||
{% if has_static %}
|
{% if has_static %}
|
||||||
addresses:
|
addresses:
|
||||||
- "{{ system_cfg.network.ip }}/{{ system_cfg.network.prefix }}"
|
- "{{ iface.ip }}/{{ iface.prefix }}"
|
||||||
{% if system_cfg.network.gateway is defined and system_cfg.network.gateway | string | length %}
|
{% if iface.gateway | default('') | string | length %}
|
||||||
gateway4: "{{ system_cfg.network.gateway }}"
|
gateway4: "{{ iface.gateway }}"
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% else %}
|
{% else %}
|
||||||
dhcp4: true
|
dhcp4: true
|
||||||
{% if dns_list | length or search_list | length %}
|
{% if loop.index0 == 0 and (dns_list | length or search_list | length) %}
|
||||||
dhcp4-overrides:
|
dhcp4-overrides:
|
||||||
{% if dns_list | length %}
|
{% if dns_list | length %}
|
||||||
use-dns: false
|
use-dns: false
|
||||||
@@ -25,7 +27,7 @@ network:
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if dns_list or search_list %}
|
{% if loop.index0 == 0 and (dns_list or search_list) %}
|
||||||
nameservers:
|
nameservers:
|
||||||
{% if dns_list %}
|
{% if dns_list %}
|
||||||
addresses:
|
addresses:
|
||||||
@@ -40,3 +42,4 @@ network:
|
|||||||
{% endfor %}
|
{% endfor %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
{% endfor %}
|
||||||
|
|||||||
@@ -46,11 +46,13 @@
|
|||||||
<target dev="sdc" bus="sata"/>
|
<target dev="sdc" bus="sata"/>
|
||||||
</disk>
|
</disk>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
{% for iface in system_cfg.network.interfaces %}
|
||||||
<interface type='network'>
|
<interface type='network'>
|
||||||
<mac address="{{ virtualization_mac_address }}"/>
|
<mac address="{{ '52:54:00' | community.general.random_mac(seed=hostname if loop.index0 == 0 else hostname ~ '-nic' ~ loop.index0) }}"/>
|
||||||
<source network='{{ system_cfg.network.bridge if (system_cfg.network.bridge | default('' ) | string | length) > 0 else "default" }}'/>
|
<source network='{{ iface.bridge | default("default") }}'/>
|
||||||
<model type='virtio'/>
|
<model type='virtio'/>
|
||||||
</interface>
|
</interface>
|
||||||
|
{% endfor %}
|
||||||
{% if virtualization_tpm2_enabled %}
|
{% if virtualization_tpm2_enabled %}
|
||||||
<tpm model='tpm-crb'>
|
<tpm model='tpm-crb'>
|
||||||
<backend type='emulator' version='2.0'/>
|
<backend type='emulator' version='2.0'/>
|
||||||
|
|||||||
@@ -10,7 +10,11 @@ disk = [
|
|||||||
'{{ boot_iso }},,hdc,cdrom'{% if rhel_iso is defined and rhel_iso | length > 0 %}, '{{ rhel_iso }},,hdd,cdrom'{% endif %}
|
'{{ boot_iso }},,hdc,cdrom'{% if rhel_iso is defined and rhel_iso | length > 0 %}, '{{ rhel_iso }},,hdd,cdrom'{% endif %}
|
||||||
{%- endif -%}
|
{%- endif -%}
|
||||||
]
|
]
|
||||||
vif = [ 'bridge={{ system_cfg.network.bridge }},model=e1000' ]
|
vif = [
|
||||||
|
{%- for iface in system_cfg.network.interfaces -%}
|
||||||
|
'bridge={{ iface.bridge }},model=e1000'{% if not loop.last %}, {% endif %}
|
||||||
|
{%- endfor -%}
|
||||||
|
]
|
||||||
boot = "{{ 'dc' if xen_installer_media_enabled | bool else 'c' }}"
|
boot = "{{ 'dc' if xen_installer_media_enabled | bool else 'c' }}"
|
||||||
on_crash = "preserve"
|
on_crash = "preserve"
|
||||||
on_poweroff = "destroy"
|
on_poweroff = "destroy"
|
||||||
|
|||||||
@@ -28,7 +28,8 @@ system:
|
|||||||
user:
|
user:
|
||||||
name: "admin"
|
name: "admin"
|
||||||
password: "CHANGE_ME"
|
password: "CHANGE_ME"
|
||||||
key: "ssh-ed25519 AAAA..."
|
keys:
|
||||||
|
- "ssh-ed25519 AAAA..."
|
||||||
root:
|
root:
|
||||||
password: "CHANGE_ME"
|
password: "CHANGE_ME"
|
||||||
luks:
|
luks:
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ system:
|
|||||||
memory: 8192
|
memory: 8192
|
||||||
balloon: 0
|
balloon: 0
|
||||||
network:
|
network:
|
||||||
|
# Flat fields (AWX survey compatibility, builds single-entry interfaces[])
|
||||||
bridge: "vmbr0"
|
bridge: "vmbr0"
|
||||||
ip: "{{ inventory_hostname }}"
|
ip: "{{ inventory_hostname }}"
|
||||||
prefix: 24
|
prefix: 24
|
||||||
@@ -35,6 +36,16 @@ system:
|
|||||||
- "1.0.0.1"
|
- "1.0.0.1"
|
||||||
search:
|
search:
|
||||||
- "example.com"
|
- "example.com"
|
||||||
|
# Multi-NIC: use interfaces[] instead of flat fields above
|
||||||
|
# interfaces:
|
||||||
|
# - name: "eth0"
|
||||||
|
# bridge: "vmbr0"
|
||||||
|
# ip: "10.0.0.10"
|
||||||
|
# prefix: 24
|
||||||
|
# gateway: "10.0.0.1"
|
||||||
|
# - name: "eth1"
|
||||||
|
# bridge: "vmbr1"
|
||||||
|
# vlan: "100"
|
||||||
path: "/Lab/Example"
|
path: "/Lab/Example"
|
||||||
disks:
|
disks:
|
||||||
- size: 80
|
- size: 80
|
||||||
@@ -47,7 +58,8 @@ system:
|
|||||||
user:
|
user:
|
||||||
name: "ops"
|
name: "ops"
|
||||||
password: "CHANGE_ME"
|
password: "CHANGE_ME"
|
||||||
key: "ssh-ed25519 AAAA..."
|
keys:
|
||||||
|
- "ssh-ed25519 AAAA..."
|
||||||
root:
|
root:
|
||||||
password: "CHANGE_ME"
|
password: "CHANGE_ME"
|
||||||
luks:
|
luks:
|
||||||
|
|||||||
Reference in New Issue
Block a user