From 5fca7af2d1f5fb3b4731a92251cd0cc86e8d23a6 Mon Sep 17 00:00:00 2001 From: Allar Viik Date: Wed, 17 Sep 2025 13:34:56 +0300 Subject: [PATCH 01/15] Added limited and experimental Openstack support --- nova/core/galaxy.yml | 2 +- .../core/roles/accounts/tasks/unix_create.yml | 4 +-- nova/core/roles/connection/tasks/main.yml | 4 +++ .../core/roles/connection/tasks/openstack.yml | 16 ++++++++++ .../machine_operations/defaults/main.yml | 22 ++++++++++++++ .../roles/machine_operations/tasks/main.yml | 4 +++ .../tasks/openstack/create.yml | 6 ++-- .../tasks/openstack/main.yml | 28 ++++++++++++++++++ .../tasks/openstack/remove.yml | 29 +++++++++++++++++++ 9 files changed, 110 insertions(+), 5 deletions(-) create mode 100644 nova/core/roles/connection/tasks/openstack.yml create mode 100644 nova/core/roles/machine_operations/tasks/openstack/main.yml create mode 100644 nova/core/roles/machine_operations/tasks/openstack/remove.yml diff --git a/nova/core/galaxy.yml b/nova/core/galaxy.yml index 5b5115da3..28396f17e 100644 --- a/nova/core/galaxy.yml +++ b/nova/core/galaxy.yml @@ -1,6 +1,6 @@ namespace: nova name: core -version: 6.8.18 +version: 6.8.19 readme: README.md authors: - https://github.com/novateams diff --git a/nova/core/roles/accounts/tasks/unix_create.yml b/nova/core/roles/accounts/tasks/unix_create.yml index 6e1165625..31c146705 100644 --- a/nova/core/roles/accounts/tasks/unix_create.yml +++ b/nova/core/roles/accounts/tasks/unix_create.yml @@ -15,7 +15,7 @@ - name: Checking if {{ vars[infra_env ~ '_template_username'] | default('') }} user exists... ansible.builtin.stat: path: /home/{{ vars[infra_env ~ '_template_username'] | default('') }} - register: aws_template_user + register: template_user - name: Adding account creation source flag for {{ vars[infra_env ~ '_template_username'] | default('') }}... ansible.builtin.lineinfile: @@ -25,7 +25,7 @@ create: true owner: "{{ vars[infra_env ~ '_template_username'] | default('') }}" mode: "0600" - when: aws_template_user.stat.exists + when: template_user.stat.exists - name: Creating accounts for... ansible.builtin.user: diff --git a/nova/core/roles/connection/tasks/main.yml b/nova/core/roles/connection/tasks/main.yml index 292d5bf93..7035c7302 100644 --- a/nova/core/roles/connection/tasks/main.yml +++ b/nova/core/roles/connection/tasks/main.yml @@ -27,6 +27,10 @@ ansible.builtin.include_tasks: proxmox.yml when: infra_env == 'proxmox' +- name: Configuring connection in OpenStack environment... + ansible.builtin.include_tasks: openstack.yml + when: infra_env == 'openstack' + - name: Configuring connection for external or pre-existing machine... ansible.builtin.include_tasks: external.yml when: infra_env == 'external' diff --git a/nova/core/roles/connection/tasks/openstack.yml b/nova/core/roles/connection/tasks/openstack.yml new file mode 100644 index 000000000..6b634b68e --- /dev/null +++ b/nova/core/roles/connection/tasks/openstack.yml @@ -0,0 +1,16 @@ +--- +- name: + Connecting as {{ openstack_template_username | default(template_username) if fresh_deploy else ansible_deployer_username + }} to {{ inventory_hostname }} using {{ default_connection_plugin | upper + }} over {{ connection_address_custom | default(connection_address) }}... # noqa: name[template] + ansible.builtin.set_fact: + ansible_host: "{{ connection_address_custom | default(connection_address) }}" + ansible_connection: "{{ default_connection_plugin }}" + ansible_user: "{{ openstack_template_username | default(template_username) if fresh_deploy else ansible_deployer_username }}" + ansible_password: "{{ openstack_template_password | default(template_password) if fresh_deploy else ansible_deployer_password }}" + + # Initial connection will be done with temporary SSH key + ansible_private_key_file: "{{ machine_operations_openstack_temp_ssh_key_path if fresh_deploy else omit }}" + # To avoid SSH MaxAuthTries limit + ansible_ssh_extra_args: "{{ connection_custom_ansible_ssh_extra_args + | default('-o IdentitiesOnly=yes' if fresh_deploy else omit) }}" diff --git a/nova/core/roles/machine_operations/defaults/main.yml b/nova/core/roles/machine_operations/defaults/main.yml index 2d8066565..7f02f4031 100644 --- a/nova/core/roles/machine_operations/defaults/main.yml +++ b/nova/core/roles/machine_operations/defaults/main.yml @@ -184,3 +184,25 @@ machine_operations_proxmox_vmid: "{{ custom_vm_name | default(vm_name) | hash('s # Start VM on boot machine_operations_proxmox_vm_start_on_boot: true + +############# +# OpenStack # +############# + +# Pre-existing keypair name to use for the instance +machine_operations_openstack_key_name: {} + +# Temp path where autogenerated temp SSH key is stored +machine_operations_openstack_temp_ssh_key_name: "{{ 'temp_' ~ custom_vm_name | default(vm_name) ~ '_key' }}" +machine_operations_openstack_temp_ssh_key_path: /tmp/{{ project_fullname | default('') }}_{{ inventory_hostname }}_openstack_key + +# Floating IP pool name. +# If left empty no floating IP will be attached +machine_operations_openstack_floating_ip_pool_name: {} + +# Backend interface name where the floating IP will send traffic +# Usually only to define required when an instance has multiple interfaces +machine_operations_openstack_floating_ip_nat_destination: {} + +# Set to true to inject the generated keypair to the instance using cloud-init +machine_operations_openstack_use_keypair: false diff --git a/nova/core/roles/machine_operations/tasks/main.yml b/nova/core/roles/machine_operations/tasks/main.yml index d05c6de9c..64f347088 100644 --- a/nova/core/roles/machine_operations/tasks/main.yml +++ b/nova/core/roles/machine_operations/tasks/main.yml @@ -57,6 +57,10 @@ ansible.builtin.include_tasks: proxmox/main.yml when: infra_env == 'proxmox' + - name: Including OpenStack environment tasks... + ansible.builtin.include_tasks: openstack/main.yml + when: infra_env == 'openstack' + - name: Including external environment tasks... ansible.builtin.include_tasks: external/main.yml when: infra_env == 'external' diff --git a/nova/core/roles/machine_operations/tasks/openstack/create.yml b/nova/core/roles/machine_operations/tasks/openstack/create.yml index 980bb8ff3..0c12c89be 100644 --- a/nova/core/roles/machine_operations/tasks/openstack/create.yml +++ b/nova/core/roles/machine_operations/tasks/openstack/create.yml @@ -68,7 +68,8 @@ state: present terminate_volume: true volume_size: "{{ hardware_primary_disk_size }}" - key_name: "{{ machine_operations_openstack_key_name if machine_operations_openstack_key_name != {} + key_name: + "{{ machine_operations_openstack_key_name if machine_operations_openstack_key_name != {} else machine_operations_openstack_temp_ssh_key_name if machine_operations_openstack_use_keypair else omit }}" security_groups: "{{ openstack_security_groups | default(['default']) }}" nics: "{{ openstack_nics }}" @@ -78,7 +79,8 @@ openstack.cloud.floating_ip: server: "{{ custom_vm_name | default(vm_name) }}" network: "{{ machine_operations_openstack_floating_ip_pool_name }}" - nat_destination: "{{ omit if machine_operations_openstack_floating_ip_nat_destination is ansible.builtin.falsy + nat_destination: + "{{ omit if machine_operations_openstack_floating_ip_nat_destination is ansible.builtin.falsy else machine_operations_openstack_floating_ip_nat_destination }}" when: machine_operations_openstack_floating_ip_pool_name is ansible.builtin.truthy diff --git a/nova/core/roles/machine_operations/tasks/openstack/main.yml b/nova/core/roles/machine_operations/tasks/openstack/main.yml new file mode 100644 index 000000000..d86b06e25 --- /dev/null +++ b/nova/core/roles/machine_operations/tasks/openstack/main.yml @@ -0,0 +1,28 @@ +--- +- name: Checking for required variables... + ansible.builtin.fail: + msg: | + Following variables are required to run this role: + - openstack_auth_url + - openstack_username + - openstack_password + - openstack_project_id + when: > + openstack_defaults.auth.auth_url is ansible.builtin.falsy + or openstack_defaults.auth.username is ansible.builtin.falsy + or openstack_defaults.auth.password is ansible.builtin.falsy + or openstack_defaults.auth.project_id is ansible.builtin.falsy + +- name: Checking if {{ custom_vm_name | default(vm_name) }} exists... + openstack.cloud.server_info: + name: "{{ custom_vm_name | default(vm_name) }}" + delegate_to: localhost + become: false + register: openstack_vm_exists + +- name: Including {{ custom_vm_name | default(vm_name) }} removal tasks... + ansible.builtin.include_tasks: remove.yml + when: deploy_mode in ['undeploy', 'redeploy'] + +- name: Including {{ custom_vm_name | default(vm_name) }} creation tasks... + ansible.builtin.include_tasks: create.yml diff --git a/nova/core/roles/machine_operations/tasks/openstack/remove.yml b/nova/core/roles/machine_operations/tasks/openstack/remove.yml new file mode 100644 index 000000000..9ea428b1c --- /dev/null +++ b/nova/core/roles/machine_operations/tasks/openstack/remove.yml @@ -0,0 +1,29 @@ +--- +- name: NO UNDEPLOY + ansible.builtin.fail: + msg: "{{ inventory_hostname }} Has no_undeploy set and won't be removed" + when: no_undeploy or ['no_undeploy', 'custom_no_undeploy'] | intersect(group_names) | length > 0 + +- name: Removing {{ custom_vm_name | default(vm_name) }} and related configuration... + delegate_to: localhost + become: false + block: + - name: Removing {{ custom_vm_name | default(vm_name) }} VM... + openstack.cloud.server: + name: "{{ custom_vm_name | default(vm_name) }}" + delete_ips: true + state: absent + + - name: Removing any lingering volumes for {{ custom_vm_name | default(vm_name) }} VM... + openstack.cloud.volume: + name: "{{ custom_vm_name | default(vm_name) }}_volume" + state: absent + + - name: Removing existing keypair for {{ machine_operations_openstack_temp_ssh_key_name }}... + openstack.cloud.keypair: + name: "{{ machine_operations_openstack_temp_ssh_key_name }}" + state: absent + +- name: Stopping play... + ansible.builtin.meta: end_host + when: deploy_mode == 'undeploy' From 55949bdf006b177d9944e0171280704943ef25b9 Mon Sep 17 00:00:00 2001 From: Allar Viik Date: Wed, 26 Nov 2025 22:52:31 +0200 Subject: [PATCH 02/15] Fixed an issue where defaulting creds in `deploy_vars` role did not always work when environment_name or project_fullname was missing --- nova/core/roles/deploy_vars/defaults/main.yml | 55 +++++++++++-------- 1 file changed, 31 insertions(+), 24 deletions(-) diff --git a/nova/core/roles/deploy_vars/defaults/main.yml b/nova/core/roles/deploy_vars/defaults/main.yml index 53460118b..47bf14a89 100644 --- a/nova/core/roles/deploy_vars/defaults/main.yml +++ b/nova/core/roles/deploy_vars/defaults/main.yml @@ -144,11 +144,11 @@ vmware_workstation: aws_defaults: region: "{{ aws_region | default('eu-north-1') }}" # Default is Stockholm - aws_access_key: "{{ vars[environment_name ~ '_' ~ project_fullname ~ '_aws_access_key_id'] - | default(vars[project_fullname ~ '_aws_access_key_id']) + aws_access_key: "{{ vars[environment_name | default('') ~ '_' ~ project_fullname | default('') ~ '_aws_access_key_id'] + | default(vars[project_fullname | default('') ~ '_aws_access_key_id']) | default(aws_access_key_id) | default('') }}" - aws_secret_key: "{{ vars[environment_name ~ '_' ~ project_fullname ~ '_aws_access_key'] - | default(vars[project_fullname ~ '_aws_access_key']) + aws_secret_key: "{{ vars[environment_name | default('') ~ '_' ~ project_fullname | default('') ~ '_aws_access_key'] + | default(vars[project_fullname | default('') ~ '_aws_access_key']) | default(aws_access_key) | default('') }}" ######### @@ -159,22 +159,24 @@ azure_resource_group: "{{ project_fullname | default('') }}" azure_location: westeurope azure_defaults: # Current subscription ID from https://portal.azure.com/#blade/Microsoft_Azure_Billing/SubscriptionsBlade - subscription_id: "{{ vars[environment_name ~ '_' ~ project_fullname ~ '_azure_subscription_id'] - | default(vars[project_fullname ~ '_azure_subscription_id']) + subscription_id: + "{{ vars[environment_name | default('') ~ '_' ~ project_fullname | default('') ~ '_azure_subscription_id'] + | default(vars[project_fullname | default('') ~ '_azure_subscription_id']) | default(azure_subscription_id) | default('') }}" # The Service Principal Name's (Service Account used for deployment) ID from # https://portal.azure.com/#blade/Microsoft_AAD_IAM/ActiveDirectoryMenuBlade/RegisteredApps - client_id: "{{ vars[environment_name ~ '_' ~ project_fullname ~ '_azure_client_id'] - | default(vars[project_fullname ~ '_azure_client_id']) + client_id: "{{ vars[environment_name | default('') ~ '_' ~ project_fullname | default('') ~ '_azure_client_id'] + | default(vars[project_fullname | default('') ~ '_azure_client_id']) | default(azure_client_id) | default('') }}" # The current Tenant's ID from https://portal.azure.com/#blade/Microsoft_AAD_IAM/ActiveDirectoryMenuBlade/RegisteredApps - tenant: "{{ vars[environment_name ~ '_' ~ project_fullname ~ '_azure_tenant_id'] - | default(vars[project_fullname ~ '_azure_tenant_id']) + tenant: "{{ vars[environment_name | default('') ~ '_' ~ project_fullname | default('') ~ '_azure_tenant_id'] + | default(vars[project_fullname | default('') ~ '_azure_tenant_id']) | default(azure_tenant_id) | default('') }}" - secret: "{{ vars[environment_name ~ '_' ~ project_fullname ~ '_azure_service_principal_secret'] - | default(vars[project_fullname ~ '_azure_service_principal_secret']) + secret: + "{{ vars[environment_name | default('') ~ '_' ~ project_fullname | default('') ~ '_azure_service_principal_secret'] + | default(vars[project_fullname | default('') ~ '_azure_service_principal_secret']) | default(azure_service_principal_secret) | default('') }}" ########### @@ -183,14 +185,16 @@ azure_defaults: proxmox_defaults: api_host: "{{ proxmox_api_host | default('') }}" - api_user: "{{ vars[environment_name ~ '_' ~ project_fullname ~ '_proxmox_api_user'] - | default(vars[project_fullname ~ '_proxmox_api_user']) + api_user: "{{ vars[environment_name | default('') ~ '_' ~ project_fullname | default('') ~ '_proxmox_api_user'] + | default(vars[project_fullname | default('') ~ '_proxmox_api_user']) | default(proxmox_api_user) | default('') }}" - api_token_id: "{{ vars[environment_name ~ '_' ~ project_fullname ~ '_proxmox_api_token_id'] - | default(vars[project_fullname ~ '_proxmox_api_token_id']) + api_token_id: + "{{ vars[environment_name | default('') ~ '_' ~ project_fullname | default('') ~ '_proxmox_api_token_id'] + | default(vars[project_fullname | default('') ~ '_proxmox_api_token_id']) | default(proxmox_api_token_id) | default('') }}" - api_token_secret: "{{ vars[environment_name ~ '_' ~ project_fullname ~ '_proxmox_api_token_secret'] - | default(vars[project_fullname ~ '_proxmox_api_token_secret']) + api_token_secret: + "{{ vars[environment_name | default('') ~ '_' ~ project_fullname | default('') ~ '_proxmox_api_token_secret'] + | default(vars[project_fullname | default('') ~ '_proxmox_api_token_secret']) | default(proxmox_api_token_secret) | default('') }}" proxmox_validate_certs: true @@ -210,12 +214,15 @@ openstack_defaults: | default(deployer_username) | default('') }}" password: "{{ openstack_password | default(env_project_deployer_password) | default(project_deployer_password) | default(deployer_password) | default('') }}" - project_id: "{{ vars[environment_name ~ '_' ~ project_fullname ~ '_openstack_project_id'] - | default(vars[project_fullname ~ '_openstack_project_id']) + project_id: + "{{ vars[environment_name | default('') ~ '_' ~ project_fullname | default('') ~ '_openstack_project_id'] + | default(vars[project_fullname | default('') ~ '_openstack_project_id']) | default(openstack_project_id) | default('') }}" - user_domain_name: "{{ vars[environment_name ~ '_' ~ project_fullname ~ '_openstack_user_domain_name'] - | default(vars[project_fullname ~ '_openstack_user_domain_name']) + user_domain_name: + "{{ vars[environment_name | default('') ~ '_' ~ project_fullname | default('') ~ '_openstack_user_domain_name'] + | default(vars[project_fullname | default('') ~ '_openstack_user_domain_name']) | default(openstack_user_domain_name) | default('Default') }}" - project_domain_name: "{{ vars[environment_name ~ '_' ~ project_fullname ~ '_openstack_project_domain_name'] - | default(vars[project_fullname ~ '_openstack_project_domain_name']) + project_domain_name: + "{{ vars[environment_name | default('') ~ '_' ~ project_fullname | default('') ~ '_openstack_project_domain_name'] + | default(vars[project_fullname | default('') ~ '_openstack_project_domain_name']) | default(openstack_project_domain_name) | default('Default') }}" From 1cbebbd9132b8fafea26ac8a710b9a4a81d282da Mon Sep 17 00:00:00 2001 From: Allar Viik Date: Wed, 26 Nov 2025 22:53:23 +0200 Subject: [PATCH 03/15] Fixed an issue in `machine_operations` role where env or project specific AWS creds were not checked correctly --- nova/core/galaxy.yml | 2 +- nova/core/roles/machine_operations/tasks/aws/main.yml | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/nova/core/galaxy.yml b/nova/core/galaxy.yml index 28396f17e..023f774c3 100644 --- a/nova/core/galaxy.yml +++ b/nova/core/galaxy.yml @@ -1,6 +1,6 @@ namespace: nova name: core -version: 6.8.19 +version: 6.8.20 readme: README.md authors: - https://github.com/novateams diff --git a/nova/core/roles/machine_operations/tasks/aws/main.yml b/nova/core/roles/machine_operations/tasks/aws/main.yml index 164e1a7ab..b74008756 100644 --- a/nova/core/roles/machine_operations/tasks/aws/main.yml +++ b/nova/core/roles/machine_operations/tasks/aws/main.yml @@ -20,8 +20,8 @@ required_variables: - aws_access_key - aws_access_key_id - when: aws_access_key_id | default(false) is ansible.builtin.falsy - or aws_access_key | default(false) is ansible.builtin.falsy + when: aws_defaults.aws_access_key | default(false) is ansible.builtin.falsy + or aws_defaults.aws_secret_key | default(false) is ansible.builtin.falsy - name: MISSING DEPLOYER SSH KEY ansible.builtin.fail: From bd08d679e66fdc94ffe2d13a03b071d254002233 Mon Sep 17 00:00:00 2001 From: Allar Viik Date: Wed, 26 Nov 2025 22:57:08 +0200 Subject: [PATCH 04/15] Fixed an issue in `machine_operations` role where env or project specific credentials where not checked for Azure --- nova/core/galaxy.yml | 2 +- nova/core/roles/machine_operations/tasks/azure/main.yml | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/nova/core/galaxy.yml b/nova/core/galaxy.yml index 023f774c3..e9c730efc 100644 --- a/nova/core/galaxy.yml +++ b/nova/core/galaxy.yml @@ -1,6 +1,6 @@ namespace: nova name: core -version: 6.8.20 +version: 6.8.21 readme: README.md authors: - https://github.com/novateams diff --git a/nova/core/roles/machine_operations/tasks/azure/main.yml b/nova/core/roles/machine_operations/tasks/azure/main.yml index f7d4b9511..b1af0893d 100644 --- a/nova/core/roles/machine_operations/tasks/azure/main.yml +++ b/nova/core/roles/machine_operations/tasks/azure/main.yml @@ -22,10 +22,10 @@ - azure_service_principal_secret - azure_subscription_id - azure_tenant_id - when: azure_client_id | default(false) is ansible.builtin.falsy - or azure_service_principal_secret | default(false) is ansible.builtin.falsy - or azure_subscription_id | default(false) is ansible.builtin.falsy - or azure_tenant_id | default(false) is ansible.builtin.falsy + when: azure_defaults.subscription_id | default(false) is ansible.builtin.falsy + or azure_defaults.client_id | default(false) is ansible.builtin.falsy + or azure_defaults.tenant | default(false) is ansible.builtin.falsy + or azure_defaults.secret | default(false) is ansible.builtin.falsy - name: Checking for resource group and VM existence... delegate_to: localhost From d3f2ea3d2be25673c86eb3d29bff047a3a0bd5f1 Mon Sep 17 00:00:00 2001 From: Allar Viik Date: Tue, 2 Dec 2025 13:14:32 +0200 Subject: [PATCH 05/15] Set `keycloak` version to 26.4.7 --- nova/core/galaxy.yml | 2 +- nova/core/roles/keycloak/defaults/main.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/nova/core/galaxy.yml b/nova/core/galaxy.yml index e9c730efc..209f8361b 100644 --- a/nova/core/galaxy.yml +++ b/nova/core/galaxy.yml @@ -1,6 +1,6 @@ namespace: nova name: core -version: 6.8.21 +version: 6.8.22 readme: README.md authors: - https://github.com/novateams diff --git a/nova/core/roles/keycloak/defaults/main.yml b/nova/core/roles/keycloak/defaults/main.yml index b6d985e91..e0793d5d7 100644 --- a/nova/core/roles/keycloak/defaults/main.yml +++ b/nova/core/roles/keycloak/defaults/main.yml @@ -1,6 +1,6 @@ --- # The Keycloak version and the Keycloak Config CLI version should be compatible -keycloak_version: 26.4.6 +keycloak_version: 26.4.7 # https://github.com/adorsys/keycloak-config-cli # https://hub.docker.com/r/adorsys/keycloak-config-cli/tags From 3708d4c1629cc50cbe0a3879cb2c42a0af64117b Mon Sep 17 00:00:00 2001 From: Allar Viik Date: Wed, 3 Dec 2025 09:33:51 +0200 Subject: [PATCH 06/15] Updated Github actions versions --- .github/workflows/release.yml | 2 +- nova/core/galaxy.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 9e507b5b2..ebfecc9a9 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -17,7 +17,7 @@ jobs: steps: - name: Cloning the repository repo... - uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0 + uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 with: fetch-depth: 0 fetch-tags: true diff --git a/nova/core/galaxy.yml b/nova/core/galaxy.yml index 209f8361b..188aab7cf 100644 --- a/nova/core/galaxy.yml +++ b/nova/core/galaxy.yml @@ -1,6 +1,6 @@ namespace: nova name: core -version: 6.8.22 +version: 6.8.23 readme: README.md authors: - https://github.com/novateams From 3957d6a163ffce422a1f4e0bf422f6cef51b5528 Mon Sep 17 00:00:00 2001 From: Allar Viik Date: Wed, 3 Dec 2025 14:14:52 +0200 Subject: [PATCH 07/15] Fixed an issue where missing secrets were evaluated in `secrets_to_vault` thus breaking certaini deploy modes --- nova/core/galaxy.yml | 2 +- nova/core/roles/secrets_to_vault/defaults/main.yml | 2 -- nova/core/roles/secrets_to_vault/tasks/main.yml | 9 ++++++++- 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/nova/core/galaxy.yml b/nova/core/galaxy.yml index 188aab7cf..23d122607 100644 --- a/nova/core/galaxy.yml +++ b/nova/core/galaxy.yml @@ -1,6 +1,6 @@ namespace: nova name: core -version: 6.8.23 +version: 6.8.24 readme: README.md authors: - https://github.com/novateams diff --git a/nova/core/roles/secrets_to_vault/defaults/main.yml b/nova/core/roles/secrets_to_vault/defaults/main.yml index 0f47b6c1d..000555807 100644 --- a/nova/core/roles/secrets_to_vault/defaults/main.yml +++ b/nova/core/roles/secrets_to_vault/defaults/main.yml @@ -12,8 +12,6 @@ admin_accounts: [] user_accounts: [] # List of domain user accounts to save to Vault usually defined in host var for the domain controller domain_user_accounts: [] -# Combine all account types to one list -accounts: "{{ user_accounts + admin_accounts + domain_user_accounts }}" ########### # Secrets # diff --git a/nova/core/roles/secrets_to_vault/tasks/main.yml b/nova/core/roles/secrets_to_vault/tasks/main.yml index 9d846e120..c3758d82e 100644 --- a/nova/core/roles/secrets_to_vault/tasks/main.yml +++ b/nova/core/roles/secrets_to_vault/tasks/main.yml @@ -1,5 +1,5 @@ --- -- name: Getting Authentication token for {{ secrets_vault_address }}... +- name: Getting Authentication token for {{ secrets_vault_address | default('') }}... ansible.builtin.uri: url: "{{ secrets_vault_address }}/v1/auth/ldap/login/{{ vault_username }}" method: POST @@ -33,6 +33,13 @@ label: "{{ sct.key }}" when: secrets != [] +# Combine all account types to one list +# Since this can use Vault lookups, we want to avoid evaluating it unless needed +# For that reason, this variable is set by set_fact instead of defaults/main.yml +- name: Setting accounts variable + ansible.builtin.set_fact: + accounts: "{{ user_accounts + admin_accounts + domain_user_accounts }}" + - name: Including accounts save secrets tasks... ansible.builtin.include_tasks: save_account_password.yml loop: "{{ accounts if secrets == [] else [] }}" # This is to avoid evaluating Vault lookups when not saving account passwords to Vault From daacb0688d55c9268cebcebbb2b7b8a36d978758 Mon Sep 17 00:00:00 2001 From: Allar Viik Date: Wed, 3 Dec 2025 14:34:58 +0200 Subject: [PATCH 08/15] Unlocked `docker` version since issues with 29.x.x have been fixed --- nova/core/galaxy.yml | 2 +- nova/core/roles/docker/defaults/main.yml | 2 ++ nova/core/roles/monolith/meta/main.yml | 3 --- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/nova/core/galaxy.yml b/nova/core/galaxy.yml index 23d122607..c0b6b9ccc 100644 --- a/nova/core/galaxy.yml +++ b/nova/core/galaxy.yml @@ -1,6 +1,6 @@ namespace: nova name: core -version: 6.8.24 +version: 6.8.25 readme: README.md authors: - https://github.com/novateams diff --git a/nova/core/roles/docker/defaults/main.yml b/nova/core/roles/docker/defaults/main.yml index 4cae1b598..a29c2896d 100644 --- a/nova/core/roles/docker/defaults/main.yml +++ b/nova/core/roles/docker/defaults/main.yml @@ -25,6 +25,8 @@ docker_network: ipv4_subnet: 172.18.0.0/16 ipv6_subnet: fd42::/64 +# Default is latest version available in the Docker APT repository but can be locked to a specific major version +docker_engine_version: {} # Docker APT proxy docker_apt_proxy: https://download.docker.com/linux/{{ ansible_distribution | lower }} diff --git a/nova/core/roles/monolith/meta/main.yml b/nova/core/roles/monolith/meta/main.yml index b6d27c8ef..4eb29062e 100644 --- a/nova/core/roles/monolith/meta/main.yml +++ b/nova/core/roles/monolith/meta/main.yml @@ -1,6 +1,3 @@ --- dependencies: - role: nova.core.docker - vars: - # Temporary fix where Docker 29+ does not pull the Providentia image correctly after Vault has been composed or vice versa - docker_engine_version: 28 From 497076b415b7e307e49cda2d2e72161f26d33963 Mon Sep 17 00:00:00 2001 From: Allar Viik Date: Wed, 3 Dec 2025 14:35:46 +0200 Subject: [PATCH 09/15] Fixed an issue where `docker` role needed 2 runs to update previously locked versions --- nova/core/galaxy.yml | 2 +- nova/core/roles/docker/defaults/main.yml | 15 ++++++++--- .../roles/docker/tasks/debian_os_default.yml | 25 ++++++++++++++----- nova/core/roles/docker/tasks/windows.yml | 2 +- 4 files changed, 32 insertions(+), 12 deletions(-) diff --git a/nova/core/galaxy.yml b/nova/core/galaxy.yml index c0b6b9ccc..e596e1737 100644 --- a/nova/core/galaxy.yml +++ b/nova/core/galaxy.yml @@ -1,6 +1,6 @@ namespace: nova name: core -version: 6.8.25 +version: 6.8.26 readme: README.md authors: - https://github.com/novateams diff --git a/nova/core/roles/docker/defaults/main.yml b/nova/core/roles/docker/defaults/main.yml index a29c2896d..3c497d30d 100644 --- a/nova/core/roles/docker/defaults/main.yml +++ b/nova/core/roles/docker/defaults/main.yml @@ -1,5 +1,6 @@ --- -docker_engine_version: 28 +# Default docker daemon.json template to be used +# Update the path if you have a custom template docker_daemon_template: daemon.json # Docker daemon.json defaults @@ -9,15 +10,16 @@ docker_buildkit: true docker_enable_ipv6: true docker_enable_ip6tables: true docker_enable_iptables: true -docker_firewall_backend: iptables # Options are iptables or (still experimental) nftables +docker_firewall_backend: iptables # Options are iptables or still experimental nftables docker_bridge_ipv6_range: fd69::/64 docker_userland_proxy: true docker_log_to_journald: false -# List of mirrors to be added to the Docker daemon config. The mirror must be a complete URL including a protocol (http:// or https://) +# List of mirrors to be added to the Docker daemon config. +# The mirror must be a complete URL including a protocol eg https:// docker_registry_mirrors: [] -# List of Docker networks to be create +# Docker networks to be create docker_create_network: true docker_network: name: local-network @@ -25,8 +27,13 @@ docker_network: ipv4_subnet: 172.18.0.0/16 ipv6_subnet: fd42::/64 +############# +# Debian OS # +############# + # Default is latest version available in the Docker APT repository but can be locked to a specific major version docker_engine_version: {} + # Docker APT proxy docker_apt_proxy: https://download.docker.com/linux/{{ ansible_distribution | lower }} diff --git a/nova/core/roles/docker/tasks/debian_os_default.yml b/nova/core/roles/docker/tasks/debian_os_default.yml index 8f62d64a3..95cab196f 100644 --- a/nova/core/roles/docker/tasks/debian_os_default.yml +++ b/nova/core/roles/docker/tasks/debian_os_default.yml @@ -52,7 +52,8 @@ retries: 6 delay: 5 -- name: "{{ 'Locking' if docker_engine_version != {} else 'Unlocking' }} Docker engine version..." # This is to avoid updating Docker with apt upgrade +# This is to avoid updating Docker with apt upgrade if a specific version is set +- name: "{{ 'Locking' if docker_engine_version != {} else 'Unlocking' }} Docker engine version..." ansible.builtin.dpkg_selections: name: "{{ docker_package }}" selection: "{{ 'hold' if docker_engine_version != {} else 'install' }}" @@ -61,6 +62,18 @@ loop: - docker-ce-cli - docker-ce + register: docker_dpkg_lock + +- name: Updating Docker packages after unlock... # noqa: package-latest + ansible.builtin.apt: + name: + - docker-ce-cli + - docker-ce + state: latest + update_cache: true + when: + - docker_engine_version == {} + - docker_dpkg_lock.changed - name: Adding Docker daemon config... ansible.builtin.template: @@ -81,10 +94,10 @@ - name: Including default network creation tasks... when: docker_create_network | bool block: - - name: Checking if {{ docker_network.name }} exists... - community.docker.docker_network_info: - name: "{{ docker_network.name }}" - register: docker_network_check + # - name: Checking if {{ docker_network.name }} exists... + # community.docker.docker_network_info: + # name: "{{ docker_network.name }}" + # register: docker_network_check - name: Creating {{ docker_network.name }}... community.docker.docker_network: @@ -93,4 +106,4 @@ ipam_config: - subnet: "{{ docker_network.ipv4_subnet }}" - subnet: "{{ docker_network.ipv6_subnet }}" - when: not docker_network_check.exists + # when: not docker_network_check.exists diff --git a/nova/core/roles/docker/tasks/windows.yml b/nova/core/roles/docker/tasks/windows.yml index a9ddab4d7..d16167d05 100644 --- a/nova/core/roles/docker/tasks/windows.yml +++ b/nova/core/roles/docker/tasks/windows.yml @@ -5,7 +5,7 @@ start_mode: auto state: started -- name: Instaling Hyper-V & Containers with sub features and management tools... +- name: Installing Hyper-V & Containers with sub features and management tools... ansible.windows.win_feature: name: - Containers From eab874149167f8619bb6b9df4ceda6db35edf4a4 Mon Sep 17 00:00:00 2001 From: Allar Viik Date: Wed, 3 Dec 2025 14:51:17 +0200 Subject: [PATCH 10/15] Added a feature to create multiple networks with `docker` role --- nova/core/galaxy.yml | 2 +- nova/core/roles/docker/README.md | 20 +++++++++++-- nova/core/roles/docker/defaults/main.yml | 20 ++++++++----- nova/core/roles/docker/tasks/archlinux.yml | 28 ++++++++----------- .../roles/docker/tasks/debian_os_default.yml | 28 ++++++++----------- 5 files changed, 56 insertions(+), 42 deletions(-) diff --git a/nova/core/galaxy.yml b/nova/core/galaxy.yml index e596e1737..65cd4aff7 100644 --- a/nova/core/galaxy.yml +++ b/nova/core/galaxy.yml @@ -1,6 +1,6 @@ namespace: nova name: core -version: 6.8.26 +version: 6.9.2 readme: README.md authors: - https://github.com/novateams diff --git a/nova/core/roles/docker/README.md b/nova/core/roles/docker/README.md index 128d39be5..12908b6e5 100644 --- a/nova/core/roles/docker/README.md +++ b/nova/core/roles/docker/README.md @@ -14,10 +14,26 @@ Refer to the [defaults/main.yml](https://github.com/novateams/nova.core/blob/mai none -## Example +## Examples ```yaml -- name: Including docker role... +- name: Including docker role ansible.builtin.include_role: name: nova.core.docker ``` + +```yaml +- name: Including docker with multiple custom networks... + ansible.builtin.include_role: + name: nova.core.docker + vars: + docker_networks: + - name: local-network + enable_ipv6: true + ipv4_subnet: 172.18.0.0/16 + ipv6_subnet: fd42::/64 + - name: local-network2 + enable_ipv6: true + ipv4_subnet: 172.19.0.0/16 + ipv6_subnet: fd43::/64 +``` diff --git a/nova/core/roles/docker/defaults/main.yml b/nova/core/roles/docker/defaults/main.yml index 3c497d30d..6c148c7d7 100644 --- a/nova/core/roles/docker/defaults/main.yml +++ b/nova/core/roles/docker/defaults/main.yml @@ -1,4 +1,8 @@ --- +######### +# Linux # +######### + # Default docker daemon.json template to be used # Update the path if you have a custom template docker_daemon_template: daemon.json @@ -19,13 +23,15 @@ docker_log_to_journald: false # The mirror must be a complete URL including a protocol eg https:// docker_registry_mirrors: [] -# Docker networks to be create -docker_create_network: true -docker_network: - name: local-network - enable_ipv6: true - ipv4_subnet: 172.18.0.0/16 - ipv6_subnet: fd42::/64 +# Docker networks to be created by default +# Set docker_networks: [] to disable default network creation +# For more advanced use cases use the following module separately after this role +# https://docs.ansible.com/projects/ansible/latest/collections/community/docker/docker_network_module.html +docker_networks: + - name: local-network + enable_ipv6: true + ipv4_subnet: 172.18.0.0/16 + ipv6_subnet: fd42::/64 ############# # Debian OS # diff --git a/nova/core/roles/docker/tasks/archlinux.yml b/nova/core/roles/docker/tasks/archlinux.yml index 7bf729e80..bdb8f6682 100644 --- a/nova/core/roles/docker/tasks/archlinux.yml +++ b/nova/core/roles/docker/tasks/archlinux.yml @@ -40,19 +40,15 @@ state: restarted when: daemon_config.changed or docker_install.changed -- name: Including default network creation tasks... - when: docker_create_network - block: - - name: Checking if {{ docker_network.name }} exists... - community.docker.docker_network_info: - name: "{{ docker_network.name }}" - register: docker_network_check - - - name: Creating {{ docker_network.name }}... - community.docker.docker_network: - name: "{{ docker_network.name }}" - enable_ipv6: "{{ docker_network.enable_ipv6 }}" - ipam_config: - - subnet: "{{ docker_network.ipv4_subnet }}" - - subnet: "{{ docker_network.ipv6_subnet }}" - when: not docker_network_check.exists +- name: Creating following Docker networks... + community.docker.docker_network: + name: "{{ docker_net.name }}" + enable_ipv6: "{{ docker_net.enable_ipv6 }}" + ipam_config: + - subnet: "{{ docker_net.ipv4_subnet }}" + - subnet: "{{ docker_net.ipv6_subnet }}" + loop: "{{ docker_networks }}" + loop_control: + loop_var: docker_net + label: "{{ docker_net.name }}" + when: docker_networks != [] diff --git a/nova/core/roles/docker/tasks/debian_os_default.yml b/nova/core/roles/docker/tasks/debian_os_default.yml index 95cab196f..f2973f19b 100644 --- a/nova/core/roles/docker/tasks/debian_os_default.yml +++ b/nova/core/roles/docker/tasks/debian_os_default.yml @@ -91,19 +91,15 @@ state: restarted when: daemon_config.changed -- name: Including default network creation tasks... - when: docker_create_network | bool - block: - # - name: Checking if {{ docker_network.name }} exists... - # community.docker.docker_network_info: - # name: "{{ docker_network.name }}" - # register: docker_network_check - - - name: Creating {{ docker_network.name }}... - community.docker.docker_network: - name: "{{ docker_network.name }}" - enable_ipv6: "{{ docker_network.enable_ipv6 }}" - ipam_config: - - subnet: "{{ docker_network.ipv4_subnet }}" - - subnet: "{{ docker_network.ipv6_subnet }}" - # when: not docker_network_check.exists +- name: Creating following Docker networks... + community.docker.docker_network: + name: "{{ docker_net.name }}" + enable_ipv6: "{{ docker_net.enable_ipv6 }}" + ipam_config: + - subnet: "{{ docker_net.ipv4_subnet }}" + - subnet: "{{ docker_net.ipv6_subnet }}" + loop: "{{ docker_networks }}" + loop_control: + loop_var: docker_net + label: "{{ docker_net.name }}" + when: docker_networks != [] From 02c423490ebd1f01ca5bf1e06e05d921312abfdd Mon Sep 17 00:00:00 2001 From: Allar Viik Date: Thu, 4 Dec 2025 11:33:46 +0200 Subject: [PATCH 11/15] In `configure_networking` fixed an issue where `|` was interpreted incorrectly when configuring Alpine networking on vSphere --- nova/core/galaxy.yml | 2 +- .../tasks/vsphere/interfaces_alpine.yml | 15 +++++++++------ 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/nova/core/galaxy.yml b/nova/core/galaxy.yml index 65cd4aff7..44dafccd3 100644 --- a/nova/core/galaxy.yml +++ b/nova/core/galaxy.yml @@ -1,6 +1,6 @@ namespace: nova name: core -version: 6.9.2 +version: 6.9.4 readme: README.md authors: - https://github.com/novateams diff --git a/nova/core/roles/configure_networking/tasks/vsphere/interfaces_alpine.yml b/nova/core/roles/configure_networking/tasks/vsphere/interfaces_alpine.yml index a44d3d618..ecdbf90d3 100644 --- a/nova/core/roles/configure_networking/tasks/vsphere/interfaces_alpine.yml +++ b/nova/core/roles/configure_networking/tasks/vsphere/interfaces_alpine.yml @@ -55,14 +55,17 @@ - ENTER # Interfaces + # Not using | character since it gets interpreted incorrectly by the sendkey module from some keyboard layouts - name: Sending interfaces config to {{ custom_vm_name | default(vm_name) }}... community.vmware.vmware_guest_sendkey: - string_send: "echo {{ slurped_network_files.results[0].content }} | base64 -d > /etc/network/interfaces" + string_send: + "echo {{ slurped_network_files.results[0].content }} > /tmp/interfaces_base64 && base64 + -d /tmp/interfaces_base64 > /etc/network/interfaces" name: "{{ custom_vm_name | default(vm_name) }}" - when: slurped_network_files.results[0].content | length <= 500 + when: slurped_network_files.results[0].content | length <= 1000 - name: Configuring long string interfaces file for {{ custom_vm_name | default(vm_name) }}... - when: slurped_network_files.results[0].content | length >= 500 + when: slurped_network_files.results[0].content | length >= 1000 block: - name: Sending interfaces configs to {{ custom_vm_name | default(vm_name) }}... community.vmware.vmware_guest_sendkey: @@ -77,7 +80,7 @@ - name: Sending base64 decode command to {{ custom_vm_name | default(vm_name) }}... community.vmware.vmware_guest_sendkey: - string_send: "cat /tmp/interfaces_base64 | base64 -d > /etc/network/interfaces" + string_send: "base64 -d /tmp/interfaces_base64 > /etc/network/interfaces" name: "{{ custom_vm_name | default(vm_name) }}" keys_send: - ENTER @@ -91,7 +94,7 @@ # DNS - name: Sending DNS config to {{ custom_vm_name | default(vm_name) }}... community.vmware.vmware_guest_sendkey: - string_send: "echo {{ slurped_network_files.results[1].content }} | base64 -d > /etc/resolv.conf" + string_send: "echo {{ slurped_network_files.results[1].content }} > /tmp/resolv_conf_base64 && base64 -d /tmp/resolv_conf_base64 > /etc/resolv.conf" name: "{{ custom_vm_name | default(vm_name) }}" - name: Committing DNS update for {{ custom_vm_name | default(vm_name) }}... @@ -103,7 +106,7 @@ # Network interface persistence - name: Sending network interface persistence config to {{ custom_vm_name | default(vm_name) }}... community.vmware.vmware_guest_sendkey: - string_send: "echo {{ slurped_network_files.results[2].content }} | base64 -d > /etc/mactab" + string_send: "echo {{ slurped_network_files.results[2].content }} > /tmp/mactab_base64 && base64 -d /tmp/mactab_base64 > /etc/mactab" name: "{{ custom_vm_name | default(vm_name) }}" - name: Committing network interface persistence update for {{ custom_vm_name | default(vm_name) }}... From 180607b76837b11063bd852dfaf61dda9c941ab7 Mon Sep 17 00:00:00 2001 From: Allar Viik Date: Thu, 4 Dec 2025 12:58:49 +0200 Subject: [PATCH 12/15] Made sure that `cleanup` role cleans up history from all shells --- nova/core/roles/cleanup/tasks/linux.yml | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/nova/core/roles/cleanup/tasks/linux.yml b/nova/core/roles/cleanup/tasks/linux.yml index a3508bd0d..19c82720a 100644 --- a/nova/core/roles/cleanup/tasks/linux.yml +++ b/nova/core/roles/cleanup/tasks/linux.yml @@ -55,13 +55,10 @@ executable: "/bin/{{ 'tcsh' if ansible_system | default('') == 'FreeBSD' else 'sh' if ansible_distribution | default('') == 'Alpine' else 'bash' }}" changed_when: true -# tcsh automatically writes current history in HISTFILE -- name: Removing Linux bash history... +- name: Removing Linux shell history... ansible.builtin.shell: | - {{ 'setenv HISTFILE "/dev/null"' if ansible_system == 'FreeBSD' else 'export HISTFILE=/dev/null' }} - rm -f /home/*/.bash_history - rm -f /root/.bash_history - history -c && {{ 'history -w && ' if ansible_system != 'FreeBSD' }}cat /dev/null > /root/.bash_history + rm -f /home/*/.*_history + rm -f /root/.*_history args: executable: "/bin/{{ 'tcsh' if ansible_system | default('') == 'FreeBSD' else 'sh' if ansible_distribution | default('') == 'Alpine' else 'bash' }}" changed_when: true From 770c8b2409eb0618ebc6cb5d883f3e97bac5af43 Mon Sep 17 00:00:00 2001 From: Allar Viik Date: Thu, 4 Dec 2025 13:49:49 +0200 Subject: [PATCH 13/15] Added Alpine and OPN/pfSense support to `updates` role --- nova/core/galaxy.yml | 2 +- nova/core/roles/updates/tasks/alpine.yml | 6 +++ nova/core/roles/updates/tasks/main.yml | 52 ++++++++++++------------ 3 files changed, 32 insertions(+), 28 deletions(-) create mode 100644 nova/core/roles/updates/tasks/alpine.yml diff --git a/nova/core/galaxy.yml b/nova/core/galaxy.yml index 44dafccd3..9a0e5d3f1 100644 --- a/nova/core/galaxy.yml +++ b/nova/core/galaxy.yml @@ -1,6 +1,6 @@ namespace: nova name: core -version: 6.9.4 +version: 6.9.5 readme: README.md authors: - https://github.com/novateams diff --git a/nova/core/roles/updates/tasks/alpine.yml b/nova/core/roles/updates/tasks/alpine.yml new file mode 100644 index 000000000..7d717179d --- /dev/null +++ b/nova/core/roles/updates/tasks/alpine.yml @@ -0,0 +1,6 @@ +--- +- name: Updating packages... + community.general.apk: + update_cache: true + upgrade: true + state: present diff --git a/nova/core/roles/updates/tasks/main.yml b/nova/core/roles/updates/tasks/main.yml index 5982b49d5..e68e3e64a 100644 --- a/nova/core/roles/updates/tasks/main.yml +++ b/nova/core/roles/updates/tasks/main.yml @@ -1,34 +1,32 @@ --- -- name: Including non-network OS update tasks... - when: ansible_network_os is not defined - block: - - name: Including Windows update tasks... - ansible.builtin.include_tasks: windows.yml - when: ansible_os_family == "Windows" +- name: Including Windows update tasks... + ansible.builtin.include_tasks: windows.yml + when: ansible_os_family | default('') == "Windows" - - name: Including Debian family update tasks... - ansible.builtin.include_tasks: debian_family.yml - when: ansible_os_family == 'Debian' +- name: Including Debian family update tasks... + ansible.builtin.include_tasks: debian_family.yml + when: ansible_os_family | default('') == 'Debian' - - name: Including Archlinux update tasks... - ansible.builtin.include_tasks: archlinux_family.yml - when: ansible_os_family == 'Archlinux' +- name: Including Archlinux update tasks... + ansible.builtin.include_tasks: archlinux_family.yml + when: ansible_os_family | default('') == 'Archlinux' - - name: Including RedHat family update tasks... - ansible.builtin.include_tasks: redhat_family.yml - when: ansible_os_family == 'RedHat' +- name: Including RedHat family update tasks... + ansible.builtin.include_tasks: redhat_family.yml + when: ansible_os_family | default('') == 'RedHat' - - name: Including FreeBSD update tasks... - ansible.builtin.include_tasks: freebsd.yml - when: ansible_system == "FreeBSD" +- name: Including Alpine update tasks... + ansible.builtin.include_tasks: alpine.yml + when: ansible_os_family | default('') == 'Alpine' - - name: Including MacOS update tasks... - ansible.builtin.include_tasks: macos.yml - when: ansible_system == "Darwin" +- name: Including FreeBSD update tasks... + ansible.builtin.include_tasks: freebsd.yml + when: ansible_system | default('') == "FreeBSD" -- name: Including network OS update tasks... - when: ansible_network_os is defined - block: - - name: Including RouterOS update tasks... - ansible.builtin.include_tasks: routeros.yml - when: ansible_network_os == 'community.routeros.routeros' +- name: Including MacOS update tasks... + ansible.builtin.include_tasks: macos.yml + when: ansible_system | default('') == "Darwin" + +- name: Including RouterOS update tasks... + ansible.builtin.include_tasks: routeros.yml + when: ansible_network_os | default('') == 'community.routeros.routeros' From de7b4684655047557c60cba2f1580812beb2c484 Mon Sep 17 00:00:00 2001 From: Allar Viik Date: Fri, 5 Dec 2025 14:45:48 +0200 Subject: [PATCH 14/15] In `machine_operations` role enabling firewall for all Proxmox VM NICs by default --- nova/core/galaxy.yml | 2 +- .../roles/machine_operations/defaults/main.yml | 4 ++++ .../machine_operations/tasks/proxmox/create.yml | 17 +++++++++-------- 3 files changed, 14 insertions(+), 9 deletions(-) diff --git a/nova/core/galaxy.yml b/nova/core/galaxy.yml index 9a0e5d3f1..2faf9f642 100644 --- a/nova/core/galaxy.yml +++ b/nova/core/galaxy.yml @@ -1,6 +1,6 @@ namespace: nova name: core -version: 6.9.5 +version: 6.9.7 readme: README.md authors: - https://github.com/novateams diff --git a/nova/core/roles/machine_operations/defaults/main.yml b/nova/core/roles/machine_operations/defaults/main.yml index 7f02f4031..6dbcdaad5 100644 --- a/nova/core/roles/machine_operations/defaults/main.yml +++ b/nova/core/roles/machine_operations/defaults/main.yml @@ -185,6 +185,10 @@ machine_operations_proxmox_vmid: "{{ custom_vm_name | default(vm_name) | hash('s # Start VM on boot machine_operations_proxmox_vm_start_on_boot: true +# Enable Firewall for all VM network interfaces +# True or false for all interfaces. Use a custom role/task to enable/disable per interface if needed +machine_operations_proxmox_enable_firewall: true + ############# # OpenStack # ############# diff --git a/nova/core/roles/machine_operations/tasks/proxmox/create.yml b/nova/core/roles/machine_operations/tasks/proxmox/create.yml index 4b022ab89..fbf8017b8 100644 --- a/nova/core/roles/machine_operations/tasks/proxmox/create.yml +++ b/nova/core/roles/machine_operations/tasks/proxmox/create.yml @@ -81,11 +81,11 @@ target: "{{ proxmox_node }}" timeout: "{{ proxmox_machine_operations_operation_timeout }}" - - name: Creating /tmp/proxmox_vm_clone{{ project_fullname | default('') }}_{{ custom_vm_name | default(vm_name) }}.done file... + - name: Creating /tmp/proxmox_vm_clone_{{ project_fullname | default('') }}_{{ custom_vm_name | default(vm_name) }}.done file... delegate_to: localhost become: false ansible.builtin.file: - path: /tmp/proxmox_vm_clone_{{ project_fullname | default('') }}_{{ inventory_hostname }}.done + path: /tmp/proxmox_vm_clone_{{ project_fullname | default('') }}_{{ custom_vm_name | default(vm_name) }}.done state: touch mode: "0644" @@ -93,11 +93,11 @@ rescue: # When cloning fails first making sure the limiter file is created # so the next host in the batch can proceed with cloning - - name: Creating /tmp/proxmox_vm_clone{{ project_fullname | default('') }}_{{ custom_vm_name | default(vm_name) }}.done file... + - name: Creating /tmp/proxmox_vm_clone_{{ project_fullname | default('') }}_{{ custom_vm_name | default(vm_name) }}.done file... delegate_to: localhost become: false ansible.builtin.file: - path: /tmp/proxmox_vm_clone_{{ project_fullname | default('') }}_{{ inventory_hostname }}.done + path: /tmp/proxmox_vm_clone_{{ project_fullname | default('') }}_{{ custom_vm_name | default(vm_name) }}.done state: touch mode: "0644" @@ -121,7 +121,7 @@ delegate_to: localhost become: false ansible.builtin.file: - path: /tmp/proxmox_vm_clone_{{ project_fullname | default('') }}_{{ inventory_hostname }}.done + path: /tmp/proxmox_vm_clone_{{ project_fullname | default('') }}_{{ custom_vm_name | default(vm_name) }}.done state: absent - name: FAILED CLONING ATTEMPT @@ -132,11 +132,11 @@ # Writing the file here in case the non-existing machines are in the same batch as existing ones # Otherwise the non-existing will just timeout waiting for the previous host to finish cloning - - name: Creating /tmp/proxmox_vm_clone{{ project_fullname | default('') }}_{{ custom_vm_name | default(vm_name) }}.done file... + - name: Creating /tmp/proxmox_vm_clone_{{ project_fullname | default('') }}_{{ custom_vm_name | default(vm_name) }}.done file... delegate_to: localhost become: false ansible.builtin.file: - path: /tmp/proxmox_vm_clone_{{ project_fullname | default('') }}_{{ inventory_hostname }}.done + path: /tmp/proxmox_vm_clone_{{ project_fullname | default('') }}_{{ custom_vm_name | default(vm_name) }}.done state: touch mode: "0644" when: not fresh_deploy @@ -179,6 +179,7 @@ interface: "net{{ idx }}" bridge: "{{ nic.cloud_id }}" mtu: "{{ nic.mtu | default(1) }}" + firewall: "{{ machine_operations_proxmox_enable_firewall }}" loop_control: loop_var: nic index_var: idx @@ -198,5 +199,5 @@ delegate_to: localhost become: false ansible.builtin.file: - path: /tmp/proxmox_vm_clone_{{ project_fullname | default('') }}_{{ inventory_hostname }}.done + path: /tmp/proxmox_vm_clone_{{ project_fullname | default('') }}_{{ custom_vm_name | default(vm_name) }}.done state: absent From 8c729162f91009bd43a5d96b7760db3c66d5f9e2 Mon Sep 17 00:00:00 2001 From: Allar Viik Date: Mon, 8 Dec 2025 18:53:27 +0200 Subject: [PATCH 15/15] Added OPNsense network configuration method for Proxmox --- nova/core/galaxy.yml | 2 +- .../configure_networking/defaults/main.yml | 13 ++ .../tasks/proxmox/main.yml | 13 +- .../tasks/proxmox/opnsense.yml | 162 ++++++++++++++++++ .../templates/opnsense.yml | 5 +- .../tasks/proxmox/create.yml | 16 +- 6 files changed, 200 insertions(+), 11 deletions(-) create mode 100644 nova/core/roles/configure_networking/tasks/proxmox/opnsense.yml diff --git a/nova/core/galaxy.yml b/nova/core/galaxy.yml index 2faf9f642..8f06762f9 100644 --- a/nova/core/galaxy.yml +++ b/nova/core/galaxy.yml @@ -1,6 +1,6 @@ namespace: nova name: core -version: 6.9.7 +version: 6.9.9 readme: README.md authors: - https://github.com/novateams diff --git a/nova/core/roles/configure_networking/defaults/main.yml b/nova/core/roles/configure_networking/defaults/main.yml index 15b1cf8a2..74172b610 100644 --- a/nova/core/roles/configure_networking/defaults/main.yml +++ b/nova/core/roles/configure_networking/defaults/main.yml @@ -93,3 +93,16 @@ configure_networking_alpine_boot_wait_time: 60 # Time in seconds to wait for the PanOS VM to boot before sending networking configuration commands configure_networking_panos_boot_wait_time: 60 + +########### +# Proxmox # +########### + +# This is a list of interfaces to exclude when configuring networking on Proxmox VMs +# These are typically non-physical interfaces like loopback +configure_networking_proxmox_interfaces_to_exclude: + - lo + - lo0 # OPNsense loopback + - enc0 # OPNsense encryption + - pfsync0 # OPNsense pfsync + - pflog0 # OPNsense pflog diff --git a/nova/core/roles/configure_networking/tasks/proxmox/main.yml b/nova/core/roles/configure_networking/tasks/proxmox/main.yml index 3e2089ee0..1a1eb496b 100644 --- a/nova/core/roles/configure_networking/tasks/proxmox/main.yml +++ b/nova/core/roles/configure_networking/tasks/proxmox/main.yml @@ -23,7 +23,18 @@ "{{ proxmox_vm_info.proxmox_vms[0].network | map(attribute='hardware-address', default='00:00:00:00:00:00') | reject('equalto', '00:00:00:00:00:00') | list }}" configure_networking_hw_interfaces: "{{ proxmox_vm_info.proxmox_vms[0].network | map(attribute='name') - | reject('equalto', 'lo') | list }}" + | reject('in', configure_networking_proxmox_interfaces_to_exclude) | list }}" + + - name: Checking if network customization method exists... + ansible.builtin.stat: + path: "{{ role_path }}/tasks/proxmox/{{ customization_method }}.yml" + register: customization_method_file + + - name: NETWORK CUSTOMIZATION NOT IMPLEMENTED + ansible.builtin.fail: + msg: | + Network customization method {{ customization_method | upper }} is not implemented on Proxmox. + when: not customization_method_file.stat.exists - name: Including {{ customization_method }} network configuration tasks... ansible.builtin.include_tasks: "{{ customization_method }}.yml" diff --git a/nova/core/roles/configure_networking/tasks/proxmox/opnsense.yml b/nova/core/roles/configure_networking/tasks/proxmox/opnsense.yml new file mode 100644 index 000000000..f1a0c7f2f --- /dev/null +++ b/nova/core/roles/configure_networking/tasks/proxmox/opnsense.yml @@ -0,0 +1,162 @@ +--- +- name: Configuring netplan on Proxmox VM... + become: false + delegate_to: localhost + block: + - name: Downloading /conf/config.xml {{ custom_vm_name | default(vm_name) }}... + ansible.builtin.uri: + url: "{{ proxmox_api_url }}/nodes/{{ proxmox_vm_info.proxmox_vms[0].node + }}/qemu/{{ proxmox_vm_info.proxmox_vms[0].vmid }}/agent/file-read?file=/conf/config.xml" + headers: + Authorization: PVEAPIToken={{ proxmox_defaults.api_user }}!{{ proxmox_defaults.api_token_id }}={{ proxmox_defaults.api_token_secret }} + method: GET + validate_certs: "{{ proxmox_validate_certs }}" + register: config_download + + - name: Saving /conf/config.xml to a temporary file... + ansible.builtin.copy: + content: "{{ config_download.json.data.content }}" + dest: /tmp/{{ project_fullname | default('') }}_{{ inventory_hostname }}_opnsense_config.xml + mode: "0600" + + - name: Deleting existing interfaces... + community.general.xml: + path: /tmp/{{ project_fullname | default('') }}_{{ inventory_hostname }}_opnsense_config.xml + xpath: /opnsense/interfaces/* + state: absent + + - name: Deleting existing gateways... + community.general.xml: + path: /tmp/{{ project_fullname | default('') }}_{{ inventory_hostname }}_opnsense_config.xml + xpath: /opnsense/OPNsense/Gateways/* + state: absent + + - name: Deleting existing DNS servers... + community.general.xml: + path: /tmp/{{ project_fullname | default('') }}_{{ inventory_hostname }}_opnsense_config.xml + xpath: /opnsense/system/dnsserver + state: absent + + - name: Templating interfaces config... + ansible.builtin.template: + src: opnsense.yml + dest: /tmp/{{ project_fullname | default('') }}_{{ inventory_hostname }}_opnsense_interfaces.yml + lstrip_blocks: true + mode: "0600" + + - name: Including interfaces config... + ansible.builtin.include_vars: + file: /tmp/{{ project_fullname | default('') }}_{{ inventory_hostname }}_opnsense_interfaces.yml + + - name: Configuring following opnsense interfaces for {{ inventory_hostname }}... + community.general.xml: + path: /tmp/{{ project_fullname | default('') }}_{{ inventory_hostname }}_opnsense_config.xml + xpath: /opnsense/interfaces + pretty_print: true + add_children: "{{ opnsense_interfaces }}" + + - name: Configuring egress interface gateways for {{ inventory_hostname }}... + community.general.xml: + path: /tmp/{{ project_fullname | default('') }}_{{ inventory_hostname }}_opnsense_config.xml + xpath: /opnsense/OPNsense/Gateways + pretty_print: true + add_children: "{{ opnsense_gateways }}" + + - name: Configuring following DNS server for {{ inventory_hostname }}... + community.general.xml: + path: /tmp/{{ project_fullname | default('') }}_{{ inventory_hostname }}_opnsense_config.xml + xpath: /opnsense/system + pretty_print: true + add_children: + - dnsserver: "{{ item }}" + loop: "{{ dns_server_combined }}" + + - name: Enabling {{ inventory_hostname }} configured DNS servers... + community.general.xml: + path: /tmp/{{ project_fullname | default('') }}_{{ inventory_hostname }}_opnsense_config.xml + xpath: /opnsense/system + pretty_print: true + value: remote + + - name: Adding a wan management rule for {{ inventory_hostname }}... + community.general.xml: + path: /tmp/{{ project_fullname | default('') }}_{{ inventory_hostname }}_opnsense_config.xml + xpath: /opnsense/filter + pretty_print: true + add_children: + - rule: + uuid: "{{ lookup('password', '/dev/null length=32') | ansible.builtin.to_uuid }}" + _: + - type: pass + - ipprotocol: inet46 + - statetype: keep state + - direction: in + - floating: "yes" + - quick: "1" + - source: + _: + - any: "1" + - destination: + _: + - any: "1" + - descr: MGMT + when: interfaces | selectattr('egress', 'equalto', true) | first == interfaces | selectattr('connection', 'equalto', true) | first + + - name: Getting network configuration file contents... + ansible.builtin.slurp: + path: /tmp/{{ project_fullname | default('') }}_{{ inventory_hostname }}_opnsense_config.xml + register: file_contents + + - name: Getting the nr of config.xml chunks... + ansible.builtin.set_fact: + config_file_chunks: "{{ (file_contents.content | length + 1699) // 1700 }}" + + # Writing in 1700 char chunks because otherwise the Qemu Guest Agent service fails + - name: Writing /conf/config.xml to {{ custom_vm_name | default(vm_name) }}... + ansible.builtin.uri: + url: "{{ proxmox_api_url }}/nodes/{{ proxmox_vm_info.proxmox_vms[0].node + }}/qemu/{{ proxmox_vm_info.proxmox_vms[0].vmid }}/agent/file-write" + headers: + Authorization: PVEAPIToken={{ proxmox_defaults.api_user }}!{{ proxmox_defaults.api_token_id }}={{ proxmox_defaults.api_token_secret }} + method: POST + body: + content: "{{ item | b64decode }}" + file: /tmp/config_{{ '%03d' | format(file_loop) }}.xml + body_format: json + validate_certs: "{{ proxmox_validate_certs }}" + loop: "{{ file_contents.content | regex_findall('.{1,1700}') }}" + loop_control: + index_var: file_loop + label: "{{ file_loop + 1 }}/{{ config_file_chunks }}" + + - name: Removing local config.xml file... + ansible.builtin.file: + path: /tmp/{{ project_fullname | default('') }}_{{ inventory_hostname }}_opnsense_config.xml + state: absent + + - name: Writing final /conf/config.xml to {{ custom_vm_name | default(vm_name) }}... + ansible.builtin.uri: + url: "{{ proxmox_api_url }}/nodes/{{ proxmox_vm_info.proxmox_vms[0].node + }}/qemu/{{ proxmox_vm_info.proxmox_vms[0].vmid }}/agent/exec" + headers: + Authorization: PVEAPIToken={{ proxmox_defaults.api_user }}!{{ proxmox_defaults.api_token_id }}={{ proxmox_defaults.api_token_secret }} + method: POST + body: + command: + - sh + - -c + - "cat /tmp/config_*.xml > /conf/config.xml && rm -f /tmp/config_*.xml" + - reboot + body_format: json + validate_certs: "{{ proxmox_validate_certs }}" + register: network_config_command + + - name: Including command run check task... + ansible.builtin.include_tasks: command_run_check.yml + +# This is required for network config to take effect +- name: Restarting {{ custom_vm_name | default(vm_name) }} VM... + ansible.builtin.include_role: + name: nova.core.powerstate + vars: + restart: true diff --git a/nova/core/roles/configure_networking/templates/opnsense.yml b/nova/core/roles/configure_networking/templates/opnsense.yml index 4b18807b8..497ffde9a 100644 --- a/nova/core/roles/configure_networking/templates/opnsense.yml +++ b/nova/core/roles/configure_networking/templates/opnsense.yml @@ -1,7 +1,7 @@ --- {# https://forum.netgate.com/topic/85739/change-order-of-interfaces-on-vm/13 #} {# https://forum.netgate.com/topic/159909/adding-an-interface-to-a-pfsense-esx-vm-causes-them-to-be-re-ordered-on-reboot #} - +{% if infra_env == "vsphere" %} {% if interfaces | length == 1 %} {% set nic_map = ["vmx0"] %} {% elif interfaces | length == 2 %} @@ -23,6 +23,9 @@ {% elif interfaces | length == 10 %} {% set nic_map = ["vmx2","vmx5","vmx8","vmx0","vmx3","vmx6","vmx9","vmx1","vmx4","vmx7"] %} {% endif %} +{% else %} + {% set nic_map = configure_networking_hw_interfaces %} +{% endif %} opnsense_interfaces: {% for interface in interfaces %} diff --git a/nova/core/roles/machine_operations/tasks/proxmox/create.yml b/nova/core/roles/machine_operations/tasks/proxmox/create.yml index fbf8017b8..a90d4f796 100644 --- a/nova/core/roles/machine_operations/tasks/proxmox/create.yml +++ b/nova/core/roles/machine_operations/tasks/proxmox/create.yml @@ -81,11 +81,11 @@ target: "{{ proxmox_node }}" timeout: "{{ proxmox_machine_operations_operation_timeout }}" - - name: Creating /tmp/proxmox_vm_clone_{{ project_fullname | default('') }}_{{ custom_vm_name | default(vm_name) }}.done file... + - name: Creating /tmp/proxmox_vm_clone_{{ project_fullname | default('') }}_{{ inventory_hostname }}.done file... delegate_to: localhost become: false ansible.builtin.file: - path: /tmp/proxmox_vm_clone_{{ project_fullname | default('') }}_{{ custom_vm_name | default(vm_name) }}.done + path: /tmp/proxmox_vm_clone_{{ project_fullname | default('') }}_{{ inventory_hostname }}.done state: touch mode: "0644" @@ -93,11 +93,11 @@ rescue: # When cloning fails first making sure the limiter file is created # so the next host in the batch can proceed with cloning - - name: Creating /tmp/proxmox_vm_clone_{{ project_fullname | default('') }}_{{ custom_vm_name | default(vm_name) }}.done file... + - name: Creating /tmp/proxmox_vm_clone_{{ project_fullname | default('') }}_{{ inventory_hostname }}.done file... delegate_to: localhost become: false ansible.builtin.file: - path: /tmp/proxmox_vm_clone_{{ project_fullname | default('') }}_{{ custom_vm_name | default(vm_name) }}.done + path: /tmp/proxmox_vm_clone_{{ project_fullname | default('') }}_{{ inventory_hostname }}.done state: touch mode: "0644" @@ -121,7 +121,7 @@ delegate_to: localhost become: false ansible.builtin.file: - path: /tmp/proxmox_vm_clone_{{ project_fullname | default('') }}_{{ custom_vm_name | default(vm_name) }}.done + path: /tmp/proxmox_vm_clone_{{ project_fullname | default('') }}_{{ inventory_hostname }}.done state: absent - name: FAILED CLONING ATTEMPT @@ -132,11 +132,11 @@ # Writing the file here in case the non-existing machines are in the same batch as existing ones # Otherwise the non-existing will just timeout waiting for the previous host to finish cloning - - name: Creating /tmp/proxmox_vm_clone_{{ project_fullname | default('') }}_{{ custom_vm_name | default(vm_name) }}.done file... + - name: Creating /tmp/proxmox_vm_clone_{{ project_fullname | default('') }}_{{ inventory_hostname }}.done file... delegate_to: localhost become: false ansible.builtin.file: - path: /tmp/proxmox_vm_clone_{{ project_fullname | default('') }}_{{ custom_vm_name | default(vm_name) }}.done + path: /tmp/proxmox_vm_clone_{{ project_fullname | default('') }}_{{ inventory_hostname }}.done state: touch mode: "0644" when: not fresh_deploy @@ -199,5 +199,5 @@ delegate_to: localhost become: false ansible.builtin.file: - path: /tmp/proxmox_vm_clone_{{ project_fullname | default('') }}_{{ custom_vm_name | default(vm_name) }}.done + path: /tmp/proxmox_vm_clone_{{ project_fullname | default('') }}_{{ inventory_hostname }}.done state: absent