From cb02612fd25e7d45cc916b514fe4fc6277612216 Mon Sep 17 00:00:00 2001 From: Michal Opala Date: Wed, 9 Sep 2026 14:36:32 +0200 Subject: [PATCH 1/2] M #-: Refactor infra role / Introduce variants - Replace frontend.xml.jinja template with mergeable structures - Introduce variants: default, opevswitch, opevswitch_dpdk - Add ipv4_mac filter - Update README / Add inventory examples for each variant Signed-off-by: Michal Opala --- playbooks/infra.yml | 2 +- plugins/filter/ipv4_mac.yml | 35 ++++ plugins/filter/main.py | 10 +- plugins/module_utils/main.py | 10 + roles/infra/README.md | 157 +++++++++++++-- roles/infra/defaults/main.yml | 232 ++++++++++++++++++++++- roles/infra/tasks/deploy.yml | 21 +- roles/infra/templates/context.sh.jinja | 12 +- roles/infra/templates/frontend.xml.jinja | 146 -------------- 9 files changed, 445 insertions(+), 180 deletions(-) create mode 100644 plugins/filter/ipv4_mac.yml delete mode 100644 roles/infra/templates/frontend.xml.jinja diff --git a/playbooks/infra.yml b/playbooks/infra.yml index 4e5f65d1..f39ab476 100644 --- a/playbooks/infra.yml +++ b/playbooks/infra.yml @@ -7,7 +7,7 @@ - role: helper/online # This installs the interpreter only. - # Extra OS/PyPI packages should not required by this playbook. + # Extra OS/PyPI packages should not be required by this playbook. - role: helper/python3 - role: helper/facts diff --git a/plugins/filter/ipv4_mac.yml b/plugins/filter/ipv4_mac.yml new file mode 100644 index 00000000..58bc7cf1 --- /dev/null +++ b/plugins/filter/ipv4_mac.yml @@ -0,0 +1,35 @@ +--- +# Copyright: OpenNebula Project, OpenNebula Systems +# Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0) + +DOCUMENTATION: + name: ipv4_mac + short_description: Convert IPv4 into MAC + description: + - Convert IPv4 (A.B.C.D) into MAC (02:01:A:B:C:D). + options: + _input: + description: + - String to convert. + type: str + required: true + fmt: + description: + - Format string (e.g. 02:01:%02x:%02x:%02x:%02x). + type: str + required: false + default: '02:01:%02x:%02x:%02x:%02x' + author: + - Michal Opala (@sk4zuzu) + +EXAMPLES: | + # '10.11.12.13' -> '02:01:0a:0b:0c:0d' + - name: Conversion example + ansible.builtin.debug: + msg: >- + {{ '10.11.12.13' | opennebula.deploy.ipv4_mac }} + +RETURN: + _value: + description: String containing MAC. + type: str diff --git a/plugins/filter/main.py b/plugins/filter/main.py index 0f0a1bda..b0b0a0cf 100644 --- a/plugins/filter/main.py +++ b/plugins/filter/main.py @@ -1,7 +1,13 @@ -from ansible_collections.opennebula.deploy.plugins.module_utils.main import to_one +from ansible_collections.opennebula.deploy.plugins.module_utils.main import ( + ipv4_mac, + to_one, +) class FilterModule(object): def filters(self): - return dict(to_one=to_one) + return dict( + ipv4_mac=ipv4_mac, + to_one=to_one, + ) diff --git a/plugins/module_utils/main.py b/plugins/module_utils/main.py index f697eaf9..386378d5 100644 --- a/plugins/module_utils/main.py +++ b/plugins/module_utils/main.py @@ -3,6 +3,16 @@ # Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0) +# EXAMPLES: +# '10.11.12.13' -> '02:01:0a:0b:0c:0d' +def ipv4_mac(ipv4, fmt='02:01:%02x:%02x:%02x:%02x'): + """Converts IPv4 (string) into MAC.""" + + import ipaddress + + return fmt % tuple(ipaddress.IPv4Address(ipv4).packed) + + # NOTE: It does not validate character classes or character count! # EXAMPLES: # pci -> match_address('0000:0*:00.*', sep='[:.]') diff --git a/roles/infra/README.md b/roles/infra/README.md index 7f8215a9..a5fddf61 100644 --- a/roles/infra/README.md +++ b/roles/infra/README.md @@ -20,18 +20,19 @@ Role Variables | `os_image_url` | `str` | | | HTTP(S) link to Debian/RedHat-like image running `one-contextd`. | | `os_image_size` | `str` | `20G` | | The size to which one-deploy will **try** to adjust the OS image. | | `memory_KiB` | `str` | `2097152` | | Memory amount to be set in XML in Libvirt. | -| `vcpu_static` | `str` | `1` | | VCPU amount to be set in XML in Libvirt. | -| `cputune_shares_base` | `int` | `200` | | Base value used to compute PWS (assuming CGv2). | -| `cputune_shares_max` | `int` | `10000` | | PWS value cap (assuming CGv2). | -| `cputune_shares` | `int` | computed | | The proportional weighted share (PWS) for the domain. | | `vnc_max_port` | `str` | `65535` | | Upper limit for VNC ports to start counting-down from. | | `passthrough_fs` | `list` | `[]` | (check below) | Shared HV filesystems to attach to the Front-end VMs. | | | | | | | +| `vcpu_static` | `str` | `1` | | VCPU amount to be set in XML in Libvirt. | +| `vcpu_shares` | `str` | `200` | | The proportional weighted share (PWS) for the domain. | +| | | | | | | `infra_bridge` | `str` | `br0` | | Pre-defined bridge interface to insert VM NICs to. | -| `infra_bridge_type` | `str` | `bridge` | | Supported values: bridge, openvswitch, openvswitch_dpdk | -| `infra_dpdk_socket_path` | `str` | | | Path for existing socket when using OVS with DPDK. | | `infra_vlan_id` | `str` | | | Optionally set the VLAN ID for the bridge. | +| `dpdk_socket_path` | `str` | `null` | | Path for existing socket when using OVS with DPDK. | +| | | | | | | `infra_hostname` | `str` | | `n1a1` | Defines on which HV machine the Front-end VM should be deployed. | +| `infra_xml_variant` | `str` | undefined | `default` | Defined which domain XML variant will be used in Libvirt. | +| | | | | | | `context.ETH0_DNS` | `str` | | `1.1.1.1` | DNS server. | | `context.ETH0_SEARCH_DOMAIN` | `str` | | `1.1.1.1` | DNS search domain. | | `context.ETH0_GATEWAY` | `str` | | `10.2.50.1` | Gateway. | @@ -43,6 +44,7 @@ Role Variables | `context.PASSWORD` | `str` | `opennebula` | | Root's password. | | `context.SET_HOSTNAME` | `str` | name of the FE VM | | Hostname. | | `context.SSH_PUBLIC_KEY` | `str` | | (check below) | Root's extra authorized keys. | +| `context.START_SCRIPT_BASE64` | `str` | | | Start script (base64-encoded). | **NOTE**: The `infra_hostname` and `context` dictionary should be set for members of the `frontend` group (please check the `inventory/infra.yml` example). @@ -52,19 +54,140 @@ Dependencies - `community.libvirt` - `ansible.posix` -Example Playbook ----------------- +Example Inventory +----------------- + + # --- VARIANT: default --- - - hosts: infra + infra: vars: - os_image_url: https://d24fmfybwxpuhu.cloudfront.net/ubuntu2204-6.10.0-1-20240514.qcow2 - passthrough_fs: - - driver_type: virtiofs - source_dir: /var/lib/one/datastores - target_dir: /var/lib/one/datastores - roles: - - role: opennebula.deploy.helper.facts - - role: opennebula.deploy.infra + os_image_url: http://10.2.11.1/images/ubuntu2404-7.4.0-0-20260714.qcow2 + os_image_size: 20G + memory_KiB: 2097152 # 2 GiB + infra_xml_variant: default + hosts: + u1q20: { ansible_host: 10.3.10.20 } + u1q30: { ansible_host: 10.3.10.30 } + + frontend: + vars: + context: + ETH0_DNS: 10.3.10.1 + ETH0_GATEWAY: 10.3.10.1 + ETH0_MASK: 255.255.255.0 + ETH0_NETWORK: 10.3.10.0 + ETH0_IP: "{{ ansible_host }}" + PASSWORD: asd + SSH_PUBLIC_KEY: |- + ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIF5ndznuZTNZ8u7FCYKgv6Q3/HUVxnaha3tPDUXPfIIw + hosts: + u1q40: { ansible_host: 10.3.10.40, infra_hostname: u1q20 } + u1q50: { ansible_host: 10.3.10.50, infra_hostname: u1q30 } + + node: + hosts: + u1q20: { ansible_host: 10.3.10.20 } + u1q30: { ansible_host: 10.3.10.30 } + + # --- VARIANT: openvswitch --- + + infra: + vars: + os_image_url: http://10.2.11.1/images/ubuntu2404-7.4.0-0-20260714.qcow2 + os_image_size: 20G + memory_KiB: 2097152 # 2 GiB + infra_xml_variant: openvswitch + infra_bridge: ovsbr0 + hosts: + u1q20: { ansible_host: 10.3.10.20 } + + frontend: + vars: + context: + ETH0_DNS: 10.3.10.1 + ETH0_SEARCH_DOMAIN: u1q.lh + ETH0_GATEWAY: 10.3.10.1 + ETH0_MASK: 255.255.255.0 + ETH0_NETWORK: 10.3.10.0 + ETH0_IP: "{{ ansible_host }}" + PASSWORD: asd + SSH_PUBLIC_KEY: |- + ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIF5ndznuZTNZ8u7FCYKgv6Q3/HUVxnaha3tPDUXPfIIw + hosts: + u1q40: { ansible_host: 10.3.10.40, infra_hostname: u1q20 } + + node: + vars: + ovs: + iface: + eth1: {} + br: + ovsbr0: + ports: [eth1] + hosts: + u1q20: { ansible_host: 10.3.10.20 } + + # --- VARIANT: openvswitch_dpdk --- + + infra: + vars: + os_image_url: http://10.2.11.1/images/ubuntu2404-7.4.0-0-20260714.qcow2 + os_image_size: 20G + memory_KiB: 2097152 # 2 GiB + infra_xml_variant: openvswitch_dpdk + hosts: + u1q20: { ansible_host: 10.3.10.20, dpdk_socket_path: /var/run/one/vhost-socks/u1q40.sock } + + frontend: + vars: + context: + ETH0_DNS: 10.3.10.1 + ETH0_SEARCH_DOMAIN: u1q.lh + ETH0_GATEWAY: 10.3.10.1 + ETH0_MASK: 255.255.255.0 + ETH0_NETWORK: 10.3.10.0 + ETH0_IP: "{{ ansible_host }}" + PASSWORD: asd + SSH_PUBLIC_KEY: |- + ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIF5ndznuZTNZ8u7FCYKgv6Q3/HUVxnaha3tPDUXPfIIw + hosts: + u1q40: { ansible_host: 10.3.10.40, infra_hostname: u1q20 } + + node: + vars: + kernel_ok_to_reboot: true + kernel_params: + - default_hugepagesz: '1G' + - hugepages: '0:4,1:4' + - intel_iommu: 'on' + kernel_modules: + - load: vfio-pci + - load: vfio_iommu_type1 + options: ["allow_unsafe_interrupts=1"] + + pci_devices: + - address: '0000:40:00.0' + + ovs: + set: + - other_config:dpdk-init: 'true' + - other_config:dpdk-socket-mem: '2048,2048' + iface: + dpdk-p0: + set: + - type: dpdk + - options:dpdk-devargs: '0000:40:00.0' + dpdk-p1: + set: + - type: dpdkvhostuserclient + - options:vhost-server-path: "{{ dpdk_socket_path }}" + br: + ovsbr1: + ports: [dpdk-p0, dpdk-p1] + set: + - datapath_type: netdev + hosts: + u1q20: { ansible_host: 10.3.10.20 } License ------- diff --git a/roles/infra/defaults/main.yml b/roles/infra/defaults/main.yml index 0d7ac641..f519217d 100644 --- a/roles/infra/defaults/main.yml +++ b/roles/infra/defaults/main.yml @@ -1,13 +1,229 @@ --- runtime_dir: /var/one-deploy/ -os_image_url: https://d24fmfybwxpuhu.cloudfront.net/ubuntu2204-6.8.1-1-20240131.qcow2 +os_image_url: https://d24fmfybwxpuhu.cloudfront.net/ubuntu2404-7.4.0-0-20260714.qcow2 os_image_size: 20G -memory_KiB: 2097152 # 2 GiB -vcpu_static: 1 vnc_max_port: 65535 -infra_bridge: br0 -infra_bridge_type: bridge passthrough_fs: [] -cputune_shares_base: 200 # twice the OpenNebula's default (for CGv2) -cputune_shares_max: 10000 -cputune_shares: "{{ ((vcpu_static | int) * (cputune_shares_base | int)) | round(0, 'ceil') }}" + +infra_bridge: br0 + +dpdk_socket_path: null + +memory_KiB: 2097152 # 2 GiB + +vcpu_static: 1 +vcpu_shares: 200 + +infra_xml_variant: "{{ undef() }}" + +infra_xml: + default: + - "{{ infra_xml_base.default }}" + - "{{ infra_xml_cputune.default }}" + - "{{ infra_xml_memoryBacking.default }}" + - "{{ infra_xml_interfaces.default }}" + - "{{ infra_xml_filesystems.default }}" + openvswitch: + - "{{ infra_xml_base.default }}" + - "{{ infra_xml_cputune.default }}" + - "{{ infra_xml_memoryBacking.default }}" + - "{{ infra_xml_interfaces.openvswitch }}" + - "{{ infra_xml_filesystems.default }}" + openvswitch_dpdk: + - "{{ infra_xml_base.default }}" + - "{{ infra_xml_cputune.default }}" + - "{{ infra_xml_memoryBacking.hugepages }}" + - "{{ infra_xml_interfaces.openvswitch_dpdk }}" + - "{{ infra_xml_filesystems.default }}" + +infra_xml_cputune: + default: | + + + {% if vcpu_shares | int < 10000 %} + {{ vcpu_shares | int }} + {% else %} + 10000 + {% endif %} + + + +infra_xml_memoryBacking: + default: | + + + + + + hugepages: | + + + + + + + +infra_xml_interfaces: + default: | + + + + + + {% if infra_vlan_id is defined %} + + + + {% endif %} + + + + + + openvswitch: | + + + + + + + {% if infra_vlan_id is defined %} + + + + {% endif %} + + + + + + openvswitch_dpdk: | + + + + + + + {% if infra_vlan_id is defined %} + + + + {% endif %} + + + + + + +infra_xml_filesystems: + default: | + + + {% for fs in passthrough_fs %} + + {% if fs.driver_type is defined %} + + {% endif %} + + + + {% endfor %} + + + +infra_xml_base: + default: | + + {{ frontend }} + {{ frontend }} + + {{ memory_KiB }} + {{ vcpu_static }} + + + + /machine + + + hvm + + + + + + + + + + + + destroy + restart + destroy + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/roles/infra/tasks/deploy.yml b/roles/infra/tasks/deploy.yml index fc83ec55..2b7f7b3f 100644 --- a/roles/infra/tasks/deploy.yml +++ b/roles/infra/tasks/deploy.yml @@ -90,7 +90,7 @@ - name: Compute VNC ports ansible.builtin.set_fact: - frontends_to_vnc_ports: >- + frontend_vnc_ports: >- {{ dict(_frontends | zip(_ports)) }} vars: _ports: >- @@ -103,9 +103,26 @@ - name: Define Front-end VMs community.libvirt.virt: command: define - xml: "{{ lookup('template', 'frontend.xml.jinja') }}" + xml: >- + {%- set output = {} -%} + {%- for v in infra_xml[infra_xml_variant] -%} + {{- + output.update(output | combine( + v | ansible.utils.from_xml | combine(_fixup, recursive=true), + recursive=true, + list_merge='append_rp', + )) + -}} + {%- endfor -%} + {{- output | ansible.utils.to_xml(full_document=false) -}} autostart: true vars: + # NOTE: This is required because (for example) '' produces '{"devices": null}' + # which cannot be merged recursively. + _fixup: + domain: + cputune: {} + devices: {} context: "{{ hostvars[frontend].context }}" loop_control: { loop_var: frontend } loop: "{{ infra_to_frontends[inventory_hostname] }}" diff --git a/roles/infra/templates/context.sh.jinja b/roles/infra/templates/context.sh.jinja index 77cac65e..57b131ee 100644 --- a/roles/infra/templates/context.sh.jinja +++ b/roles/infra/templates/context.sh.jinja @@ -1,18 +1,22 @@ # Context variables generated by one-deploy DISK_ID='1' -ETH0_DNS='{{ context.ETH0_DNS }}' -ETH0_SEARCH_DOMAIN='{{ context.ETH0_SEARCH_DOMAIN | d("") }}' -ETH0_GATEWAY='{{ context.ETH0_GATEWAY }}' ETH0_IP='{{ context.ETH0_IP }}' -ETH0_MAC='{{ context.ETH0_MAC | d("02:01:%02x:%02x:%02x:%02x" | format(*(context.ETH0_IP.split(".") | map("int")))) }}' +ETH0_MAC='{{ context.ETH0_MAC | d(context.ETH0_IP | opennebula.deploy.ipv4_mac) }}' ETH0_MASK='{{ context.ETH0_MASK }}' ETH0_NETWORK='{{ context.ETH0_NETWORK }}' +ETH0_GATEWAY='{{ context.ETH0_GATEWAY }}' GROW_FS='{{ context.GROW_FS | d("/") }}' NETWORK='YES' PASSWORD='{{ context.PASSWORD | d("opennebula") }}' SET_HOSTNAME='{{ context.SET_HOSTNAME | d(frontend) }}' SSH_PUBLIC_KEY='{{ context.SSH_PUBLIC_KEY | d("") }}' TARGET='hda' +{% if context.ETH0_DNS is defined %} +ETH0_DNS='{{ context.ETH0_DNS }}' +{% endif %} +{% if context.ETH0_SEARCH_DOMAIN %} +ETH0_SEARCH_DOMAIN='{{ context.ETH0_SEARCH_DOMAIN }}' +{% endif %} {% if context.START_SCRIPT_BASE64 is defined %} START_SCRIPT_BASE64='{{ context.START_SCRIPT_BASE64 | b64encode }}' {% endif %} diff --git a/roles/infra/templates/frontend.xml.jinja b/roles/infra/templates/frontend.xml.jinja deleted file mode 100644 index b407e975..00000000 --- a/roles/infra/templates/frontend.xml.jinja +++ /dev/null @@ -1,146 +0,0 @@ - - {{ frontend }} - {{ frontend }} - - {{ memory_KiB }} - {{ vcpu_static }} - - - {{ (cputune_shares if (cputune_shares | int) < (cputune_shares_max | int) else cputune_shares_max) | int }} - - - - /machine - - - hvm - - - - - - - - - - - - destroy - restart - destroy - -{% set use_dpdk = (infra_bridge_type is defined and infra_bridge_type == 'openvswitch_dpdk') %} -{% set use_virtiofs = ('virtiofs' in (passthrough_fs | map(attribute='driver_type') | map('default', None) | select | map('lower'))) %} - -{% if use_dpdk or use_virtiofs %} - -{% if use_dpdk %} - -{% endif %} -{% if use_virtiofs and not use_dpdk %} - -{% endif %} - - -{% endif %} - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -{% for fs in passthrough_fs %} - -{% if fs.driver_type is defined and fs.driver_type is truthy %} - -{% endif %} - - - -{% endfor %} - - - - - -{% if infra_bridge_type is defined and infra_bridge_type in ['bridge', 'openvswitch', 'openvswitch_dpdk'] %} -{% if infra_bridge_type in ['bridge', 'openvswitch'] %} - - - -{% elif infra_bridge_type == 'openvswitch_dpdk' %} - - -{% endif %} - -{% if infra_bridge_type == 'openvswitch' %} - -{% endif %} -{% if infra_vlan_id is defined and infra_vlan_id %} - - - -{% endif %} - - - - -{% endif %} - - - - - - - - - - - - - - - - - - - - - From 6564225c50f10d3d573905574d36aa6e72a0571c Mon Sep 17 00:00:00 2001 From: Michal Opala Date: Thu, 10 Sep 2026 19:29:40 +0200 Subject: [PATCH 2/2] M #-: Apply selected review suggestions (fix) - Provide default values for infra_xml_variant and dpdk_socket_path - Simplify XML composition code / Remove useless list_merge='append_rp' - Adjust if-statements inside context.sh.jinja (fix) - Adjust handling of vcpu_shares - Update README examples Signed-off-by: Michal Opala --- roles/infra/README.md | 85 +++++++++++++++----------- roles/infra/defaults/main.yml | 16 ++--- roles/infra/tasks/deploy.yml | 20 +++--- roles/infra/templates/context.sh.jinja | 6 +- 4 files changed, 67 insertions(+), 60 deletions(-) diff --git a/roles/infra/README.md b/roles/infra/README.md index a5fddf61..9df1aec4 100644 --- a/roles/infra/README.md +++ b/roles/infra/README.md @@ -11,40 +11,40 @@ Pre-installed Libvirt software (in the case of `infra` and `node` groups share h Role Variables -------------- -| Name | Type | Default | Example | Description | -|--------------------------------------|--------|--------------------|---------------------|-------------------------------------------------------------------| -| `frontend_group` | `str` | `frontend` | | Custom name of the Frontend group in the inventory. | -| `infra_group` | `str` | `infra` | | Custom name of the Infra group in the inventory. | -| | | | | | -| `runtime_dir` | `str` | `/var/one-deploy/` | | Directory used to store QCOW2 and ISO images. | -| `os_image_url` | `str` | | | HTTP(S) link to Debian/RedHat-like image running `one-contextd`. | -| `os_image_size` | `str` | `20G` | | The size to which one-deploy will **try** to adjust the OS image. | -| `memory_KiB` | `str` | `2097152` | | Memory amount to be set in XML in Libvirt. | -| `vnc_max_port` | `str` | `65535` | | Upper limit for VNC ports to start counting-down from. | -| `passthrough_fs` | `list` | `[]` | (check below) | Shared HV filesystems to attach to the Front-end VMs. | -| | | | | | -| `vcpu_static` | `str` | `1` | | VCPU amount to be set in XML in Libvirt. | -| `vcpu_shares` | `str` | `200` | | The proportional weighted share (PWS) for the domain. | -| | | | | | -| `infra_bridge` | `str` | `br0` | | Pre-defined bridge interface to insert VM NICs to. | -| `infra_vlan_id` | `str` | | | Optionally set the VLAN ID for the bridge. | -| `dpdk_socket_path` | `str` | `null` | | Path for existing socket when using OVS with DPDK. | -| | | | | | -| `infra_hostname` | `str` | | `n1a1` | Defines on which HV machine the Front-end VM should be deployed. | -| `infra_xml_variant` | `str` | undefined | `default` | Defined which domain XML variant will be used in Libvirt. | -| | | | | | -| `context.ETH0_DNS` | `str` | | `1.1.1.1` | DNS server. | -| `context.ETH0_SEARCH_DOMAIN` | `str` | | `1.1.1.1` | DNS search domain. | -| `context.ETH0_GATEWAY` | `str` | | `10.2.50.1` | Gateway. | -| `context.ETH0_IP` | `str` | | `10.2.50.100` | IPv4 address to be set on eth0. | -| `context.ETH0_MAC` | `str` | | `02:01:0a:02:32:64` | MAC address to be set on eth0 (**MUST** match MAC set in XML.) | -| `context.ETH0_MASK` | `str` | | `255.255.255.0` | Network mask. | -| `context.ETH0_NETWORK` | `str` | | `10.2.50.0` | Network address. | -| `context.GROW_FS` | `str` | `/` | | Filesystems to grow. | -| `context.PASSWORD` | `str` | `opennebula` | | Root's password. | -| `context.SET_HOSTNAME` | `str` | name of the FE VM | | Hostname. | -| `context.SSH_PUBLIC_KEY` | `str` | | (check below) | Root's extra authorized keys. | -| `context.START_SCRIPT_BASE64` | `str` | | | Start script (base64-encoded). | +| Name | Type | Default | Example | Description | +|--------------------------------------|--------|------------------------------------------|---------------------|-------------------------------------------------------------------| +| `frontend_group` | `str` | `frontend` | | Custom name of the Frontend group in the inventory. | +| `infra_group` | `str` | `infra` | | Custom name of the Infra group in the inventory. | +| | | | | | +| `runtime_dir` | `str` | `/var/one-deploy/` | | Directory used to store QCOW2 and ISO images. | +| `os_image_url` | `str` | | | HTTP(S) link to Debian/RedHat-like image running `one-contextd`. | +| `os_image_size` | `str` | `20G` | | The size to which one-deploy will **try** to adjust the OS image. | +| `memory_KiB` | `str` | `2097152` | | Memory amount to be set in XML in Libvirt. | +| `vnc_max_port` | `str` | `65535` | | Upper limit for VNC ports to start counting-down from. | +| `passthrough_fs` | `list` | `[]` | (check below) | Shared HV filesystems to attach to the Front-end VMs. | +| | | | | | +| `vcpu_static` | `str` | `1` | | VCPU amount to be set in XML in Libvirt. | +| `vcpu_shares` | `str` | `200` | | The total CPU share for the domain. | +| | | | | | +| `infra_bridge` | `str` | `br0` | | Pre-defined bridge interface to insert VM NICs to. | +| `infra_vlan_id` | `str` | | | Optionally set the VLAN ID for the bridge. | +| `dpdk_socket_path` | `str` | `/var/run/one/vhost-socks/frontend.sock` | | Path for existing socket when using OVS with DPDK. | +| | | | | | +| `infra_hostname` | `str` | | `n1a1` | Defines on which HV machine the Front-end VM should be deployed. | +| `infra_xml_variant` | `str` | `default` | | Defined which domain XML variant will be used in Libvirt. | +| | | | | | +| `context.ETH0_DNS` | `str` | | `1.1.1.1` | DNS server. | +| `context.ETH0_SEARCH_DOMAIN` | `str` | | `1.1.1.1` | DNS search domain. | +| `context.ETH0_GATEWAY` | `str` | | `10.2.50.1` | Gateway. | +| `context.ETH0_IP` | `str` | | `10.2.50.100` | IPv4 address to be set on eth0. | +| `context.ETH0_MAC` | `str` | | `02:01:0a:02:32:64` | MAC address to be set on eth0 (**MUST** match MAC set in XML.) | +| `context.ETH0_MASK` | `str` | | `255.255.255.0` | Network mask. | +| `context.ETH0_NETWORK` | `str` | | `10.2.50.0` | Network address. | +| `context.GROW_FS` | `str` | `/` | | Filesystems to grow. | +| `context.PASSWORD` | `str` | `opennebula` | | Root's password. | +| `context.SET_HOSTNAME` | `str` | name of the FE VM | | Hostname. | +| `context.SSH_PUBLIC_KEY` | `str` | | (check below) | Root's extra authorized keys. | +| `context.START_SCRIPT_BASE64` | `str` | | | Start script (base64-encoded automatically). | **NOTE**: The `infra_hostname` and `context` dictionary should be set for members of the `frontend` group (please check the `inventory/infra.yml` example). @@ -54,6 +54,20 @@ Dependencies - `community.libvirt` - `ansible.posix` +Example Playbook +---------------- + + - hosts: infra + vars: + os_image_url: https://d24fmfybwxpuhu.cloudfront.net/ubuntu2404-7.4.0-0-20260714.qcow2 + passthrough_fs: + - driver_type: virtiofs + source_dir: /var/lib/one/datastores + target_dir: /var/lib/one/datastores + roles: + - role: opennebula.deploy.helper.facts + - role: opennebula.deploy.infra + Example Inventory ----------------- @@ -64,7 +78,6 @@ Example Inventory os_image_url: http://10.2.11.1/images/ubuntu2404-7.4.0-0-20260714.qcow2 os_image_size: 20G memory_KiB: 2097152 # 2 GiB - infra_xml_variant: default hosts: u1q20: { ansible_host: 10.3.10.20 } u1q30: { ansible_host: 10.3.10.30 } @@ -163,7 +176,7 @@ Example Inventory kernel_modules: - load: vfio-pci - load: vfio_iommu_type1 - options: ["allow_unsafe_interrupts=1"] + options: ['allow_unsafe_interrupts=1'] pci_devices: - address: '0000:40:00.0' diff --git a/roles/infra/defaults/main.yml b/roles/infra/defaults/main.yml index f519217d..72d44b4d 100644 --- a/roles/infra/defaults/main.yml +++ b/roles/infra/defaults/main.yml @@ -7,14 +7,18 @@ passthrough_fs: [] infra_bridge: br0 -dpdk_socket_path: null +# NOTE: We assume that no more than a single FE VM is deployed on a HV +# (which should be considered a regular / default setup). +# More FE VMs can be deployed on a HV of course, but in such a case, +# please handle it directly in your inventory. +dpdk_socket_path: /var/run/one/vhost-socks/frontend.sock memory_KiB: 2097152 # 2 GiB vcpu_static: 1 -vcpu_shares: 200 +vcpu_shares: "{{ ((vcpu_static | int) * 200) | int }}" -infra_xml_variant: "{{ undef() }}" +infra_xml_variant: default infra_xml: default: @@ -40,11 +44,7 @@ infra_xml_cputune: default: | - {% if vcpu_shares | int < 10000 %} - {{ vcpu_shares | int }} - {% else %} - 10000 - {% endif %} + {{ [vcpu_shares | int, 10000] | min }} diff --git a/roles/infra/tasks/deploy.yml b/roles/infra/tasks/deploy.yml index 2b7f7b3f..afbf9f98 100644 --- a/roles/infra/tasks/deploy.yml +++ b/roles/infra/tasks/deploy.yml @@ -104,24 +104,18 @@ community.libvirt.virt: command: define xml: >- - {%- set output = {} -%} - {%- for v in infra_xml[infra_xml_variant] -%} - {{- - output.update(output | combine( - v | ansible.utils.from_xml | combine(_fixup, recursive=true), - recursive=true, - list_merge='append_rp', - )) - -}} - {%- endfor -%} - {{- output | ansible.utils.to_xml(full_document=false) -}} + {{ _variant | map('ansible.utils.from_xml') + | map('combine', _fixup, recursive=true) + | combine(recursive=true) + | ansible.utils.to_xml(full_document=false) }} autostart: true vars: + _variant: >- + {{ infra_xml.get(infra_xml_variant, undef(hint='Unknown variant "{}"'.format(infra_xml_variant))) }} # NOTE: This is required because (for example) '' produces '{"devices": null}' - # which cannot be merged recursively. + # which cannot be merged correctly. _fixup: domain: - cputune: {} devices: {} context: "{{ hostvars[frontend].context }}" loop_control: { loop_var: frontend } diff --git a/roles/infra/templates/context.sh.jinja b/roles/infra/templates/context.sh.jinja index 57b131ee..24ad3ca3 100644 --- a/roles/infra/templates/context.sh.jinja +++ b/roles/infra/templates/context.sh.jinja @@ -11,12 +11,12 @@ PASSWORD='{{ context.PASSWORD | d("opennebula") }}' SET_HOSTNAME='{{ context.SET_HOSTNAME | d(frontend) }}' SSH_PUBLIC_KEY='{{ context.SSH_PUBLIC_KEY | d("") }}' TARGET='hda' -{% if context.ETH0_DNS is defined %} +{% if context.ETH0_DNS | d('') %} ETH0_DNS='{{ context.ETH0_DNS }}' {% endif %} -{% if context.ETH0_SEARCH_DOMAIN %} +{% if context.ETH0_SEARCH_DOMAIN | d('') %} ETH0_SEARCH_DOMAIN='{{ context.ETH0_SEARCH_DOMAIN }}' {% endif %} -{% if context.START_SCRIPT_BASE64 is defined %} +{% if context.START_SCRIPT_BASE64 | d('') %} START_SCRIPT_BASE64='{{ context.START_SCRIPT_BASE64 | b64encode }}' {% endif %}