From a11b9edf3b4db809b5ecd0fb4b69727bc7a5c4a3 Mon Sep 17 00:00:00 2001 From: Paul Kroeher Date: Mon, 3 Aug 2026 15:41:53 +0200 Subject: [PATCH 01/71] nix: add option to disable destructive database setup Signed-off-by: Paul Kroeher On-behalf-of: SAP paul.kroeher@sap.com --- modules/controller/openstack-controller.nix | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/modules/controller/openstack-controller.nix b/modules/controller/openstack-controller.nix index 3861fe7..2875a71 100644 --- a/modules/controller/openstack-controller.nix +++ b/modules/controller/openstack-controller.nix @@ -9,6 +9,7 @@ }: { config, + lib, pkgs, ... }: @@ -36,9 +37,18 @@ in (import ./cinder.nix { inherit cinder; }) # only cinder management component ]; + options.openstack.production_setup = lib.mkOption { + type = lib.types.bool; + default = false; + description = '' + Whether the controller uses a production database setup. When enabled, + the destructive database setup service is disabled. + ''; + }; + config = { - systemd.services.database-setup = { + systemd.services.database-setup = lib.mkIf (!config.openstack.production_setup) { description = "OpenStack Database setup"; after = [ "mysql.service" From c2534072251a02a146e9399d5a217ad58a2a2af0 Mon Sep 17 00:00:00 2001 From: Paul Kroeher Date: Mon, 3 Aug 2026 16:56:42 +0200 Subject: [PATCH 02/71] nix: add self build openstack-client to system packages Signed-off-by: Paul Kroeher On-behalf-of: SAP paul.kroeher@sap.com --- modules/controller/openstack-controller.nix | 5 +++++ modules/default.nix | 1 + 2 files changed, 6 insertions(+) diff --git a/modules/controller/openstack-controller.nix b/modules/controller/openstack-controller.nix index 2875a71..8124a2f 100644 --- a/modules/controller/openstack-controller.nix +++ b/modules/controller/openstack-controller.nix @@ -6,6 +6,7 @@ placement, horizon, cinder, + python-openstackclient, }: { config, @@ -48,6 +49,10 @@ in config = { + environment.systemPackages = [ + python-openstackclient + ]; + systemd.services.database-setup = lib.mkIf (!config.openstack.production_setup) { description = "OpenStack Database setup"; after = [ diff --git a/modules/default.nix b/modules/default.nix index 179ed5d..ef09b5d 100644 --- a/modules/default.nix +++ b/modules/default.nix @@ -8,6 +8,7 @@ glance horizon cinder + python-openstackclient ; placement = openstackPkgs.openstack-placement; }; From 628cbf8d8b0dc2b5b19c6b7915496c8cdcfbc921 Mon Sep 17 00:00:00 2001 From: Paul Kroeher Date: Tue, 4 Aug 2026 08:39:59 +0200 Subject: [PATCH 03/71] nix: move database setup into a separate script Signed-off-by: Paul Kroeher On-behalf-of: SAP paul.kroeher@sap.com --- modules/controller/openstack-controller.nix | 100 +++++++++++--------- 1 file changed, 54 insertions(+), 46 deletions(-) diff --git a/modules/controller/openstack-controller.nix b/modules/controller/openstack-controller.nix index 8124a2f..941e405 100644 --- a/modules/controller/openstack-controller.nix +++ b/modules/controller/openstack-controller.nix @@ -24,6 +24,54 @@ let OS_AUTH_URL = "http://controller:5000/v3"; OS_IDENTITY_API_VERSION = "3"; }; + databaseSetupScript = pkgs.writeShellScript "database-setup.sh" '' + export PATH=${lib.makeBinPath [ pkgs.mariadb ]}:$PATH + + # Keystone + mysql -N -e "drop database keystone;" || true + mysql -N -e "create database keystone;" || true + mysql -N -e "GRANT ALL PRIVILEGES ON keystone.* TO 'keystone'@'localhost' IDENTIFIED BY 'keystone';" + mysql -N -e "GRANT ALL PRIVILEGES ON keystone.* TO 'keystone'@'%' IDENTIFIED BY 'keystone';" + + # Glance + mysql -N -e "drop database glance;" || true + mysql -N -e "create database glance;" || true + mysql -N -e "GRANT ALL PRIVILEGES ON glance.* TO 'glance'@'localhost' IDENTIFIED BY 'glance';" + mysql -N -e "GRANT ALL PRIVILEGES ON glance.* TO 'glance'@'%' IDENTIFIED BY 'glance';" + + # Cinder + mysql -N -e "drop database cinder;" || true + mysql -N -e "create database cinder;" || true + mysql -N -e "GRANT ALL PRIVILEGES ON cinder.* TO 'cinder'@'localhost' IDENTIFIED BY 'cinder';" + mysql -N -e "GRANT ALL PRIVILEGES ON cinder.* TO 'cinder'@'%' IDENTIFIED BY 'cinder';" + + # Placement + mysql -N -e "drop database placement;" || true + mysql -N -e "create database placement;" || true + mysql -N -e "GRANT ALL PRIVILEGES ON placement.* TO 'placement'@'localhost' IDENTIFIED BY 'placement';" + mysql -N -e "GRANT ALL PRIVILEGES ON placement.* TO 'placement'@'%' IDENTIFIED BY 'placement';" + + # Nova + mysql -N -e "drop database nova_api;" || true + mysql -N -e "drop database nova;" || true + mysql -N -e "drop database nova_cell0;" || true + mysql -N -e "create database nova_api;" || true + mysql -N -e "create database nova;" || true + mysql -N -e "create database nova_cell0;" || true + + mysql -N -e "GRANT ALL PRIVILEGES ON nova_api.* TO 'nova'@'localhost' IDENTIFIED BY 'nova';" + mysql -N -e "GRANT ALL PRIVILEGES ON nova_api.* TO 'nova'@'%' IDENTIFIED BY 'nova';" + mysql -N -e "GRANT ALL PRIVILEGES ON nova.* TO 'nova'@'localhost' IDENTIFIED BY 'nova';" + mysql -N -e "GRANT ALL PRIVILEGES ON nova.* TO 'nova'@'%' IDENTIFIED BY 'nova';" + mysql -N -e "GRANT ALL PRIVILEGES ON nova_cell0.* TO 'nova'@'localhost' IDENTIFIED BY 'nova';" + mysql -N -e "GRANT ALL PRIVILEGES ON nova_cell0.* TO 'nova'@'%' IDENTIFIED BY 'nova';" + + # Neutron + mysql -N -e "drop database neutron;" || true + mysql -N -e "create database neutron;" || true + mysql -N -e "GRANT ALL PRIVILEGES ON neutron.* TO 'neutron'@'localhost' IDENTIFIED BY 'neutron';" + mysql -N -e "GRANT ALL PRIVILEGES ON neutron.* TO 'neutron'@'%' IDENTIFIED BY 'neutron';" + ''; in { imports = [ @@ -53,6 +101,11 @@ in python-openstackclient ]; + system.activationScripts.database-setup.text = '' + install -d -m 0700 /root/os-setup + install -m 0700 ${databaseSetupScript} /root/os-setup/database-setup.sh + ''; + systemd.services.database-setup = lib.mkIf (!config.openstack.production_setup) { description = "OpenStack Database setup"; after = [ @@ -64,52 +117,7 @@ in path = [ pkgs.mariadb ]; serviceConfig = { Type = "oneshot"; - ExecStart = pkgs.writeShellScript "database-setup.sh" '' - # Keystone - mysql -N -e "drop database keystone;" || true - mysql -N -e "create database keystone;" || true - mysql -N -e "GRANT ALL PRIVILEGES ON keystone.* TO 'keystone'@'localhost' IDENTIFIED BY 'keystone';" - mysql -N -e "GRANT ALL PRIVILEGES ON keystone.* TO 'keystone'@'%' IDENTIFIED BY 'keystone';" - - # Glance - mysql -N -e "drop database glance;" || true - mysql -N -e "create database glance;" || true - mysql -N -e "GRANT ALL PRIVILEGES ON glance.* TO 'glance'@'localhost' IDENTIFIED BY 'glance';" - mysql -N -e "GRANT ALL PRIVILEGES ON glance.* TO 'glance'@'%' IDENTIFIED BY 'glance';" - - # Cinder - mysql -N -e "drop database cinder;" || true - mysql -N -e "create database cinder;" || true - mysql -N -e "GRANT ALL PRIVILEGES ON cinder.* TO 'cinder'@'localhost' IDENTIFIED BY 'cinder';" - mysql -N -e "GRANT ALL PRIVILEGES ON cinder.* TO 'cinder'@'%' IDENTIFIED BY 'cinder';" - - # Placement - mysql -N -e "drop database placement;" || true - mysql -N -e "create database placement;" || true - mysql -N -e "GRANT ALL PRIVILEGES ON placement.* TO 'placement'@'localhost' IDENTIFIED BY 'placement';" - mysql -N -e "GRANT ALL PRIVILEGES ON placement.* TO 'placement'@'%' IDENTIFIED BY 'placement';" - - # Nova - mysql -N -e "drop database nova_api;" || true - mysql -N -e "drop database nova;" || true - mysql -N -e "drop database nova_cell0;" || true - mysql -N -e "create database nova_api;" || true - mysql -N -e "create database nova;" || true - mysql -N -e "create database nova_cell0;" || true - - mysql -N -e "GRANT ALL PRIVILEGES ON nova_api.* TO 'nova'@'localhost' IDENTIFIED BY 'nova';" - mysql -N -e "GRANT ALL PRIVILEGES ON nova_api.* TO 'nova'@'%' IDENTIFIED BY 'nova';" - mysql -N -e "GRANT ALL PRIVILEGES ON nova.* TO 'nova'@'localhost' IDENTIFIED BY 'nova';" - mysql -N -e "GRANT ALL PRIVILEGES ON nova.* TO 'nova'@'%' IDENTIFIED BY 'nova';" - mysql -N -e "GRANT ALL PRIVILEGES ON nova_cell0.* TO 'nova'@'localhost' IDENTIFIED BY 'nova';" - mysql -N -e "GRANT ALL PRIVILEGES ON nova_cell0.* TO 'nova'@'%' IDENTIFIED BY 'nova';" - - # Neutron - mysql -N -e "drop database neutron;" || true - mysql -N -e "create database neutron;" || true - mysql -N -e "GRANT ALL PRIVILEGES ON neutron.* TO 'neutron'@'localhost' IDENTIFIED BY 'neutron';" - mysql -N -e "GRANT ALL PRIVILEGES ON neutron.* TO 'neutron'@'%' IDENTIFIED BY 'neutron';" - ''; + ExecStart = "/root/os-setup/database-setup.sh"; }; }; From 2029a9ca0e5f5c2b8dd179ebc3968f5dc7fe7818 Mon Sep 17 00:00:00 2001 From: Paul Kroeher Date: Tue, 4 Aug 2026 09:18:05 +0200 Subject: [PATCH 04/71] nix: move keystone setup into a separate script Signed-off-by: Paul Kroeher On-behalf-of: SAP paul.kroeher@sap.com --- modules/controller/openstack-controller.nix | 62 ++++++++++++++------- 1 file changed, 43 insertions(+), 19 deletions(-) diff --git a/modules/controller/openstack-controller.nix b/modules/controller/openstack-controller.nix index 941e405..2ea42da 100644 --- a/modules/controller/openstack-controller.nix +++ b/modules/controller/openstack-controller.nix @@ -24,6 +24,7 @@ let OS_AUTH_URL = "http://controller:5000/v3"; OS_IDENTITY_API_VERSION = "3"; }; + databaseSetupScript = pkgs.writeShellScript "database-setup.sh" '' export PATH=${lib.makeBinPath [ pkgs.mariadb ]}:$PATH @@ -72,6 +73,42 @@ let mysql -N -e "GRANT ALL PRIVILEGES ON neutron.* TO 'neutron'@'localhost' IDENTIFIED BY 'neutron';" mysql -N -e "GRANT ALL PRIVILEGES ON neutron.* TO 'neutron'@'%' IDENTIFIED BY 'neutron';" ''; + + keystonePreStartScript = pkgs.writeShellScript "keystone-all-pre-start.sh" '' + export PATH=${ + lib.makeBinPath [ + keystone + pkgs.coreutils + ] + }:$PATH + + # Initialise the database + keystone-manage --config-file ${config.keystone.config} db_sync + # Set up the keystone's PKI infrastructure + keystone-manage --config-file ${config.keystone.config} fernet_setup --keystone-user keystone --keystone-group keystone + keystone-manage --config-file ${config.keystone.config} credential_setup --keystone-user keystone --keystone-group keystone + chown -R keystone:keystone /etc/keystone + chown -R keystone:keystone /var/log/keystone + ''; + + keystoneStartScript = pkgs.writeShellScript "keystone-all.sh" '' + export PATH=${ + lib.makeBinPath [ + keystone + pkgs.openstackclient + pkgs.util-linux + ] + }:$PATH + + exec runuser --user keystone --preserve-environment -- ${pkgs.runtimeShell} <<'EOF' + set -euxo pipefail + keystone-manage --config-file ${config.keystone.config} bootstrap \ + --bootstrap-password admin\ + --bootstrap-region-id RegionOne + openstack project create --domain default --description "Service Project" service + EOF + ''; + in { imports = [ @@ -101,9 +138,11 @@ in python-openstackclient ]; - system.activationScripts.database-setup.text = '' + system.activationScripts.openstack-setup-scripts.text = '' install -d -m 0700 /root/os-setup install -m 0700 ${databaseSetupScript} /root/os-setup/database-setup.sh + install -m 0700 ${keystonePreStartScript} /root/os-setup/keystone-all-pre-start.sh + install -m 0700 ${keystoneStartScript} /root/os-setup/keystone-all.sh ''; systemd.services.database-setup = lib.mkIf (!config.openstack.production_setup) { @@ -121,7 +160,7 @@ in }; }; - systemd.services.keystone-all = { + systemd.services.keystone-all = lib.mkIf (!config.openstack.production_setup) { description = "OpenStack Keystone Daemon"; after = [ "database-setup.service" ]; path = [ @@ -130,27 +169,12 @@ in ]; environment = adminEnv; wantedBy = [ "multi-user.target" ]; - preStart = '' - # Initialise the database - keystone-manage --config-file ${config.keystone.config} db_sync - # Set up the keystone's PKI infrastructure - keystone-manage --config-file ${config.keystone.config} fernet_setup --keystone-user keystone --keystone-group keystone - keystone-manage --config-file ${config.keystone.config} credential_setup --keystone-user keystone --keystone-group keystone - chown -R keystone:keystone /etc/keystone - chown -R keystone:keystone /var/log/keystone - ''; serviceConfig = { - PermissionsStartOnly = true; User = "keystone"; Group = "keystone"; Type = "oneshot"; - ExecStart = pkgs.writeShellScript "keystone-all.sh" '' - set -euxo pipefail - keystone-manage --config-file ${config.keystone.config} bootstrap \ - --bootstrap-password admin\ - --bootstrap-region-id RegionOne - openstack project create --domain default --description "Service Project" service - ''; + ExecStartPre = "+/root/os-setup/keystone-all-pre-start.sh"; + ExecStart = "+/root/os-setup/keystone-all.sh"; }; }; From 70c1dec50e0f45da586ad6c40b0ed87775d4d09e Mon Sep 17 00:00:00 2001 From: Paul Kroeher Date: Tue, 4 Aug 2026 09:21:36 +0200 Subject: [PATCH 05/71] nix: move glance setup into a separate script Signed-off-by: Paul Kroeher On-behalf-of: SAP paul.kroeher@sap.com --- modules/controller/openstack-controller.nix | 29 +++++++++++++++------ 1 file changed, 21 insertions(+), 8 deletions(-) diff --git a/modules/controller/openstack-controller.nix b/modules/controller/openstack-controller.nix index 2ea42da..b1010b7 100644 --- a/modules/controller/openstack-controller.nix +++ b/modules/controller/openstack-controller.nix @@ -109,6 +109,24 @@ let EOF ''; + glanceStartScript = pkgs.writeShellScript "glance.sh" '' + export PATH=${ + lib.makeBinPath [ + glance + pkgs.openstackclient + pkgs.util-linux + ] + }:$PATH + + exec runuser --user glance --preserve-environment -- ${pkgs.runtimeShell} <<'EOF' + set -euxo pipefail + openstack user create --domain default --password glance glance + openstack role add --project service --user glance admin + openstack role add --user glance --user-domain default --system all reader + glance-manage --config-file ${config.glance.config} db_sync + EOF + ''; + in { imports = [ @@ -143,6 +161,7 @@ in install -m 0700 ${databaseSetupScript} /root/os-setup/database-setup.sh install -m 0700 ${keystonePreStartScript} /root/os-setup/keystone-all-pre-start.sh install -m 0700 ${keystoneStartScript} /root/os-setup/keystone-all.sh + install -m 0700 ${glanceStartScript} /root/os-setup/glance.sh ''; systemd.services.database-setup = lib.mkIf (!config.openstack.production_setup) { @@ -178,7 +197,7 @@ in }; }; - systemd.services.glance = { + systemd.services.glance = lib.mkIf (!config.openstack.production_setup) { description = "OpenStack Glance setup"; after = [ "keystone-all.service" ]; wantedBy = [ "multi-user.target" ]; @@ -191,13 +210,7 @@ in Type = "oneshot"; User = "glance"; Group = "glance"; - ExecStart = pkgs.writeShellScript "glance.sh" '' - set -euxo pipefail - openstack user create --domain default --password glance glance - openstack role add --project service --user glance admin - openstack role add --user glance --user-domain default --system all reader - glance-manage --config-file ${config.glance.config} db_sync - ''; + ExecStart = "+/root/os-setup/glance.sh"; }; }; From c443595602195d832a9be38b68b726b97091b0a4 Mon Sep 17 00:00:00 2001 From: Paul Kroeher Date: Tue, 4 Aug 2026 09:53:44 +0200 Subject: [PATCH 06/71] nix: write default credentials into a separate script Signed-off-by: Paul Kroeher On-behalf-of: SAP paul.kroeher@sap.com --- modules/controller/openstack-controller.nix | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/modules/controller/openstack-controller.nix b/modules/controller/openstack-controller.nix index b1010b7..9d2b1d2 100644 --- a/modules/controller/openstack-controller.nix +++ b/modules/controller/openstack-controller.nix @@ -25,6 +25,12 @@ let OS_IDENTITY_API_VERSION = "3"; }; + adminEnvScript = pkgs.writeShellScript "openstack-admin-env" ( + lib.concatStringsSep "\n" ( + lib.mapAttrsToList (name: value: "export ${name}=${lib.escapeShellArg value}") adminEnv + ) + ); + databaseSetupScript = pkgs.writeShellScript "database-setup.sh" '' export PATH=${lib.makeBinPath [ pkgs.mariadb ]}:$PATH @@ -158,6 +164,7 @@ in system.activationScripts.openstack-setup-scripts.text = '' install -d -m 0700 /root/os-setup + install -m 0700 ${adminEnvScript} /root/os-setup/.env install -m 0700 ${databaseSetupScript} /root/os-setup/database-setup.sh install -m 0700 ${keystonePreStartScript} /root/os-setup/keystone-all-pre-start.sh install -m 0700 ${keystoneStartScript} /root/os-setup/keystone-all.sh From c542c378446d2e94a96b6bd4538d1774f72d1d2d Mon Sep 17 00:00:00 2001 From: Paul Kroeher Date: Tue, 4 Aug 2026 09:57:40 +0200 Subject: [PATCH 07/71] nix: move cinder setup into a separate script Signed-off-by: Paul Kroeher On-behalf-of: SAP paul.kroeher@sap.com --- modules/controller/openstack-controller.nix | 29 +++++++++++++++------ 1 file changed, 21 insertions(+), 8 deletions(-) diff --git a/modules/controller/openstack-controller.nix b/modules/controller/openstack-controller.nix index 9d2b1d2..5c80368 100644 --- a/modules/controller/openstack-controller.nix +++ b/modules/controller/openstack-controller.nix @@ -133,6 +133,24 @@ let EOF ''; + cinderStartScript = pkgs.writeShellScript "cinder.sh" '' + export PATH=${ + lib.makeBinPath [ + cinder + pkgs.openstackclient + pkgs.util-linux + ] + }:$PATH + + exec runuser --user cinder --preserve-environment -- ${pkgs.runtimeShell} <<'EOF' + set -euxo pipefail + openstack user create --domain default --password cinder cinder || true + openstack role add --project service --user cinder admin || true + openstack role add --user cinder --user-domain default --system all reader || true + cinder-manage --config-file ${config.cinder.config} db sync + EOF + ''; + in { imports = [ @@ -169,6 +187,7 @@ in install -m 0700 ${keystonePreStartScript} /root/os-setup/keystone-all-pre-start.sh install -m 0700 ${keystoneStartScript} /root/os-setup/keystone-all.sh install -m 0700 ${glanceStartScript} /root/os-setup/glance.sh + install -m 0700 ${cinderStartScript} /root/os-setup/cinder.sh ''; systemd.services.database-setup = lib.mkIf (!config.openstack.production_setup) { @@ -221,7 +240,7 @@ in }; }; - systemd.services.cinder = { + systemd.services.cinder = lib.mkIf (!config.openstack.production_setup) { description = "OpenStack Cinder setup"; after = [ "keystone-all.service" ]; wantedBy = [ "multi-user.target" ]; @@ -234,13 +253,7 @@ in Type = "oneshot"; User = "cinder"; Group = "cinder"; - ExecStart = pkgs.writeShellScript "cinder.sh" '' - set -euxo pipefail - openstack user create --domain default --password cinder cinder || true - openstack role add --project service --user cinder admin || true - openstack role add --user cinder --user-domain default --system all reader || true - cinder-manage --config-file ${config.cinder.config} db sync - ''; + ExecStart = "+/root/os-setup/cinder.sh"; }; }; From 9f885eb15a3d5c90413fe80ba8248bda07c06ee0 Mon Sep 17 00:00:00 2001 From: Paul Kroeher Date: Tue, 4 Aug 2026 09:59:22 +0200 Subject: [PATCH 08/71] nix: move placement setup into a separate script Signed-off-by: Paul Kroeher On-behalf-of: SAP paul.kroeher@sap.com --- modules/controller/openstack-controller.nix | 27 +++++++++++++++------ 1 file changed, 20 insertions(+), 7 deletions(-) diff --git a/modules/controller/openstack-controller.nix b/modules/controller/openstack-controller.nix index 5c80368..236608f 100644 --- a/modules/controller/openstack-controller.nix +++ b/modules/controller/openstack-controller.nix @@ -151,6 +151,23 @@ let EOF ''; + placementStartScript = pkgs.writeShellScript "placement.sh" '' + export PATH=${ + lib.makeBinPath [ + placement + pkgs.openstackclient + pkgs.util-linux + ] + }:$PATH + + exec runuser --user placement --preserve-environment -- ${pkgs.runtimeShell} <<'EOF' + set -euxo pipefail + openstack user create --domain default --password placement placement + openstack role add --project service --user placement admin + placement-manage --config-file ${config.placement.config} db sync + EOF + ''; + in { imports = [ @@ -188,6 +205,7 @@ in install -m 0700 ${keystoneStartScript} /root/os-setup/keystone-all.sh install -m 0700 ${glanceStartScript} /root/os-setup/glance.sh install -m 0700 ${cinderStartScript} /root/os-setup/cinder.sh + install -m 0700 ${placementStartScript} /root/os-setup/placement.sh ''; systemd.services.database-setup = lib.mkIf (!config.openstack.production_setup) { @@ -260,7 +278,7 @@ in # Placement service can be tested by executing # curl http://controller:8778 # and receive some json with version info as result. - systemd.services.placement = { + systemd.services.placement = lib.mkIf (!config.openstack.production_setup) { description = "OpenStack Placement setup"; after = [ "glance.service" ]; requiredBy = [ "multi-user.target" ]; @@ -273,12 +291,7 @@ in Type = "oneshot"; User = "placement"; Group = "placement"; - ExecStart = pkgs.writeShellScript "placement.sh" '' - set -euxo pipefail - openstack user create --domain default --password placement placement - openstack role add --project service --user placement admin - placement-manage --config-file ${config.placement.config} db sync - ''; + ExecStart = "+/root/os-setup/placement.sh"; }; }; From c484c7fefaf0bd88d03b9daae556c095f6e7f439 Mon Sep 17 00:00:00 2001 From: Paul Kroeher Date: Tue, 4 Aug 2026 10:01:11 +0200 Subject: [PATCH 09/71] nix: move nova setup into a separate script Signed-off-by: Paul Kroeher On-behalf-of: SAP paul.kroeher@sap.com --- modules/controller/openstack-controller.nix | 33 ++++++++++++++------- 1 file changed, 23 insertions(+), 10 deletions(-) diff --git a/modules/controller/openstack-controller.nix b/modules/controller/openstack-controller.nix index 236608f..9714c1b 100644 --- a/modules/controller/openstack-controller.nix +++ b/modules/controller/openstack-controller.nix @@ -168,6 +168,26 @@ let EOF ''; + novaStartScript = pkgs.writeShellScript "nova.sh" '' + export PATH=${ + lib.makeBinPath [ + nova + pkgs.openstackclient + pkgs.util-linux + ] + }:$PATH + + exec runuser --user nova --preserve-environment -- ${pkgs.runtimeShell} <<'EOF' + set -euxo pipefail + openstack user create --domain default --password nova nova + openstack role add --project service --user nova admin + nova-manage --config-file ${config.nova.config} api_db sync + nova-manage --config-file ${config.nova.config} cell_v2 map_cell0 + nova-manage --config-file ${config.nova.config} cell_v2 create_cell --name=cell1 --verbose + nova-manage --config-file ${config.nova.config} db sync + EOF + ''; + in { imports = [ @@ -206,6 +226,7 @@ in install -m 0700 ${glanceStartScript} /root/os-setup/glance.sh install -m 0700 ${cinderStartScript} /root/os-setup/cinder.sh install -m 0700 ${placementStartScript} /root/os-setup/placement.sh + install -m 0700 ${novaStartScript} /root/os-setup/nova.sh ''; systemd.services.database-setup = lib.mkIf (!config.openstack.production_setup) { @@ -295,7 +316,7 @@ in }; }; - systemd.services.nova = { + systemd.services.nova = lib.mkIf (!config.openstack.production_setup) { description = "OpenStack Nova setup"; after = [ "neutron.service" ]; wantedBy = [ "multi-user.target" ]; @@ -308,15 +329,7 @@ in Type = "oneshot"; User = "nova"; Group = "nova"; - ExecStart = pkgs.writeShellScript "nova.sh" '' - set -euxo pipefail - openstack user create --domain default --password nova nova - openstack role add --project service --user nova admin - nova-manage --config-file ${config.nova.config} api_db sync - nova-manage --config-file ${config.nova.config} cell_v2 map_cell0 - nova-manage --config-file ${config.nova.config} cell_v2 create_cell --name=cell1 --verbose - nova-manage --config-file ${config.nova.config} db sync - ''; + ExecStart = "+/root/os-setup/nova.sh"; }; }; From 2f4006a5e79606cd6e6b3febe068f68695362391 Mon Sep 17 00:00:00 2001 From: Paul Kroeher Date: Tue, 4 Aug 2026 10:03:01 +0200 Subject: [PATCH 10/71] nix: move neutron setup into a separate script Signed-off-by: Paul Kroeher On-behalf-of: SAP paul.kroeher@sap.com --- modules/controller/openstack-controller.nix | 25 ++++++++++++++++----- 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/modules/controller/openstack-controller.nix b/modules/controller/openstack-controller.nix index 9714c1b..42f3123 100644 --- a/modules/controller/openstack-controller.nix +++ b/modules/controller/openstack-controller.nix @@ -188,6 +188,23 @@ let EOF ''; + neutronStartScript = pkgs.writeShellScript "neutron.sh" '' + export PATH=${ + lib.makeBinPath [ + neutron + pkgs.openstackclient + pkgs.util-linux + ] + }:$PATH + + exec runuser --user neutron --preserve-environment -- ${pkgs.runtimeShell} <<'EOF' + set -euxo pipefail + openstack user create --domain default --password neutron neutron + openstack role add --project service --user neutron admin + neutron-db-manage --config-file ${config.neutron.config} --config-file ${config.neutron.ml2Config} upgrade head + EOF + ''; + in { imports = [ @@ -227,6 +244,7 @@ in install -m 0700 ${cinderStartScript} /root/os-setup/cinder.sh install -m 0700 ${placementStartScript} /root/os-setup/placement.sh install -m 0700 ${novaStartScript} /root/os-setup/nova.sh + install -m 0700 ${neutronStartScript} /root/os-setup/neutron.sh ''; systemd.services.database-setup = lib.mkIf (!config.openstack.production_setup) { @@ -346,12 +364,7 @@ in Type = "oneshot"; User = "neutron"; Group = "neutron"; - ExecStart = pkgs.writeShellScript "neutron.sh" '' - set -euxo pipefail - openstack user create --domain default --password neutron neutron - openstack role add --project service --user neutron admin - neutron-db-manage --config-file ${config.neutron.config} --config-file ${config.neutron.ml2Config} upgrade head - ''; + ExecStart = "+/root/os-setup/neutron.sh"; }; }; }; From ac7e227c7a0cc4a63d6b0121a3a49bd56612cc8d Mon Sep 17 00:00:00 2001 From: Paul Kroeher Date: Tue, 4 Aug 2026 10:06:18 +0200 Subject: [PATCH 11/71] nix: rename generated scripts Signed-off-by: Paul Kroeher On-behalf-of: SAP paul.kroeher@sap.com --- modules/controller/openstack-controller.nix | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/modules/controller/openstack-controller.nix b/modules/controller/openstack-controller.nix index 42f3123..9d5c03b 100644 --- a/modules/controller/openstack-controller.nix +++ b/modules/controller/openstack-controller.nix @@ -237,14 +237,14 @@ in system.activationScripts.openstack-setup-scripts.text = '' install -d -m 0700 /root/os-setup install -m 0700 ${adminEnvScript} /root/os-setup/.env - install -m 0700 ${databaseSetupScript} /root/os-setup/database-setup.sh - install -m 0700 ${keystonePreStartScript} /root/os-setup/keystone-all-pre-start.sh - install -m 0700 ${keystoneStartScript} /root/os-setup/keystone-all.sh - install -m 0700 ${glanceStartScript} /root/os-setup/glance.sh - install -m 0700 ${cinderStartScript} /root/os-setup/cinder.sh - install -m 0700 ${placementStartScript} /root/os-setup/placement.sh - install -m 0700 ${novaStartScript} /root/os-setup/nova.sh - install -m 0700 ${neutronStartScript} /root/os-setup/neutron.sh + install -m 0700 ${databaseSetupScript} /root/os-setup/000-database-setup.sh + install -m 0700 ${keystonePreStartScript} /root/os-setup/001-keystone-all-pre-start.sh + install -m 0700 ${keystoneStartScript} /root/os-setup/002-keystone-all.sh + install -m 0700 ${glanceStartScript} /root/os-setup/003-glance.sh + install -m 0700 ${cinderStartScript} /root/os-setup/003-cinder.sh + install -m 0700 ${placementStartScript} /root/os-setup/004-placement.sh + install -m 0700 ${novaStartScript} /root/os-setup/006-nova.sh + install -m 0700 ${neutronStartScript} /root/os-setup/005-neutron.sh ''; systemd.services.database-setup = lib.mkIf (!config.openstack.production_setup) { From 2b677f421c8a9c0efaca70468b1b7e157ca49d12 Mon Sep 17 00:00:00 2001 From: Paul Kroeher Date: Tue, 4 Aug 2026 15:57:32 +0200 Subject: [PATCH 12/71] nix: rename generated scripts Signed-off-by: Paul Kroeher On-behalf-of: SAP paul.kroeher@sap.com --- modules/controller/openstack-controller.nix | 34 +++++++++++++++------ 1 file changed, 24 insertions(+), 10 deletions(-) diff --git a/modules/controller/openstack-controller.nix b/modules/controller/openstack-controller.nix index 9d5c03b..05bcfa0 100644 --- a/modules/controller/openstack-controller.nix +++ b/modules/controller/openstack-controller.nix @@ -78,6 +78,8 @@ let mysql -N -e "create database neutron;" || true mysql -N -e "GRANT ALL PRIVILEGES ON neutron.* TO 'neutron'@'localhost' IDENTIFIED BY 'neutron';" mysql -N -e "GRANT ALL PRIVILEGES ON neutron.* TO 'neutron'@'%' IDENTIFIED BY 'neutron';" + + mysql -N -e "FLUSH PRIVILEGES;" ''; keystonePreStartScript = pkgs.writeShellScript "keystone-all-pre-start.sh" '' @@ -106,12 +108,14 @@ let ] }:$PATH + source /root/os-setup/.env + exec runuser --user keystone --preserve-environment -- ${pkgs.runtimeShell} <<'EOF' set -euxo pipefail keystone-manage --config-file ${config.keystone.config} bootstrap \ - --bootstrap-password admin\ + --bootstrap-password admin \ --bootstrap-region-id RegionOne - openstack project create --domain default --description "Service Project" service + openstack project create --domain default --description "Service Project" service EOF ''; @@ -124,6 +128,8 @@ let ] }:$PATH + source /root/os-setup/.env + exec runuser --user glance --preserve-environment -- ${pkgs.runtimeShell} <<'EOF' set -euxo pipefail openstack user create --domain default --password glance glance @@ -142,6 +148,8 @@ let ] }:$PATH + source /root/os-setup/.env + exec runuser --user cinder --preserve-environment -- ${pkgs.runtimeShell} <<'EOF' set -euxo pipefail openstack user create --domain default --password cinder cinder || true @@ -160,6 +168,8 @@ let ] }:$PATH + source /root/os-setup/.env + exec runuser --user placement --preserve-environment -- ${pkgs.runtimeShell} <<'EOF' set -euxo pipefail openstack user create --domain default --password placement placement @@ -177,6 +187,8 @@ let ] }:$PATH + source /root/os-setup/.env + exec runuser --user nova --preserve-environment -- ${pkgs.runtimeShell} <<'EOF' set -euxo pipefail openstack user create --domain default --password nova nova @@ -197,6 +209,8 @@ let ] }:$PATH + source /root/os-setup/.env + exec runuser --user neutron --preserve-environment -- ${pkgs.runtimeShell} <<'EOF' set -euxo pipefail openstack user create --domain default --password neutron neutron @@ -258,7 +272,7 @@ in path = [ pkgs.mariadb ]; serviceConfig = { Type = "oneshot"; - ExecStart = "/root/os-setup/database-setup.sh"; + ExecStart = "/root/os-setup/000-database-setup.sh"; }; }; @@ -275,8 +289,8 @@ in User = "keystone"; Group = "keystone"; Type = "oneshot"; - ExecStartPre = "+/root/os-setup/keystone-all-pre-start.sh"; - ExecStart = "+/root/os-setup/keystone-all.sh"; + ExecStartPre = "+/root/os-setup/001-keystone-all-pre-start.sh"; + ExecStart = "+/root/os-setup/002-keystone-all.sh"; }; }; @@ -293,7 +307,7 @@ in Type = "oneshot"; User = "glance"; Group = "glance"; - ExecStart = "+/root/os-setup/glance.sh"; + ExecStart = "+/root/os-setup/003-glance.sh"; }; }; @@ -310,7 +324,7 @@ in Type = "oneshot"; User = "cinder"; Group = "cinder"; - ExecStart = "+/root/os-setup/cinder.sh"; + ExecStart = "+/root/os-setup/003-cinder.sh"; }; }; @@ -330,7 +344,7 @@ in Type = "oneshot"; User = "placement"; Group = "placement"; - ExecStart = "+/root/os-setup/placement.sh"; + ExecStart = "+/root/os-setup/004-placement.sh"; }; }; @@ -347,7 +361,7 @@ in Type = "oneshot"; User = "nova"; Group = "nova"; - ExecStart = "+/root/os-setup/nova.sh"; + ExecStart = "+/root/os-setup/006-nova.sh"; }; }; @@ -364,7 +378,7 @@ in Type = "oneshot"; User = "neutron"; Group = "neutron"; - ExecStart = "+/root/os-setup/neutron.sh"; + ExecStart = "+/root/os-setup/005-neutron.sh"; }; }; }; From f8ff8279f85c1c4d99926481889c91489452530e Mon Sep 17 00:00:00 2001 From: Paul Kroeher Date: Wed, 5 Aug 2026 11:54:18 +0200 Subject: [PATCH 13/71] nix: split database script into create and cleanup Signed-off-by: Paul Kroeher On-behalf-of: SAP paul.kroeher@sap.com --- modules/controller/openstack-controller.nix | 76 ++++++++++++--------- 1 file changed, 42 insertions(+), 34 deletions(-) diff --git a/modules/controller/openstack-controller.nix b/modules/controller/openstack-controller.nix index 05bcfa0..2fbbca3 100644 --- a/modules/controller/openstack-controller.nix +++ b/modules/controller/openstack-controller.nix @@ -31,53 +31,60 @@ let ) ); + databaseCleanupScript = pkgs.writeShellScript "database-cleanup.sh" '' + export PATH=${lib.makeBinPath [ pkgs.mariadb ]}:$PATH + mysql -N -e "drop database keystone;" || true + mysql -N -e "drop database glance;" || true + mysql -N -e "drop database cinder;" || true + mysql -N -e "drop database placement;" || true + mysql -N -e "drop database nova_api;" || true + mysql -N -e "drop database nova;" || true + mysql -N -e "drop database nova_cell0;" || true + mysql -N -e "drop database neutron;" || true + ''; + databaseSetupScript = pkgs.writeShellScript "database-setup.sh" '' export PATH=${lib.makeBinPath [ pkgs.mariadb ]}:$PATH # Keystone - mysql -N -e "drop database keystone;" || true - mysql -N -e "create database keystone;" || true - mysql -N -e "GRANT ALL PRIVILEGES ON keystone.* TO 'keystone'@'localhost' IDENTIFIED BY 'keystone';" - mysql -N -e "GRANT ALL PRIVILEGES ON keystone.* TO 'keystone'@'%' IDENTIFIED BY 'keystone';" + mysql -N -e "CREATE DATABASE IF NOT EXISTS keystone;" + mysql -N -e "CREATE USER IF NOT EXISTS 'keystone'@'%' IDENTIFIED BY 'keystone';" + mysql -N -e "ALTER USER 'keystone'@'%' IDENTIFIED BY 'keystone';" + mysql -N -e "GRANT ALL PRIVILEGES ON keystone.* TO 'keystone'@'%';" # Glance - mysql -N -e "drop database glance;" || true - mysql -N -e "create database glance;" || true - mysql -N -e "GRANT ALL PRIVILEGES ON glance.* TO 'glance'@'localhost' IDENTIFIED BY 'glance';" - mysql -N -e "GRANT ALL PRIVILEGES ON glance.* TO 'glance'@'%' IDENTIFIED BY 'glance';" + mysql -N -e "CREATE DATABASE IF NOT EXISTS glance;" + mysql -N -e "CREATE USER IF NOT EXISTS 'glance'@'%' IDENTIFIED BY 'glance';" + mysql -N -e "ALTER USER 'glance'@'%' IDENTIFIED BY 'glance';" + mysql -N -e "GRANT ALL PRIVILEGES ON glance.* TO 'glance'@'%';" # Cinder - mysql -N -e "drop database cinder;" || true - mysql -N -e "create database cinder;" || true - mysql -N -e "GRANT ALL PRIVILEGES ON cinder.* TO 'cinder'@'localhost' IDENTIFIED BY 'cinder';" - mysql -N -e "GRANT ALL PRIVILEGES ON cinder.* TO 'cinder'@'%' IDENTIFIED BY 'cinder';" + mysql -N -e "CREATE DATABASE IF NOT EXISTS cinder;" + mysql -N -e "CREATE USER IF NOT EXISTS 'cinder'@'%' IDENTIFIED BY 'cinder';" + mysql -N -e "ALTER USER 'cinder'@'%' IDENTIFIED BY 'cinder';" + mysql -N -e "GRANT ALL PRIVILEGES ON cinder.* TO 'cinder'@'%';" # Placement - mysql -N -e "drop database placement;" || true - mysql -N -e "create database placement;" || true - mysql -N -e "GRANT ALL PRIVILEGES ON placement.* TO 'placement'@'localhost' IDENTIFIED BY 'placement';" - mysql -N -e "GRANT ALL PRIVILEGES ON placement.* TO 'placement'@'%' IDENTIFIED BY 'placement';" + mysql -N -e "CREATE DATABASE IF NOT EXISTS placement;" + mysql -N -e "CREATE USER IF NOT EXISTS 'placement'@'%' IDENTIFIED BY 'placement';" + mysql -N -e "ALTER USER 'placement'@'%' IDENTIFIED BY 'placement';" + mysql -N -e "GRANT ALL PRIVILEGES ON placement.* TO 'placement'@'%';" # Nova - mysql -N -e "drop database nova_api;" || true - mysql -N -e "drop database nova;" || true - mysql -N -e "drop database nova_cell0;" || true - mysql -N -e "create database nova_api;" || true - mysql -N -e "create database nova;" || true - mysql -N -e "create database nova_cell0;" || true - - mysql -N -e "GRANT ALL PRIVILEGES ON nova_api.* TO 'nova'@'localhost' IDENTIFIED BY 'nova';" - mysql -N -e "GRANT ALL PRIVILEGES ON nova_api.* TO 'nova'@'%' IDENTIFIED BY 'nova';" - mysql -N -e "GRANT ALL PRIVILEGES ON nova.* TO 'nova'@'localhost' IDENTIFIED BY 'nova';" - mysql -N -e "GRANT ALL PRIVILEGES ON nova.* TO 'nova'@'%' IDENTIFIED BY 'nova';" - mysql -N -e "GRANT ALL PRIVILEGES ON nova_cell0.* TO 'nova'@'localhost' IDENTIFIED BY 'nova';" - mysql -N -e "GRANT ALL PRIVILEGES ON nova_cell0.* TO 'nova'@'%' IDENTIFIED BY 'nova';" + mysql -N -e "CREATE DATABASE IF NOT EXISTS nova_api;" + mysql -N -e "CREATE DATABASE IF NOT EXISTS nova;" + mysql -N -e "CREATE DATABASE IF NOT EXISTS nova_cell0;" + mysql -N -e "CREATE USER IF NOT EXISTS 'nova'@'%' IDENTIFIED BY 'nova';" + mysql -N -e "ALTER USER 'nova'@'%' IDENTIFIED BY 'nova';" + mysql -N -e "GRANT ALL PRIVILEGES ON nova_api.* TO 'nova'@'%';" + mysql -N -e "GRANT ALL PRIVILEGES ON nova.* TO 'nova'@'%';" + mysql -N -e "GRANT ALL PRIVILEGES ON nova_cell0.* TO 'nova'@'%';" # Neutron - mysql -N -e "drop database neutron;" || true - mysql -N -e "create database neutron;" || true - mysql -N -e "GRANT ALL PRIVILEGES ON neutron.* TO 'neutron'@'localhost' IDENTIFIED BY 'neutron';" - mysql -N -e "GRANT ALL PRIVILEGES ON neutron.* TO 'neutron'@'%' IDENTIFIED BY 'neutron';" + mysql -N -e "CREATE DATABASE IF NOT EXISTS neutron;" + mysql -N -e "CREATE USER IF NOT EXISTS 'neutron'@'%' IDENTIFIED BY 'neutron';" + mysql -N -e "ALTER USER 'neutron'@'%' IDENTIFIED BY 'neutron';" + mysql -N -e "GRANT ALL PRIVILEGES ON neutron.* TO 'neutron'@'%';" mysql -N -e "FLUSH PRIVILEGES;" ''; @@ -251,6 +258,7 @@ in system.activationScripts.openstack-setup-scripts.text = '' install -d -m 0700 /root/os-setup install -m 0700 ${adminEnvScript} /root/os-setup/.env + install -m 0700 ${databaseCleanupScript} /root/os-setup/000-database-cleanup.sh install -m 0700 ${databaseSetupScript} /root/os-setup/000-database-setup.sh install -m 0700 ${keystonePreStartScript} /root/os-setup/001-keystone-all-pre-start.sh install -m 0700 ${keystoneStartScript} /root/os-setup/002-keystone-all.sh @@ -365,7 +373,7 @@ in }; }; - systemd.services.neutron = { + systemd.services.neutron = lib.mkIf (!config.openstack.production_setup) { description = "OpenStack Neutron setup"; after = [ "placement.service" ]; wantedBy = [ "multi-user.target" ]; From 47dac0b23412dc86582045409843dd56e76ed160 Mon Sep 17 00:00:00 2001 From: Paul Kroeher Date: Wed, 5 Aug 2026 15:07:25 +0200 Subject: [PATCH 14/71] nix: disable services but create all configurations and systemd units Signed-off-by: Paul Kroeher On-behalf-of: SAP paul.kroeher@sap.com --- modules/controller/cinder.nix | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/modules/controller/cinder.nix b/modules/controller/cinder.nix index 55b31a7..0dd4f2b 100644 --- a/modules/controller/cinder.nix +++ b/modules/controller/cinder.nix @@ -57,7 +57,7 @@ in ''; }; }; - config = mkIf cfg.enable { + config = { users.extraUsers.cinder = { group = "cinder"; @@ -121,6 +121,7 @@ in .cinder-wsgi-wrapped --port 8776 ''; }; + enable = cfg.enable; }; systemd.services.cinder-scheduler = { @@ -140,6 +141,7 @@ in .cinder-scheduler-wrapped ''; }; + enable = cfg.enable; }; }; } From 46645a40dd6a8e358cd6b4f7aa33a5333afc83f7 Mon Sep 17 00:00:00 2001 From: Paul Kroeher Date: Wed, 5 Aug 2026 15:07:36 +0200 Subject: [PATCH 15/71] nix: disable services but create all configurations and systemd units Signed-off-by: Paul Kroeher On-behalf-of: SAP paul.kroeher@sap.com --- modules/controller/neutron.nix | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/modules/controller/neutron.nix b/modules/controller/neutron.nix index 448d38f..0e02d48 100644 --- a/modules/controller/neutron.nix +++ b/modules/controller/neutron.nix @@ -172,7 +172,7 @@ in ''; }; }; - config = mkIf cfg.enable { + config = { users.extraUsers.neutron = { group = "neutron"; @@ -252,6 +252,7 @@ in serviceConfig = { ExecStart = "${neutron}/bin/neutron-metadata-agent --config-file=${cfg.config}"; }; + enable = cfg.enable; }; virtualisation.vswitch = { @@ -291,6 +292,7 @@ in ${neutron}/bin/neutron-openvswitch-agent --config-file=${cfg.config} --config-file=${cfg.openvswitchConfig} ''; }; + enable = cfg.enable; }; systemd.services.neutron-server = { @@ -321,6 +323,7 @@ in LimitNOFILE = 65535; TimeoutStopSec = 15; }; + enable = cfg.enable; }; systemd.services.neutron-dhcp-agent = { @@ -352,6 +355,7 @@ in LimitNOFILE = 65535; TimeoutStopSec = 15; }; + enable = cfg.enable; }; }; From 17913811e0159c7c2a601c374622d0ad687568ed Mon Sep 17 00:00:00 2001 From: Paul Kroeher Date: Wed, 5 Aug 2026 15:14:38 +0200 Subject: [PATCH 16/71] nix: disable services but create all configurations and systemd units Signed-off-by: Paul Kroeher On-behalf-of: SAP paul.kroeher@sap.com --- modules/controller/glance.nix | 1 + modules/controller/nova.nix | 8 +++++++- modules/controller/placement.nix | 3 ++- 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/modules/controller/glance.nix b/modules/controller/glance.nix index ffabe08..34a8212 100644 --- a/modules/controller/glance.nix +++ b/modules/controller/glance.nix @@ -131,6 +131,7 @@ in glance-api --config-file=${cfg.config} --config-file=/etc/glance/glance-api-paste.ini ''; }; + enable = cfg.enable; }; services.uwsgi = { diff --git a/modules/controller/nova.nix b/modules/controller/nova.nix index 1ea834b..12e6bda 100644 --- a/modules/controller/nova.nix +++ b/modules/controller/nova.nix @@ -108,7 +108,7 @@ in ''; }; }; - config = mkIf cfg.enable { + config = { users.extraUsers.nova = { group = "nova"; @@ -184,6 +184,7 @@ in nova-api --config-file ${cfg.config} ''; }; + enable = cfg.enable; }; systemd.services.nova-conductor = { @@ -216,6 +217,7 @@ in LimitNOFILE = 65535; TimeoutStopSec = 15; }; + enable = cfg.enable; }; systemd.services.nova-scheduler = { @@ -248,6 +250,7 @@ in LimitNOFILE = 65535; TimeoutStopSec = 15; }; + enable = cfg.enable; }; systemd.services.nova-host-discovery = { @@ -269,6 +272,7 @@ in nova-manage cell_v2 discover_hosts --verbose ''; }; + enable = cfg.enable; }; systemd.services.nova-novncproxy = { @@ -301,6 +305,7 @@ in LimitNOFILE = 65535; TimeoutStopSec = 15; }; + enable = cfg.enable; }; systemd.services.nova-serialproxy = { @@ -326,6 +331,7 @@ in LimitNOFILE = 65535; TimeoutStopSec = 15; }; + enable = cfg.enable; }; }; } diff --git a/modules/controller/placement.nix b/modules/controller/placement.nix index 98be12c..d4ed670 100644 --- a/modules/controller/placement.nix +++ b/modules/controller/placement.nix @@ -41,7 +41,7 @@ in ''; }; }; - config = mkIf cfg.enable { + config = { users.extraUsers.placement = { group = "placement"; @@ -84,6 +84,7 @@ in placement-api --port 8778 ''; }; + enable = cfg.enable; }; }; } From 6fbfa100863303da5ed70c82898e006645256600 Mon Sep 17 00:00:00 2001 From: Paul Kroeher Date: Thu, 6 Aug 2026 09:18:55 +0200 Subject: [PATCH 17/71] nix: rename mysql command and stop services before setup its database Signed-off-by: Paul Kroeher On-behalf-of: SAP paul.kroeher@sap.com --- modules/controller/openstack-controller.nix | 111 +++++++++++++------- 1 file changed, 74 insertions(+), 37 deletions(-) diff --git a/modules/controller/openstack-controller.nix b/modules/controller/openstack-controller.nix index 2fbbca3..5070ff4 100644 --- a/modules/controller/openstack-controller.nix +++ b/modules/controller/openstack-controller.nix @@ -33,60 +33,60 @@ let databaseCleanupScript = pkgs.writeShellScript "database-cleanup.sh" '' export PATH=${lib.makeBinPath [ pkgs.mariadb ]}:$PATH - mysql -N -e "drop database keystone;" || true - mysql -N -e "drop database glance;" || true - mysql -N -e "drop database cinder;" || true - mysql -N -e "drop database placement;" || true - mysql -N -e "drop database nova_api;" || true - mysql -N -e "drop database nova;" || true - mysql -N -e "drop database nova_cell0;" || true - mysql -N -e "drop database neutron;" || true + mariadb -N -e "drop database keystone;" || true + mariadb -N -e "drop database glance;" || true + mariadb -N -e "drop database cinder;" || true + mariadb -N -e "drop database placement;" || true + mariadb -N -e "drop database nova_api;" || true + mariadb -N -e "drop database nova;" || true + mariadb -N -e "drop database nova_cell0;" || true + mariadb -N -e "drop database neutron;" || true ''; databaseSetupScript = pkgs.writeShellScript "database-setup.sh" '' export PATH=${lib.makeBinPath [ pkgs.mariadb ]}:$PATH # Keystone - mysql -N -e "CREATE DATABASE IF NOT EXISTS keystone;" - mysql -N -e "CREATE USER IF NOT EXISTS 'keystone'@'%' IDENTIFIED BY 'keystone';" - mysql -N -e "ALTER USER 'keystone'@'%' IDENTIFIED BY 'keystone';" - mysql -N -e "GRANT ALL PRIVILEGES ON keystone.* TO 'keystone'@'%';" + mariadb -N -e "CREATE DATABASE IF NOT EXISTS keystone;" + mariadb -N -e "CREATE USER IF NOT EXISTS 'keystone'@'%' IDENTIFIED BY 'keystone';" + mariadb -N -e "ALTER USER 'keystone'@'%' IDENTIFIED BY 'keystone';" + mariadb -N -e "GRANT ALL PRIVILEGES ON keystone.* TO 'keystone'@'%';" # Glance - mysql -N -e "CREATE DATABASE IF NOT EXISTS glance;" - mysql -N -e "CREATE USER IF NOT EXISTS 'glance'@'%' IDENTIFIED BY 'glance';" - mysql -N -e "ALTER USER 'glance'@'%' IDENTIFIED BY 'glance';" - mysql -N -e "GRANT ALL PRIVILEGES ON glance.* TO 'glance'@'%';" + mariadb -N -e "CREATE DATABASE IF NOT EXISTS glance;" + mariadb -N -e "CREATE USER IF NOT EXISTS 'glance'@'%' IDENTIFIED BY 'glance';" + mariadb -N -e "ALTER USER 'glance'@'%' IDENTIFIED BY 'glance';" + mariadb -N -e "GRANT ALL PRIVILEGES ON glance.* TO 'glance'@'%';" # Cinder - mysql -N -e "CREATE DATABASE IF NOT EXISTS cinder;" - mysql -N -e "CREATE USER IF NOT EXISTS 'cinder'@'%' IDENTIFIED BY 'cinder';" - mysql -N -e "ALTER USER 'cinder'@'%' IDENTIFIED BY 'cinder';" - mysql -N -e "GRANT ALL PRIVILEGES ON cinder.* TO 'cinder'@'%';" + mariadb -N -e "CREATE DATABASE IF NOT EXISTS cinder;" + mariadb -N -e "CREATE USER IF NOT EXISTS 'cinder'@'%' IDENTIFIED BY 'cinder';" + mariadb -N -e "ALTER USER 'cinder'@'%' IDENTIFIED BY 'cinder';" + mariadb -N -e "GRANT ALL PRIVILEGES ON cinder.* TO 'cinder'@'%';" # Placement - mysql -N -e "CREATE DATABASE IF NOT EXISTS placement;" - mysql -N -e "CREATE USER IF NOT EXISTS 'placement'@'%' IDENTIFIED BY 'placement';" - mysql -N -e "ALTER USER 'placement'@'%' IDENTIFIED BY 'placement';" - mysql -N -e "GRANT ALL PRIVILEGES ON placement.* TO 'placement'@'%';" + mariadb -N -e "CREATE DATABASE IF NOT EXISTS placement;" + mariadb -N -e "CREATE USER IF NOT EXISTS 'placement'@'%' IDENTIFIED BY 'placement';" + mariadb -N -e "ALTER USER 'placement'@'%' IDENTIFIED BY 'placement';" + mariadb -N -e "GRANT ALL PRIVILEGES ON placement.* TO 'placement'@'%';" # Nova - mysql -N -e "CREATE DATABASE IF NOT EXISTS nova_api;" - mysql -N -e "CREATE DATABASE IF NOT EXISTS nova;" - mysql -N -e "CREATE DATABASE IF NOT EXISTS nova_cell0;" - mysql -N -e "CREATE USER IF NOT EXISTS 'nova'@'%' IDENTIFIED BY 'nova';" - mysql -N -e "ALTER USER 'nova'@'%' IDENTIFIED BY 'nova';" - mysql -N -e "GRANT ALL PRIVILEGES ON nova_api.* TO 'nova'@'%';" - mysql -N -e "GRANT ALL PRIVILEGES ON nova.* TO 'nova'@'%';" - mysql -N -e "GRANT ALL PRIVILEGES ON nova_cell0.* TO 'nova'@'%';" + mariadb -N -e "CREATE DATABASE IF NOT EXISTS nova_api;" + mariadb -N -e "CREATE DATABASE IF NOT EXISTS nova;" + mariadb -N -e "CREATE DATABASE IF NOT EXISTS nova_cell0;" + mariadb -N -e "CREATE USER IF NOT EXISTS 'nova'@'%' IDENTIFIED BY 'nova';" + mariadb -N -e "ALTER USER 'nova'@'%' IDENTIFIED BY 'nova';" + mariadb -N -e "GRANT ALL PRIVILEGES ON nova_api.* TO 'nova'@'%';" + mariadb -N -e "GRANT ALL PRIVILEGES ON nova.* TO 'nova'@'%';" + mariadb -N -e "GRANT ALL PRIVILEGES ON nova_cell0.* TO 'nova'@'%';" # Neutron - mysql -N -e "CREATE DATABASE IF NOT EXISTS neutron;" - mysql -N -e "CREATE USER IF NOT EXISTS 'neutron'@'%' IDENTIFIED BY 'neutron';" - mysql -N -e "ALTER USER 'neutron'@'%' IDENTIFIED BY 'neutron';" - mysql -N -e "GRANT ALL PRIVILEGES ON neutron.* TO 'neutron'@'%';" + mariadb -N -e "CREATE DATABASE IF NOT EXISTS neutron;" + mariadb -N -e "CREATE USER IF NOT EXISTS 'neutron'@'%' IDENTIFIED BY 'neutron';" + mariadb -N -e "ALTER USER 'neutron'@'%' IDENTIFIED BY 'neutron';" + mariadb -N -e "GRANT ALL PRIVILEGES ON neutron.* TO 'neutron'@'%';" - mysql -N -e "FLUSH PRIVILEGES;" + mariadb -N -e "FLUSH PRIVILEGES;" ''; keystonePreStartScript = pkgs.writeShellScript "keystone-all-pre-start.sh" '' @@ -97,6 +97,8 @@ let ] }:$PATH + set -euxo pipefail + # Initialise the database keystone-manage --config-file ${config.keystone.config} db_sync # Set up the keystone's PKI infrastructure @@ -104,6 +106,8 @@ let keystone-manage --config-file ${config.keystone.config} credential_setup --keystone-user keystone --keystone-group keystone chown -R keystone:keystone /etc/keystone chown -R keystone:keystone /var/log/keystone + + systemctl restart uwsgi.service ''; keystoneStartScript = pkgs.writeShellScript "keystone-all.sh" '' @@ -135,6 +139,8 @@ let ] }:$PATH + systemctl stop glance-api.service + source /root/os-setup/.env exec runuser --user glance --preserve-environment -- ${pkgs.runtimeShell} <<'EOF' @@ -144,6 +150,8 @@ let openstack role add --user glance --user-domain default --system all reader glance-manage --config-file ${config.glance.config} db_sync EOF + + systemctl start glance-api.service ''; cinderStartScript = pkgs.writeShellScript "cinder.sh" '' @@ -155,6 +163,8 @@ let ] }:$PATH + systemctl stop cinder-api.service cinder-scheduler.service + source /root/os-setup/.env exec runuser --user cinder --preserve-environment -- ${pkgs.runtimeShell} <<'EOF' @@ -164,6 +174,8 @@ let openstack role add --user cinder --user-domain default --system all reader || true cinder-manage --config-file ${config.cinder.config} db sync EOF + + systemctl start cinder-api.service cinder-scheduler.service ''; placementStartScript = pkgs.writeShellScript "placement.sh" '' @@ -175,6 +187,8 @@ let ] }:$PATH + systemctl stop placement-api.service + source /root/os-setup/.env exec runuser --user placement --preserve-environment -- ${pkgs.runtimeShell} <<'EOF' @@ -183,6 +197,8 @@ let openstack role add --project service --user placement admin placement-manage --config-file ${config.placement.config} db sync EOF + + systemctl start placement-api.service ''; novaStartScript = pkgs.writeShellScript "nova.sh" '' @@ -196,6 +212,12 @@ let source /root/os-setup/.env + systemctl stop nova-conductor.service \ + nova-novncproxy.service \ + nova-scheduler.service \ + nova-serialproxy.service \ + nova-api.service + exec runuser --user nova --preserve-environment -- ${pkgs.runtimeShell} <<'EOF' set -euxo pipefail openstack user create --domain default --password nova nova @@ -205,6 +227,12 @@ let nova-manage --config-file ${config.nova.config} cell_v2 create_cell --name=cell1 --verbose nova-manage --config-file ${config.nova.config} db sync EOF + + systemctl start nova-conductor.service \ + nova-novncproxy.service \ + nova-scheduler.service \ + nova-serialproxy.service \ + nova-api.service ''; neutronStartScript = pkgs.writeShellScript "neutron.sh" '' @@ -216,6 +244,10 @@ let ] }:$PATH + systemctl stop neutron-dhcp-agent.service \ + neutron-metadata-agent.service \ + neutron-openvswitch-agent.service + source /root/os-setup/.env exec runuser --user neutron --preserve-environment -- ${pkgs.runtimeShell} <<'EOF' @@ -224,6 +256,11 @@ let openstack role add --project service --user neutron admin neutron-db-manage --config-file ${config.neutron.config} --config-file ${config.neutron.ml2Config} upgrade head EOF + + systemctl start neutron-dhcp-agent.service \ + neutron-metadata-agent.service \ + neutron-openvswitch-agent.service + ''; in From 3010c1fb85e7c0b4a3fdb510618f1914289467ad Mon Sep 17 00:00:00 2001 From: Paul Kroeher Date: Thu, 6 Aug 2026 09:32:03 +0200 Subject: [PATCH 18/71] nix: disable services but create all configurations and systemd units Signed-off-by: Paul Kroeher On-behalf-of: SAP paul.kroeher@sap.com --- modules/controller/glance.nix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/controller/glance.nix b/modules/controller/glance.nix index 34a8212..a8c446a 100644 --- a/modules/controller/glance.nix +++ b/modules/controller/glance.nix @@ -62,7 +62,7 @@ in ''; }; }; - config = mkIf cfg.enable { + config = { users.extraUsers.glance = { group = "glance"; From d6c64f3fc7e2c25b941ef0c34ea27701870882a8 Mon Sep 17 00:00:00 2001 From: Paul Kroeher Date: Thu, 6 Aug 2026 09:32:33 +0200 Subject: [PATCH 19/71] nix: fix mysql database permissions Signed-off-by: Paul Kroeher On-behalf-of: SAP paul.kroeher@sap.com --- modules/controller/openstack-controller.nix | 3 +++ 1 file changed, 3 insertions(+) diff --git a/modules/controller/openstack-controller.nix b/modules/controller/openstack-controller.nix index 5070ff4..6e4909f 100644 --- a/modules/controller/openstack-controller.nix +++ b/modules/controller/openstack-controller.nix @@ -86,6 +86,9 @@ let mariadb -N -e "ALTER USER 'neutron'@'%' IDENTIFIED BY 'neutron';" mariadb -N -e "GRANT ALL PRIVILEGES ON neutron.* TO 'neutron'@'%';" + # fix mariadb permissions + mariadb -N -e "delete from mysql.user where user = ''';" + mariadb -N -e "FLUSH PRIVILEGES;" ''; From 6e36c70d259135c2ca7687831b882e5c689418f7 Mon Sep 17 00:00:00 2001 From: Paul Kroeher Date: Thu, 6 Aug 2026 15:56:14 +0200 Subject: [PATCH 20/71] nix: keystone configuration Change enable logic of keystone from generation of configuration to running real service Signed-off-by: Paul Kroeher On-behalf-of: SAP paul.kroeher@sap.com --- modules/controller/keystone.nix | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/controller/keystone.nix b/modules/controller/keystone.nix index 9315577..305fca7 100644 --- a/modules/controller/keystone.nix +++ b/modules/controller/keystone.nix @@ -73,7 +73,7 @@ in }; }; - config = mkIf cfg.enable { + config = { users.extraUsers.keystone = { group = "keystone"; @@ -134,7 +134,7 @@ in ]; instance.type = "emperor"; - instance.vassals.keystone = { + instance.vassals.keystone = mkIf cfg.enable { type = "normal"; http11-socket = "127.0.0.1:5001"; buffer-size = 65535; From 00cab9652f739c43c494ad5a6779895d94ee413b Mon Sep 17 00:00:00 2001 From: Paul Kroeher Date: Thu, 6 Aug 2026 15:57:23 +0200 Subject: [PATCH 21/71] nix: change enabled logic of glance-api service Signed-off-by: Paul Kroeher On-behalf-of: SAP paul.kroeher@sap.com --- modules/controller/glance.nix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/controller/glance.nix b/modules/controller/glance.nix index a8c446a..9b2a487 100644 --- a/modules/controller/glance.nix +++ b/modules/controller/glance.nix @@ -135,7 +135,7 @@ in }; services.uwsgi = { - instance.vassals.glance = { + instance.vassals.glance = mkIf cfg.enable { socket-timeout = 10; http-auto-chunked = true; http-chunked-input = true; From b7e9222fe0c770c97f2d2ae06104c16d30d69d0f Mon Sep 17 00:00:00 2001 From: Paul Kroeher Date: Thu, 6 Aug 2026 15:58:26 +0200 Subject: [PATCH 22/71] nix: change cinder-api deployment to a uwsgi vassal Signed-off-by: Paul Kroeher On-behalf-of: SAP paul.kroeher@sap.com --- modules/controller/cinder.nix | 39 +++++++++++---------- modules/controller/openstack-controller.nix | 4 +-- 2 files changed, 23 insertions(+), 20 deletions(-) diff --git a/modules/controller/cinder.nix b/modules/controller/cinder.nix index 0dd4f2b..e5adc14 100644 --- a/modules/controller/cinder.nix +++ b/modules/controller/cinder.nix @@ -98,30 +98,33 @@ in }; "/etc/cinder/cinder.conf" = { L = { - argument = "${cinderConf}"; + argument = "${cfg.config}"; }; }; }; }; - systemd.services.cinder-api = { - description = "OpenStack Cinder API Daemon"; - after = [ - "cinder.service" - "rabbitmq.service" - "mysql.service" - "network.target" - ]; - path = [ cinder ]; - wantedBy = [ "multi-user.target" ]; - serviceConfig = { - User = "cinder"; - Group = "cinder"; - ExecStart = pkgs.writeShellScript "cinder-api.sh" '' - .cinder-wsgi-wrapped --port 8776 - ''; + services.uwsgi = { + instance.vassals.cinder-api = mkIf cfg.enable { + type = "normal"; + http-socket = "127.0.0.1:8776"; + wsgi-file = "${cinder}/bin/.cinder-wsgi-wrapped"; + pyargv = "--config-file ${cfg.config}"; + env = [ "PATH=$PATH:/run/current-system/sw/bin" ]; + + master = true; + processes = 4; + enable-threads = true; + thunder-lock = true; + lazy-apps = true; + die-on-term = true; + vacuum = true; + need-app = true; + buffer-size = 65535; + + immediate-uid = "cinder"; + immediate-gid = "cinder"; }; - enable = cfg.enable; }; systemd.services.cinder-scheduler = { diff --git a/modules/controller/openstack-controller.nix b/modules/controller/openstack-controller.nix index 6e4909f..3d6c5a0 100644 --- a/modules/controller/openstack-controller.nix +++ b/modules/controller/openstack-controller.nix @@ -166,7 +166,7 @@ let ] }:$PATH - systemctl stop cinder-api.service cinder-scheduler.service + systemctl stop cinder-scheduler.service source /root/os-setup/.env @@ -178,7 +178,7 @@ let cinder-manage --config-file ${config.cinder.config} db sync EOF - systemctl start cinder-api.service cinder-scheduler.service + systemctl start cinder-scheduler.service ''; placementStartScript = pkgs.writeShellScript "placement.sh" '' From bfa0a387b56bc876fff6697c61656ee01fc36d76 Mon Sep 17 00:00:00 2001 From: Paul Kroeher Date: Fri, 7 Aug 2026 10:30:21 +0200 Subject: [PATCH 23/71] nix: fix cinder setup in CI/CD context Signed-off-by: Paul Kroeher On-behalf-of: SAP paul.kroeher@sap.com --- modules/controller/cinder.nix | 24 ++++++- modules/controller/openstack-controller.nix | 69 ++++++--------------- 2 files changed, 41 insertions(+), 52 deletions(-) diff --git a/modules/controller/cinder.nix b/modules/controller/cinder.nix index e5adc14..1020a76 100644 --- a/modules/controller/cinder.nix +++ b/modules/controller/cinder.nix @@ -104,7 +104,29 @@ in }; }; - services.uwsgi = { + # create systemd service only if running in non production mode (CI/CD setup) + systemd.services.cinder-api = lib.mkIf (!config.openstack.production_setup) { + description = "OpenStack Cinder API Daemon"; + after = [ + "cinder.service" + "rabbitmq.service" + "mysql.service" + "network.target" + ]; + path = [ cinder ]; + wantedBy = [ "multi-user.target" ]; + serviceConfig = { + User = "cinder"; + Group = "cinder"; + ExecStart = pkgs.writeShellScript "cinder-api.sh" '' + .cinder-wsgi-wrapped --port 8776 + ''; + }; + enable = cfg.enable; + }; + + # create uwsgi vassal configuration only in production setup + services.uwsgi = lib.mkIf (config.openstack.production_setup) { instance.vassals.cinder-api = mkIf cfg.enable { type = "normal"; http-socket = "127.0.0.1:8776"; diff --git a/modules/controller/openstack-controller.nix b/modules/controller/openstack-controller.nix index 3d6c5a0..4d28913 100644 --- a/modules/controller/openstack-controller.nix +++ b/modules/controller/openstack-controller.nix @@ -124,7 +124,7 @@ let source /root/os-setup/.env - exec runuser --user keystone --preserve-environment -- ${pkgs.runtimeShell} <<'EOF' + runuser --user keystone --preserve-environment -- ${pkgs.runtimeShell} <<'EOF' set -euxo pipefail keystone-manage --config-file ${config.keystone.config} bootstrap \ --bootstrap-password admin \ @@ -142,19 +142,15 @@ let ] }:$PATH - systemctl stop glance-api.service - source /root/os-setup/.env - exec runuser --user glance --preserve-environment -- ${pkgs.runtimeShell} <<'EOF' + runuser --user glance --preserve-environment -- ${pkgs.runtimeShell} <<'EOF' set -euxo pipefail openstack user create --domain default --password glance glance openstack role add --project service --user glance admin openstack role add --user glance --user-domain default --system all reader glance-manage --config-file ${config.glance.config} db_sync EOF - - systemctl start glance-api.service ''; cinderStartScript = pkgs.writeShellScript "cinder.sh" '' @@ -166,19 +162,15 @@ let ] }:$PATH - systemctl stop cinder-scheduler.service - source /root/os-setup/.env - exec runuser --user cinder --preserve-environment -- ${pkgs.runtimeShell} <<'EOF' + runuser --user cinder --preserve-environment -- ${pkgs.runtimeShell} <<'EOF' set -euxo pipefail openstack user create --domain default --password cinder cinder || true openstack role add --project service --user cinder admin || true openstack role add --user cinder --user-domain default --system all reader || true cinder-manage --config-file ${config.cinder.config} db sync EOF - - systemctl start cinder-scheduler.service ''; placementStartScript = pkgs.writeShellScript "placement.sh" '' @@ -190,18 +182,14 @@ let ] }:$PATH - systemctl stop placement-api.service - source /root/os-setup/.env - exec runuser --user placement --preserve-environment -- ${pkgs.runtimeShell} <<'EOF' + runuser --user placement --preserve-environment -- ${pkgs.runtimeShell} <<'EOF' set -euxo pipefail openstack user create --domain default --password placement placement openstack role add --project service --user placement admin placement-manage --config-file ${config.placement.config} db sync EOF - - systemctl start placement-api.service ''; novaStartScript = pkgs.writeShellScript "nova.sh" '' @@ -215,13 +203,7 @@ let source /root/os-setup/.env - systemctl stop nova-conductor.service \ - nova-novncproxy.service \ - nova-scheduler.service \ - nova-serialproxy.service \ - nova-api.service - - exec runuser --user nova --preserve-environment -- ${pkgs.runtimeShell} <<'EOF' + runuser --user nova --preserve-environment -- ${pkgs.runtimeShell} <<'EOF' set -euxo pipefail openstack user create --domain default --password nova nova openstack role add --project service --user nova admin @@ -230,12 +212,6 @@ let nova-manage --config-file ${config.nova.config} cell_v2 create_cell --name=cell1 --verbose nova-manage --config-file ${config.nova.config} db sync EOF - - systemctl start nova-conductor.service \ - nova-novncproxy.service \ - nova-scheduler.service \ - nova-serialproxy.service \ - nova-api.service ''; neutronStartScript = pkgs.writeShellScript "neutron.sh" '' @@ -247,23 +223,14 @@ let ] }:$PATH - systemctl stop neutron-dhcp-agent.service \ - neutron-metadata-agent.service \ - neutron-openvswitch-agent.service - source /root/os-setup/.env - exec runuser --user neutron --preserve-environment -- ${pkgs.runtimeShell} <<'EOF' + runuser --user neutron --preserve-environment -- ${pkgs.runtimeShell} <<'EOF' set -euxo pipefail openstack user create --domain default --password neutron neutron openstack role add --project service --user neutron admin neutron-db-manage --config-file ${config.neutron.config} --config-file ${config.neutron.ml2Config} upgrade head EOF - - systemctl start neutron-dhcp-agent.service \ - neutron-metadata-agent.service \ - neutron-openvswitch-agent.service - ''; in @@ -334,8 +301,8 @@ in environment = adminEnv; wantedBy = [ "multi-user.target" ]; serviceConfig = { - User = "keystone"; - Group = "keystone"; + User = "root"; + Group = "root"; Type = "oneshot"; ExecStartPre = "+/root/os-setup/001-keystone-all-pre-start.sh"; ExecStart = "+/root/os-setup/002-keystone-all.sh"; @@ -353,8 +320,8 @@ in ]; serviceConfig = { Type = "oneshot"; - User = "glance"; - Group = "glance"; + User = "root"; + Group = "root"; ExecStart = "+/root/os-setup/003-glance.sh"; }; }; @@ -370,8 +337,8 @@ in ]; serviceConfig = { Type = "oneshot"; - User = "cinder"; - Group = "cinder"; + User = "root"; + Group = "root"; ExecStart = "+/root/os-setup/003-cinder.sh"; }; }; @@ -390,8 +357,8 @@ in ]; serviceConfig = { Type = "oneshot"; - User = "placement"; - Group = "placement"; + User = "root"; + Group = "root"; ExecStart = "+/root/os-setup/004-placement.sh"; }; }; @@ -407,8 +374,8 @@ in ]; serviceConfig = { Type = "oneshot"; - User = "nova"; - Group = "nova"; + User = "root"; + Group = "root"; ExecStart = "+/root/os-setup/006-nova.sh"; }; }; @@ -424,8 +391,8 @@ in ]; serviceConfig = { Type = "oneshot"; - User = "neutron"; - Group = "neutron"; + User = "root"; + Group = "root"; ExecStart = "+/root/os-setup/005-neutron.sh"; }; }; From 71b1847a6112458705215e48c2442c0992006530 Mon Sep 17 00:00:00 2001 From: Paul Kroeher Date: Fri, 7 Aug 2026 10:44:54 +0200 Subject: [PATCH 24/71] nix: change placement-api deployment to a uwsgi vassal Signed-off-by: Paul Kroeher On-behalf-of: SAP paul.kroeher@sap.com --- modules/controller/placement.nix | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/modules/controller/placement.nix b/modules/controller/placement.nix index d4ed670..06ccaf6 100644 --- a/modules/controller/placement.nix +++ b/modules/controller/placement.nix @@ -62,7 +62,8 @@ in }; }; - systemd.services.placement-api = { + # create systemd service only if running in non production mode (CI/CD setup) + systemd.services.placement-api = lib.mkIf (!config.openstack.production_setup) { description = "OpenStack Placement API Daemon"; after = [ "placement.service" @@ -86,5 +87,28 @@ in }; enable = cfg.enable; }; + + # create uwsgi vassal configuration only in production setup + services.uwsgi = lib.mkIf (config.openstack.production_setup) { + instance.vassals.placement-api = mkIf cfg.enable { + type = "normal"; + http-socket = "127.0.0.1:8778"; + wsgi-file = "${placement}/bin/.placement-api-wrapped"; + pyargv = "--config-file ${cfg.config}"; + + master = true; + processes = 4; + enable-threads = true; + thunder-lock = true; + lazy-apps = true; + die-on-term = true; + vacuum = true; + need-app = true; + buffer-size = 65535; + + immediate-uid = "placement"; + immediate-gid = "placement"; + }; + }; }; } From 916a78893e8e26e284814007b7cf768c81762954 Mon Sep 17 00:00:00 2001 From: Paul Kroeher Date: Fri, 7 Aug 2026 13:51:23 +0200 Subject: [PATCH 25/71] nix: nix cinder listen addresses Signed-off-by: Paul Kroeher On-behalf-of: SAP paul.kroeher@sap.com --- modules/controller/cinder.nix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/controller/cinder.nix b/modules/controller/cinder.nix index 1020a76..ba11289 100644 --- a/modules/controller/cinder.nix +++ b/modules/controller/cinder.nix @@ -129,7 +129,7 @@ in services.uwsgi = lib.mkIf (config.openstack.production_setup) { instance.vassals.cinder-api = mkIf cfg.enable { type = "normal"; - http-socket = "127.0.0.1:8776"; + http-socket = "0.0.0.0:8776"; wsgi-file = "${cinder}/bin/.cinder-wsgi-wrapped"; pyargv = "--config-file ${cfg.config}"; env = [ "PATH=$PATH:/run/current-system/sw/bin" ]; From 410a053ad4663ce93d4e10f9b45c081ba3d9718d Mon Sep 17 00:00:00 2001 From: Paul Kroeher Date: Fri, 7 Aug 2026 15:09:36 +0200 Subject: [PATCH 26/71] nix: move global option into separate file Signed-off-by: Paul Kroeher On-behalf-of: SAP paul.kroeher@sap.com --- modules/controller/openstack-controller.nix | 10 +--------- modules/generic/global-options.nix | 12 ++++++++++++ 2 files changed, 13 insertions(+), 9 deletions(-) create mode 100644 modules/generic/global-options.nix diff --git a/modules/controller/openstack-controller.nix b/modules/controller/openstack-controller.nix index 4d28913..40a479c 100644 --- a/modules/controller/openstack-controller.nix +++ b/modules/controller/openstack-controller.nix @@ -236,6 +236,7 @@ let in { imports = [ + ../generic/global-options.nix ./generic.nix ../generic/controller-host-entry.nix (import ./keystone.nix { inherit keystone; }) @@ -247,15 +248,6 @@ in (import ./cinder.nix { inherit cinder; }) # only cinder management component ]; - options.openstack.production_setup = lib.mkOption { - type = lib.types.bool; - default = false; - description = '' - Whether the controller uses a production database setup. When enabled, - the destructive database setup service is disabled. - ''; - }; - config = { environment.systemPackages = [ diff --git a/modules/generic/global-options.nix b/modules/generic/global-options.nix new file mode 100644 index 0000000..166d92d --- /dev/null +++ b/modules/generic/global-options.nix @@ -0,0 +1,12 @@ +{ lib, config, ... }: +with lib; +{ + options.openstack.production_setup = lib.mkOption { + type = lib.types.bool; + default = false; + description = '' + Whether the controller uses a production database setup. When enabled, + the destructive database setup service is disabled. + ''; + }; +} From 752bb71f12d65b5759b19554447168209c9b152d Mon Sep 17 00:00:00 2001 From: Paul Kroeher Date: Fri, 7 Aug 2026 15:12:42 +0200 Subject: [PATCH 27/71] nix: production setup for cinder-storage-node Signed-off-by: Paul Kroeher On-behalf-of: SAP paul.kroeher@sap.com --- modules/storage/cinder-storage-node.nix | 36 ++++++++++++++++++++----- 1 file changed, 29 insertions(+), 7 deletions(-) diff --git a/modules/storage/cinder-storage-node.nix b/modules/storage/cinder-storage-node.nix index a6f7ca2..e2a9adc 100644 --- a/modules/storage/cinder-storage-node.nix +++ b/modules/storage/cinder-storage-node.nix @@ -121,12 +121,27 @@ let cinderTgtConf = pkgs.writeText "cinder.conf" '' include /var/lib/cinder/volumes/* ''; + + cinderDefaultNFSexports = pkgs.writeText "exports" '' + /exports 10.0.0.0/24(rw,no_root_squash,insecure) + ''; in { imports = [ + ../generic/global-options.nix ../generic/controller-host-entry.nix ]; + options.openstack = { + storageIP = mkOption { + type = types.str; + default = "10.0.0.20"; + description = '' + IP address of the storage node. + ''; + }; + }; + options.cinder-storage-node = { enable = mkEnableOption "Enable OpenStack Cinder storage node." // { default = true; @@ -157,9 +172,15 @@ in Possible options: [ lvm | nfs ] ''; }; + exports = mkOption { + default = cinderDefaultNFSexports; + description = '' + The nfs-server /etc/exports file. + ''; + }; }; - config = mkIf cfg.enable { + config = { users.extraUsers.cinder = { group = "cinder"; isSystemUser = true; @@ -227,7 +248,7 @@ in group = "cinder"; mode = "0644"; argument = '' - 10.0.0.20:/exports + ${config.openstack.storageIP}:/exports ''; }; }; @@ -237,7 +258,7 @@ in # start iSCSI target daemon # we expose LVM block storage as iSCSI to compute hosts systemd.services.tgtd = { - enable = if (cfg.backend == "lvm") then true else false; + enable = if (cfg.backend == "lvm" && cfg.enable) then true else false; description = "iSCSI target framework daemon"; wantedBy = [ "multi-user.target" ]; after = [ @@ -270,11 +291,10 @@ in }; services.nfs.server.enable = if (cfg.backend == "lvm") then false else true; - services.nfs.server.exports = '' - /exports 10.0.0.0/24(rw,no_root_squash,insecure) - ''; + services.nfs.server.exports = builtins.readFile cfg.exports; - systemd.services.cinder-volume-group-setup = { + # run this service only in CI/CD setups + systemd.services.cinder-volume-group-setup = lib.mkIf (!config.openstack.production_setup) { description = "OpenStack Cinder volume group setup"; wantedBy = [ "multi-user.target" ]; path = with pkgs; [ @@ -305,6 +325,7 @@ in exportfs -rv ''; }; + enable = cfg.enable; }; # It seems regardless of what we do, the cinder-volume service does not @@ -370,6 +391,7 @@ in Restart = "on-failure"; RestartSec = 20; }; + enable = cfg.enable; }; }; } From a50cbfa70af3502cabfd2315224f53e33841c630 Mon Sep 17 00:00:00 2001 From: Paul Kroeher Date: Fri, 7 Aug 2026 16:55:05 +0200 Subject: [PATCH 28/71] nix: production setup for compute-node Signed-off-by: Paul Kroeher On-behalf-of: SAP paul.kroeher@sap.com --- modules/compute/compute.nix | 1 + modules/compute/neutron.nix | 3 ++- modules/compute/nova.nix | 7 ++++--- modules/generic/global-options.nix | 9 +++++++++ 4 files changed, 16 insertions(+), 4 deletions(-) diff --git a/modules/compute/compute.nix b/modules/compute/compute.nix index a085916..1786cbb 100644 --- a/modules/compute/compute.nix +++ b/modules/compute/compute.nix @@ -2,6 +2,7 @@ { ... }: { imports = [ + ../generic/global-options.nix ../generic/controller-host-entry.nix (import ./neutron.nix { inherit neutron; }) (import ./nova.nix { inherit nova; }) diff --git a/modules/compute/neutron.nix b/modules/compute/neutron.nix index f9327bc..4a931fd 100644 --- a/modules/compute/neutron.nix +++ b/modules/compute/neutron.nix @@ -78,7 +78,7 @@ in }; }; - config = mkIf cfg.enable { + config = { users.extraUsers.neutron = { group = "neutron"; isSystemUser = true; @@ -158,6 +158,7 @@ in ${neutron}/bin/neutron-openvswitch-agent --config-file=${cfg.config} --config-file=${cfg.openvswitchConfig} ''; }; + enable = cfg.enable; }; }; } diff --git a/modules/compute/nova.nix b/modules/compute/nova.nix index 9d0b0d1..eec1349 100644 --- a/modules/compute/nova.nix +++ b/modules/compute/nova.nix @@ -25,7 +25,7 @@ let state_path = /var/lib/nova rootwrap_config = ${rootwrapConf} compute_driver = libvirt.LibvirtDriver - my_ip = 10.0.0.39 + my_ip = ${config.openstack.myIp} transport_url = rabbit://openstack:openstack@controller [api] @@ -146,7 +146,7 @@ in }; }; - config = mkIf cfg.enable { + config = { users.extraUsers.nova = { group = "nova"; isSystemUser = true; @@ -194,7 +194,7 @@ in }; services.openiscsi = { - enable = true; + enable = cfg.enable; name = "iqn.iscsi.${config.networking.hostName}"; }; @@ -228,6 +228,7 @@ in ${cfg.novaPackage}/bin/nova-compute --config-file=${cfg.config} ''; }; + enable = cfg.enable; }; }; } diff --git a/modules/generic/global-options.nix b/modules/generic/global-options.nix index 166d92d..ffe2bc2 100644 --- a/modules/generic/global-options.nix +++ b/modules/generic/global-options.nix @@ -9,4 +9,13 @@ with lib; the destructive database setup service is disabled. ''; }; + + options.openstack.myIp = lib.mkOption { + # default of CI/CD setup + default = "10.0.0.39"; + type = types.str; + description = '' + My own ip address. + ''; + }; } From bb29321aefbff948138a7db074e81f8ceba72964 Mon Sep 17 00:00:00 2001 From: Paul Kroeher Date: Mon, 10 Aug 2026 15:12:07 +0200 Subject: [PATCH 29/71] nix: prod setup neutron-openvswitch-agent Create a reboot capable version of neutron-openvswitch-agent service Signed-off-by: Paul Kroeher On-behalf-of: SAP paul.kroeher@sap.com --- modules/compute/neutron.nix | 34 ++++++++++++++++++++++++++++++---- 1 file changed, 30 insertions(+), 4 deletions(-) diff --git a/modules/compute/neutron.nix b/modules/compute/neutron.nix index 4a931fd..8d21c83 100644 --- a/modules/compute/neutron.nix +++ b/modules/compute/neutron.nix @@ -50,6 +50,28 @@ let filterPath = "/etc/neutron/rootwrap.d"; inherit utils_env; }; + + openvswitchPrepareScript = pkgs.writeShellScript "openvswitch-setup.sh" '' + export PATH=${ + lib.makeBinPath [ + pkgs.openvswitch + ] + }:$PATH + + ovs-vsctl br-exists br-provider >/dev/null + status=$? + + if [[ $status -eq 0 ]]; then + echo "br-provider already exists. No further setup tasks." + else + echo "br-provider didn't exist already. Proceed with basic setup." + set -euxo pipefail + ovs-vsctl add-br br-provider + ovs-vsctl add-port br-provider ${cfg.providerInterface} + fi + + ''; + in { options.neutron = { @@ -68,6 +90,13 @@ in The Neutron OpenVSwitch config. ''; }; + openvswitchPrepare = mkOption { + default = openvswitchPrepareScript; + description = '' + Default OpenVswitch prepare script of systemd unit: neutron-openvswitch-agent.service + This prepare script creates the basic openvswitch setup. + ''; + }; providerInterface = mkOption { default = "eth2"; type = types.str; @@ -150,10 +179,7 @@ in conntrack-tools ]; serviceConfig = { - ExecStartPre = pkgs.writeShellScript "neutron-openvswitch-agent-pre.sh" '' - ${pkgs.openvswitch}/bin/ovs-vsctl add-br br-provider - ${pkgs.openvswitch}/bin/ovs-vsctl add-port br-provider ${cfg.providerInterface} - ''; + ExecStartPre = "${cfg.openvswitchPrepare}"; ExecStart = pkgs.writeShellScript "neutron-openvswitch-agent.sh" '' ${neutron}/bin/neutron-openvswitch-agent --config-file=${cfg.config} --config-file=${cfg.openvswitchConfig} ''; From b225d86e3f9b32ce4accf2beac7b4e347000c52c Mon Sep 17 00:00:00 2001 From: Paul Kroeher Date: Tue, 11 Aug 2026 11:33:50 +0200 Subject: [PATCH 30/71] nix: fix listen address placement service Signed-off-by: Paul Kroeher On-behalf-of: SAP paul.kroeher@sap.com --- modules/controller/placement.nix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/controller/placement.nix b/modules/controller/placement.nix index 06ccaf6..65ab6ff 100644 --- a/modules/controller/placement.nix +++ b/modules/controller/placement.nix @@ -92,7 +92,7 @@ in services.uwsgi = lib.mkIf (config.openstack.production_setup) { instance.vassals.placement-api = mkIf cfg.enable { type = "normal"; - http-socket = "127.0.0.1:8778"; + http-socket = "0.0.0.0:8778"; wsgi-file = "${placement}/bin/.placement-api-wrapped"; pyargv = "--config-file ${cfg.config}"; From e563749f1a0676f59fc3ce20f60f32f4213c8dd4 Mon Sep 17 00:00:00 2001 From: Paul Kroeher Date: Tue, 11 Aug 2026 16:36:29 +0200 Subject: [PATCH 31/71] nix: fix glance persistent directories after reboot Signed-off-by: Paul Kroeher On-behalf-of: SAP paul.kroeher@sap.com --- modules/controller/glance.nix | 36 ++++++++++----------- modules/controller/openstack-controller.nix | 9 ++++++ 2 files changed, 27 insertions(+), 18 deletions(-) diff --git a/modules/controller/glance.nix b/modules/controller/glance.nix index 9b2a487..d767273 100644 --- a/modules/controller/glance.nix +++ b/modules/controller/glance.nix @@ -75,42 +75,42 @@ in systemd.tmpfiles.settings = { "10-glance" = { + "/etc/glance/glance-api.conf" = { + L = { + argument = "${cfg.config}"; + }; + }; + "/etc/glance/glance-api-paste.ini" = { + L = { + argument = "${glance}/etc/glance/glance-api-paste.ini"; + }; + }; + "/etc/glance/schema-image.json" = { + L = { + argument = "${glance}/etc/glance/schema-image.json"; + }; + }; "/var/lib/glance/" = { - D = { + d = { user = "glance"; group = "glance"; mode = "0755"; }; }; "/var/log/glance/" = { - D = { + d = { user = "glance"; group = "glance"; mode = "0755"; }; }; "/var/lib/glance/images" = { - D = { + d = { user = "glance"; group = "glance"; mode = "0755"; }; }; - "/etc/glance/glance-api.conf" = { - L = { - argument = "${cfg.config}"; - }; - }; - "/etc/glance/glance-api-paste.ini" = { - L = { - argument = "${glance}/etc/glance/glance-api-paste.ini"; - }; - }; - "/etc/glance/schema-image.json" = { - L = { - argument = "${glance}/etc/glance/schema-image.json"; - }; - }; }; }; diff --git a/modules/controller/openstack-controller.nix b/modules/controller/openstack-controller.nix index 40a479c..02394e6 100644 --- a/modules/controller/openstack-controller.nix +++ b/modules/controller/openstack-controller.nix @@ -144,6 +144,15 @@ let source /root/os-setup/.env + mkdir -p /var/lib/glance + mkdir -p /var/lib/glance/images + chown -R glance /var/lib/glance + chgrp -R glance /var/lib/glance + + mkdir -p /var/log/glance + chown glance /var/log/glance + chgrp glance /var/log/glance + runuser --user glance --preserve-environment -- ${pkgs.runtimeShell} <<'EOF' set -euxo pipefail openstack user create --domain default --password glance glance From 4c2cd88e5835ced24f1953de4d873010f4f27f4f Mon Sep 17 00:00:00 2001 From: Paul Kroeher Date: Tue, 11 Aug 2026 16:37:08 +0200 Subject: [PATCH 32/71] nix: add check script for quick controller status Signed-off-by: Paul Kroeher On-behalf-of: SAP paul.kroeher@sap.com --- modules/controller/openstack-controller.nix | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/modules/controller/openstack-controller.nix b/modules/controller/openstack-controller.nix index 02394e6..eb224bb 100644 --- a/modules/controller/openstack-controller.nix +++ b/modules/controller/openstack-controller.nix @@ -242,6 +242,24 @@ let EOF ''; + checkControllerScript = pkgs.writeShellScript "check-controller.sh" '' + export PATH=${ + lib.makeBinPath [ + pkgs.openstackclient + ] + }:$PATH + + systemctl status neutron-server.service + systemctl status glance-api.server + systemctl status uwsgi.service + systemctl status cinder-scheduler.service + systemctl status nova-api.service + systemctl status nova-scheduler.service + systemctl status nova-conductor.service + systemctl status nova-novncproxy.service + systemctl status nova-serialproxy.service + ''; + in { imports = [ @@ -275,6 +293,7 @@ in install -m 0700 ${placementStartScript} /root/os-setup/004-placement.sh install -m 0700 ${novaStartScript} /root/os-setup/006-nova.sh install -m 0700 ${neutronStartScript} /root/os-setup/005-neutron.sh + install -m 0700 ${checkControllerScript} /root/os-setup/100-check-controller.sh ''; systemd.services.database-setup = lib.mkIf (!config.openstack.production_setup) { From a1e88f3deb69e487184ce56b8b5a123dd6efec60 Mon Sep 17 00:00:00 2001 From: Paul Kroeher Date: Wed, 12 Aug 2026 14:09:57 +0200 Subject: [PATCH 33/71] nix: fix cinder persistent directories after reboot Signed-off-by: Paul Kroeher On-behalf-of: SAP paul.kroeher@sap.com --- modules/storage/cinder-storage-node.nix | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/modules/storage/cinder-storage-node.nix b/modules/storage/cinder-storage-node.nix index e2a9adc..dbbe4a8 100644 --- a/modules/storage/cinder-storage-node.nix +++ b/modules/storage/cinder-storage-node.nix @@ -198,28 +198,28 @@ in systemd.tmpfiles.settings = { "20-cinder" = { "/var/lib/cinder/" = { - D = { + d = { user = "cinder"; group = "cinder"; mode = "0755"; }; }; "/var/lib/cinder/volumes" = { - D = { + d = { user = "cinder"; group = "cinder"; mode = "0755"; }; }; "/var/log/cinder/" = { - D = { + d = { user = "cinder"; group = "cinder"; mode = "0755"; }; }; "/etc/cinder/cinder.conf" = { - L = { + "L+" = { argument = "${cfg.config}"; }; }; @@ -229,12 +229,12 @@ in # LVM configuration files { "/etc/tgt/conf.d/cinder.conf" = { - L = { + "L+" = { argument = "${cinderTgtConf}"; }; }; "/etc/tgt/targets.conf" = { - L = { + "L+" = { argument = "${pkgs.tgt}/etc/tgt/targets.conf"; }; }; From 5c666bf3999a05fbf8b8bf93e5e22b0d6cf0e7ac Mon Sep 17 00:00:00 2001 From: Paul Kroeher Date: Wed, 12 Aug 2026 15:26:32 +0200 Subject: [PATCH 34/71] nix: add cinder volume setup script Signed-off-by: Paul Kroeher On-behalf-of: SAP paul.kroeher@sap.com --- modules/storage/cinder-storage-node.nix | 30 +++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/modules/storage/cinder-storage-node.nix b/modules/storage/cinder-storage-node.nix index dbbe4a8..4182dd7 100644 --- a/modules/storage/cinder-storage-node.nix +++ b/modules/storage/cinder-storage-node.nix @@ -125,6 +125,30 @@ let cinderDefaultNFSexports = pkgs.writeText "exports" '' /exports 10.0.0.0/24(rw,no_root_squash,insecure) ''; + + cinderVolumeSetupScript = pkgs.writeShellScript "cinder-volume-setup.sh" '' + export PATH=${ + lib.makeBinPath [ + pkgs.util-linux + ] + }:$PATH + + if [ -e /exports/.cinder-volume-setup-done-dont-delete-me ]; then + echo "cinder volume setup already done. Check content of this script." + fi + + mkdir /exports + mkfs.ext4 -F -m 0 -L cinder /dev/vdb + mount /dev/vdb /exports + exportfs -rv + rm -rf /exports/lost+found + chown cinder /exports + chgrp cinder /exports + + systemctl restart cinder-volume.service + touch /exports/.cinder-volume-setup-done-dont-delete-me + ''; + in { imports = [ @@ -181,6 +205,12 @@ in }; config = { + + system.activationScripts.openstack-setup-scripts.text = '' + install -d -m 0700 /root/os-setup + install -m 0700 ${cinderVolumeSetupScript} /root/os-setup/000-cinder-volume-setup.sh + ''; + users.extraUsers.cinder = { group = "cinder"; isSystemUser = true; From 5d0c01e96e382fe5347c27cb16e2d9cee60d9508 Mon Sep 17 00:00:00 2001 From: Paul Kroeher Date: Wed, 12 Aug 2026 16:24:36 +0200 Subject: [PATCH 35/71] nix: fix nova-compute persistent directories after reboot Signed-off-by: Paul Kroeher On-behalf-of: SAP paul.kroeher@sap.com --- modules/compute/nova.nix | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/modules/compute/nova.nix b/modules/compute/nova.nix index eec1349..1ac16ca 100644 --- a/modules/compute/nova.nix +++ b/modules/compute/nova.nix @@ -162,28 +162,28 @@ in systemd.tmpfiles.settings = { "10-nova" = { "/var/log/nova" = { - D = { + d = { group = "nova"; mode = "0755"; user = "nova"; }; }; "/var/lock/nova" = { - D = { + d = { group = "nova"; mode = "0755"; user = "nova"; }; }; "/var/lib/nova" = { - D = { + d = { group = "nova"; mode = "0755"; user = "nova"; }; }; "/var/lib/nova/instances" = { - D = { + d = { group = "nova"; mode = "0755"; user = "nova"; From 631b17d068c7e75926fdd07d7635fe9364ad65aa Mon Sep 17 00:00:00 2001 From: Paul Kroeher Date: Thu, 13 Aug 2026 16:52:22 +0200 Subject: [PATCH 36/71] nix: add environment config option to cinder Signed-off-by: Paul Kroeher On-behalf-of: SAP paul.kroeher@sap.com --- modules/controller/cinder.nix | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/modules/controller/cinder.nix b/modules/controller/cinder.nix index ba11289..b2d3d89 100644 --- a/modules/controller/cinder.nix +++ b/modules/controller/cinder.nix @@ -56,6 +56,24 @@ in The OpenStack Cinder package to use. ''; }; + envCinderApi = mkOption { + type = types.listOf types.str; + default = [ + "PYTHONWARNINGS=ignore::DeprecationWarning" + "PATH=$PATH:/run/current-system/sw/bin" + ]; + description = '' + Environment variables passed to the cinder-api uWSGI vassal. + ''; + }; + envCinderScheduler = mkOption { + default = { + PYTHONWARNINGS = "ignore::DeprecationWarning"; + }; + description = '' + Environment variables passed to the cinder-scheduler systemd unit. + ''; + }; }; config = { @@ -132,7 +150,7 @@ in http-socket = "0.0.0.0:8776"; wsgi-file = "${cinder}/bin/.cinder-wsgi-wrapped"; pyargv = "--config-file ${cfg.config}"; - env = [ "PATH=$PATH:/run/current-system/sw/bin" ]; + env = cfg.envCinderApi; master = true; processes = 4; @@ -167,6 +185,7 @@ in ''; }; enable = cfg.enable; + environment = cfg.envCinderScheduler; }; }; } From 8801e353e69742cc8b67debbc229934b2ba20715 Mon Sep 17 00:00:00 2001 From: Paul Kroeher Date: Thu, 13 Aug 2026 16:52:36 +0200 Subject: [PATCH 37/71] nix: add environment config option to glance Signed-off-by: Paul Kroeher On-behalf-of: SAP paul.kroeher@sap.com --- modules/controller/glance.nix | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/modules/controller/glance.nix b/modules/controller/glance.nix index d767273..5bff0d9 100644 --- a/modules/controller/glance.nix +++ b/modules/controller/glance.nix @@ -61,6 +61,15 @@ in The Glance config. ''; }; + env = mkOption { + type = types.listOf types.str; + default = [ + "PYTHONWARNINGS=ignore::DeprecationWarning" + ]; + description = '' + Environment variables passed to the Keystone uWSGI vassal. + ''; + }; }; config = { @@ -155,6 +164,7 @@ in immediate-uid = "glance"; immediate-gid = "glance"; wsgi-file = "${glance}/bin/.glance-wsgi-api-wrapped"; + env = cfg.env; }; }; }; From 109f6d65c42beeed80f6d8f0cc94adcc6a94b9c1 Mon Sep 17 00:00:00 2001 From: Paul Kroeher Date: Thu, 13 Aug 2026 16:52:46 +0200 Subject: [PATCH 38/71] nix: add environment config option to horizon Signed-off-by: Paul Kroeher On-behalf-of: SAP paul.kroeher@sap.com --- modules/controller/horizon.nix | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/modules/controller/horizon.nix b/modules/controller/horizon.nix index 90b0898..9fc5d3e 100644 --- a/modules/controller/horizon.nix +++ b/modules/controller/horizon.nix @@ -30,6 +30,15 @@ in description = "The Horizon Package to use"; type = types.package; }; + env = mkOption { + type = types.listOf types.str; + default = [ + "PYTHONWARNINGS=ignore::DeprecationWarning" + ]; + description = '' + Environment variables passed to the horizon uWSGI vassal. + ''; + }; }; config = { @@ -89,6 +98,7 @@ in thunder-lock = true; lazy-apps = true; chdir = "/var/lib/openstack_dashboard"; + env = cfg.env; }; }; From 5bcb9239010ea5a0cddc44f4ab1edd5f3132dca7 Mon Sep 17 00:00:00 2001 From: Paul Kroeher Date: Thu, 13 Aug 2026 16:53:01 +0200 Subject: [PATCH 39/71] nix: add environment config option to keystone Signed-off-by: Paul Kroeher On-behalf-of: SAP paul.kroeher@sap.com --- modules/controller/keystone.nix | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/modules/controller/keystone.nix b/modules/controller/keystone.nix index 305fca7..9818893 100644 --- a/modules/controller/keystone.nix +++ b/modules/controller/keystone.nix @@ -71,6 +71,15 @@ in The Keystone config. ''; }; + env = mkOption { + type = types.listOf types.str; + default = [ + "PYTHONWARNINGS=ignore::DeprecationWarning" + ]; + description = '' + Environment variables passed to the Keystone uWSGI vassal. + ''; + }; }; config = { @@ -148,6 +157,7 @@ in threads = 4; thunder-lock = true; lazy-apps = true; + env = cfg.env; }; }; }; From 858d244b42c776e9c49480d801ea2759af0fb6a7 Mon Sep 17 00:00:00 2001 From: Paul Kroeher Date: Thu, 13 Aug 2026 16:53:15 +0200 Subject: [PATCH 40/71] nix: add environment config option to placement Signed-off-by: Paul Kroeher On-behalf-of: SAP paul.kroeher@sap.com --- modules/controller/placement.nix | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/modules/controller/placement.nix b/modules/controller/placement.nix index 65ab6ff..d6a7eb1 100644 --- a/modules/controller/placement.nix +++ b/modules/controller/placement.nix @@ -40,6 +40,15 @@ in The Placement config. ''; }; + env = mkOption { + type = types.listOf types.str; + default = [ + "PYTHONWARNINGS=ignore::DeprecationWarning" + ]; + description = '' + Environment variables passed to the placement uWSGI vassal. + ''; + }; }; config = { @@ -108,6 +117,7 @@ in immediate-uid = "placement"; immediate-gid = "placement"; + env = cfg.env; }; }; }; From a44e4b40792eca31b5772781705efa9d08ceae6f Mon Sep 17 00:00:00 2001 From: Paul Kroeher Date: Mon, 17 Aug 2026 11:23:28 +0200 Subject: [PATCH 41/71] nix: fix neutron configuration symlinks After a configuration update the generated symlinks into the nix store should be updated by systemd-tmpfiles unit. Signed-off-by: Paul Kroeher On-behalf-of: SAP paul.kroeher@sap.com --- modules/controller/neutron.nix | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/modules/controller/neutron.nix b/modules/controller/neutron.nix index 0e02d48..b159919 100644 --- a/modules/controller/neutron.nix +++ b/modules/controller/neutron.nix @@ -193,27 +193,27 @@ in }; }; "/etc/neutron/neutron.conf" = { - L = { + "L+" = { argument = "${cfg.config}"; }; }; "/etc/neutron/plugins/ml2/ml2_conf.ini" = { - L = { + "L+" = { argument = "${cfg.ml2Config}"; }; }; "/etc/neutron/plugins/ml2/openvswitch_agent.ini" = { - L = { + "L+" = { argument = "${cfg.openvswitchConfig}"; }; }; "/etc/neutron/dhcp_agent.ini" = { - L = { + "L+" = { argument = "${cfg.dhcpAgentConfig}"; }; }; "/etc/neutron/api-paste.ini" = { - L = { + "L+" = { argument = "${neutron}/etc/neutron/api-paste.ini"; }; }; From 665ea78e280e7621845509ef82600928434ff600 Mon Sep 17 00:00:00 2001 From: Paul Kroeher Date: Mon, 17 Aug 2026 16:37:38 +0200 Subject: [PATCH 42/71] nix: explicitly enable uplink interfaces Signed-off-by: Paul Kroeher On-behalf-of: SAP paul.kroeher@sap.com --- modules/compute/neutron.nix | 3 +++ modules/controller/neutron.nix | 2 ++ 2 files changed, 5 insertions(+) diff --git a/modules/compute/neutron.nix b/modules/compute/neutron.nix index 8d21c83..329dc7d 100644 --- a/modules/compute/neutron.nix +++ b/modules/compute/neutron.nix @@ -70,6 +70,9 @@ let ovs-vsctl add-port br-provider ${cfg.providerInterface} fi + # enable uplink provider interface + ip link set dev ${cfg.providerInterface} up + ''; in diff --git a/modules/controller/neutron.nix b/modules/controller/neutron.nix index b159919..a757f4a 100644 --- a/modules/controller/neutron.nix +++ b/modules/controller/neutron.nix @@ -287,6 +287,8 @@ in ExecStartPre = pkgs.writeShellScript "pre.sh" '' ${pkgs.openvswitch}/bin/ovs-vsctl add-br br-provider || true ${pkgs.openvswitch}/bin/ovs-vsctl add-port br-provider ${cfg.providerInterface} || true + # enable uplink provider interface + ip link set dev ${cfg.providerInterface} up ''; ExecStart = pkgs.writeShellScript "neutron-openvswitch.sh" '' ${neutron}/bin/neutron-openvswitch-agent --config-file=${cfg.config} --config-file=${cfg.openvswitchConfig} From fce3505cb0b11f36419882ed7f085ca9cb84be38 Mon Sep 17 00:00:00 2001 From: Paul Kroeher Date: Mon, 24 Aug 2026 14:06:01 +0200 Subject: [PATCH 43/71] nix: update file content after configuration update Signed-off-by: Paul Kroeher On-behalf-of: SAP paul.kroeher@sap.com --- modules/storage/cinder-storage-node.nix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/storage/cinder-storage-node.nix b/modules/storage/cinder-storage-node.nix index 4182dd7..2d9cbf9 100644 --- a/modules/storage/cinder-storage-node.nix +++ b/modules/storage/cinder-storage-node.nix @@ -273,7 +273,7 @@ in # NFS configuration files { "/etc/cinder/nfs_shares" = { - f = { + "f+" = { user = "cinder"; group = "cinder"; mode = "0644"; From 24e88d781bdb20dcc88e076c0e3cf043e535fe3c Mon Sep 17 00:00:00 2001 From: Paul Kroeher Date: Wed, 26 Aug 2026 15:22:59 +0200 Subject: [PATCH 44/71] nix: add package designate Signed-off-by: Paul Kroeher On-behalf-of: SAP paul.kroeher@sap.com --- flake.nix | 1 + modules/controller/keystone.nix | 7 +- packages/default.nix | 42 ++++++++ packages/designate.nix | 151 ++++++++++++++++++++++++++++ packages/infoblox-client.nix | 49 +++++++++ packages/python-openstackclient.nix | 2 + 6 files changed, 251 insertions(+), 1 deletion(-) create mode 100644 packages/designate.nix create mode 100644 packages/infoblox-client.nix diff --git a/flake.nix b/flake.nix index 149e67a..7b18cf3 100644 --- a/flake.nix +++ b/flake.nix @@ -65,6 +65,7 @@ python3Packages writeText lib + libredirect ; }; diff --git a/modules/controller/keystone.nix b/modules/controller/keystone.nix index 9818893..937507b 100644 --- a/modules/controller/keystone.nix +++ b/modules/controller/keystone.nix @@ -44,6 +44,11 @@ let catalog.RegionOne.volumev3.adminURL = http://controller:8776/v3 catalog.RegionOne.volumev3.internalURL = http://controller:8776/v3 catalog.RegionOne.volumev3.name = Cinder Service + + catalog.RegionOne.dns.publicURL = http://controller:9001/ + catalog.RegionOne.dns.adminURL = http://controller:9001/ + catalog.RegionOne.dns.internalURL = http://controller:9001/ + catalog.RegionOne.dns.name = DNS Service ''; keystoneConf = pkgs.writeText "keystone.conf" '' @@ -112,7 +117,7 @@ in # Certain executables e.g. keystone-wsgi-public expect the config file # at a default location. "/etc/keystone/keystone.conf" = { - L = { + "L+" = { argument = "${cfg.config}"; }; }; diff --git a/packages/default.nix b/packages/default.nix index 573dcf2..329b0db 100644 --- a/packages/default.nix +++ b/packages/default.nix @@ -3,6 +3,7 @@ python3Packages, writeText, lib, + libredirect, }: let # In the past, the packages was not ready for the latest python interpreter. @@ -69,6 +70,38 @@ let tooz ; }; + designate = callPackage ./designate.nix { + inherit + futurist + libredirect + infoblox-client + keystoneauth1 + keystonemiddleware + openstacksdk + oslo-concurrency + oslo-config + oslo-context + oslo-db + oslo-i18n + oslo-log + oslo-messaging + oslo-middleware + oslo-policy + oslo-reports + oslo-rootwrap + oslo-serialization + oslo-service + oslo-upgradecheck + oslo-utils + oslo-versionedobjects + osprofiler + oslotest + python3Packages + python-designateclient + sqlalchemy + tooz + ; + }; django = callPackage ./django.nix { inherit python3Packages; }; django-appconf = callPackage ./django-appconf.nix { inherit @@ -220,6 +253,14 @@ let xstatic-tv4 ; }; + infoblox-client = callPackage ./infoblox-client.nix { + inherit + python3Packages + oslo-log + oslo-serialization + + ; + }; jsonpath-rw-ext = callPackage ./jsonpath-rw-ext.nix { inherit python3Packages; }; keystone = callPackage ./keystone.nix { inherit @@ -793,6 +834,7 @@ let osc-lib oslo-i18n python-cinderclient + python-designateclient python-keystoneclient python3Packages ; diff --git a/packages/designate.nix b/packages/designate.nix new file mode 100644 index 0000000..e34e2d5 --- /dev/null +++ b/packages/designate.nix @@ -0,0 +1,151 @@ +{ + fetchPypi, + libredirect, + futurist, + infoblox-client, + keystoneauth1, + keystonemiddleware, + openstacksdk, + oslo-concurrency, + oslo-config, + oslo-context, + oslo-db, + oslo-i18n, + oslo-log, + oslo-messaging, + oslo-middleware, + oslo-policy, + oslo-reports, + oslo-rootwrap, + oslo-serialization, + oslo-service, + oslo-upgradecheck, + oslo-utils, + oslo-versionedobjects, + oslotest, + osprofiler, + python-designateclient, + python3Packages, + sqlalchemy, + tooz, + writeScript, + lib, +}: +let + inherit (python3Packages) + alembic + dnspython + eventlet + flask + greenlet + jinja2 + jsonschema + paste + pastedeploy + pbr + pecan + pymysql + python-memcached + requests + requests-mock + stestr + stevedore + tenacity + testresources + testscenarios + webob + webtest + ; + + testExcludes = [ + "designate.tests.unit.backend.test_infoblox.*" + ]; + + excludeListFile = writeScript "test_excludes" (lib.concatStringsSep "\n" testExcludes); + +in +python3Packages.buildPythonPackage (rec { + pname = "designate"; + version = "19.1.0"; + pyproject = true; + build-system = [ + python3Packages.pbr + python3Packages.setuptools + ]; + + nativeBuildInputs = [ + pbr + ]; + + propagatedBuildInputs = [ + (alembic.override { inherit sqlalchemy; }) + dnspython + eventlet + flask + futurist + greenlet + infoblox-client + jinja2 + jsonschema + keystoneauth1 + keystonemiddleware + openstacksdk + oslo-concurrency + oslo-config + oslo-context + oslo-db + oslo-i18n + oslo-log + oslo-messaging + oslo-middleware + oslo-policy + oslo-reports + oslo-rootwrap + oslo-serialization + oslo-service + oslo-upgradecheck + oslo-utils + oslo-versionedobjects + oslotest + osprofiler + paste + pastedeploy + pbr + pecan + pymysql + python-designateclient + python-memcached + requests + sqlalchemy + stevedore + tenacity + tooz + webob + ]; + + nativeCheckInputs = [ + libredirect.hook + stestr + ]; + + checkInputs = [ + oslotest + requests-mock + testresources + testscenarios + webtest + ]; + + checkPhase = '' + runHook preCheck + echo "nameserver 127.0.0.1" > resolv.conf + export NIX_REDIRECTS="/etc/resolv.conf=$(realpath resolv.conf)" + stestr run --exclude-list ${excludeListFile} + runHook postCheck + ''; + + src = fetchPypi { + inherit pname version; + sha256 = "sha256-kqZJRM9WX8nXxEi+8KuBotwkIk8kVlpvDgp+3czDSeE="; + }; +}) diff --git a/packages/infoblox-client.nix b/packages/infoblox-client.nix new file mode 100644 index 0000000..67fc0ee --- /dev/null +++ b/packages/infoblox-client.nix @@ -0,0 +1,49 @@ +{ + fetchPypi, + python3Packages, + oslo-log, + oslo-serialization, +}: +let + inherit (python3Packages) + stestr + requests + urllib3 + six + ; +in +python3Packages.buildPythonPackage (rec { + pname = "infoblox-client"; + version = "0.6.2"; + pyproject = true; + build-system = [ + python3Packages.setuptools + ]; + + nativeBuildInputs = [ + ]; + + propagatedBuildInputs = [ + oslo-log + oslo-serialization + requests + urllib3 + six + ]; + + nativeCheckInputs = [ + stestr + ]; + + checkInputs = [ + ]; + + # checkPhase = '' + # stestr run + # ''; + + src = fetchPypi { + inherit pname version; + sha256 = "sha256-0hkgof6LLAwp2rYOl8rgLTSFYeQauik7Bi4rzhmszVE="; + }; +}) diff --git a/packages/python-openstackclient.nix b/packages/python-openstackclient.nix index 2997578..90bc4cb 100644 --- a/packages/python-openstackclient.nix +++ b/packages/python-openstackclient.nix @@ -3,6 +3,7 @@ osc-lib, oslo-i18n, python-cinderclient, + python-designateclient, python-keystoneclient, python3Packages, }: @@ -50,6 +51,7 @@ python3Packages.buildPythonPackage rec { oslo-i18n pbr python-cinderclient + python-designateclient python-keystoneclient requests stevedore From 6fe4fe6b71466749935f38197c2df507aba1fbee Mon Sep 17 00:00:00 2001 From: Paul Kroeher Date: Thu, 27 Aug 2026 15:22:38 +0200 Subject: [PATCH 45/71] nix: add module designate with external knot dns server backend Signed-off-by: Paul Kroeher On-behalf-of: SAP paul.kroeher@sap.com --- modules/controller/designate.nix | 248 ++++++++++++++++++++ modules/controller/openstack-controller.nix | 64 +++++ modules/default.nix | 3 + modules/knot-designate.nix | 105 +++++++++ packages/designate-knot3-backend.py | 112 +++++++++ packages/designate.nix | 12 + 6 files changed, 544 insertions(+) create mode 100644 modules/controller/designate.nix create mode 100644 modules/knot-designate.nix create mode 100644 packages/designate-knot3-backend.py diff --git a/modules/controller/designate.nix b/modules/controller/designate.nix new file mode 100644 index 0000000..39bdbda --- /dev/null +++ b/modules/controller/designate.nix @@ -0,0 +1,248 @@ +{ designate }: +{ + config, + lib, + pkgs, + ... +}: + +let + cfg = config.designate; + + designateConf = pkgs.writeText "designate.conf" '' + [DEFAULT] + transport_url = rabbit://openstack:openstack@controller + auth_strategy = keystone + log_dir = /var/log/designate + root_helper = "/run/wrappers/bin/sudo ${designate}/bin/designate-rootwrap ${rootwrapConf}" + + [storage:sqlalchemy] + connection = mysql+pymysql://designate:designate@controller/designate + + [service:api] + listen = 0.0.0.0:9001 + api_base_uri = http://controller:9001/ + api_paste_config = ${designate}/etc/designate/api-paste.ini + auth_strategy = keystone + enable_api_v2 = true + enable_api_admin = true + enable_host_header = true + enabled_extensions_admin = quotas,reports + + [service:mdns] + listen = 0.0.0.0:5354 + + [service:worker] + threads = 20 + + [keystone_authtoken] + www_authenticate_uri = http://controller:5000 + auth_url = http://controller:5000 + memcached_servers = controller:11211 + auth_type = password + project_domain_name = Default + user_domain_name = Default + project_name = service + username = designate + password = designate + + [oslo_concurrency] + lock_path = /var/lib/designate/tmp + ''; + + designatePools = pkgs.writeText "pools.yaml" '' + - name: default-knot + description: External Knot DNS 3 pool + attributes: {} + + ns_records: + - hostname: ns1.example.org. + priority: 1 + + nameservers: + - host: ${cfg.knot.address} + port: 53 + + targets: + - type: knot3 + description: External Knot DNS 3 server + masters: + - host: ${config.openstack.myIp} + port: 5354 + options: + host: ${cfg.knot.address} + port: 53 + ssh_bin_path: ${pkgs.openssh}/bin/ssh + ssh_host: ${cfg.knot.sshHost} + ssh_port: ${toString cfg.knot.sshPort} + ssh_user: ${cfg.knot.sshUser} + ssh_identity_file: ${cfg.knot.sshIdentityFile} + ssh_known_hosts_file: ${cfg.knot.sshKnownHostsFile} + knotc_bin_path: ${cfg.knot.knotcBinPath} + confdb_path: /var/lib/knot/confdb + control_socket: /run/knot/knot.sock + template: designate + ''; + + service = command: { + after = [ + "rabbitmq.service" + "mysql.service" + "network.target" + ] + ++ lib.optional (!config.openstack.production_setup) "designate.service"; + requires = lib.optional (!config.openstack.production_setup) "designate.service"; + wantedBy = [ "multi-user.target" ]; + path = [ designate ]; + restartTriggers = [ cfg.config ]; + serviceConfig = { + User = "designate"; + Group = "designate"; + ExecStart = "${designate}/bin/${command} --config-file=${cfg.config}"; + Restart = "on-failure"; + }; + }; + + designate_env = pkgs.python3.buildEnv.override { + extraLibs = [ designate ]; + }; + utils_env = pkgs.buildEnv { + name = "utils"; + paths = with pkgs; [ + designate_env + bind + ]; + }; + + rootwrapConf = pkgs.callPackage ../../lib/rootwrap-conf.nix { + package = designate_env; + filterPath = "/etc/designate/rootwrap.d"; + inherit utils_env; + }; + +in +{ + options.designate = { + enable = lib.mkEnableOption "OpenStack Designate with a Knot DNS backend" // { + default = false; + }; + + config = lib.mkOption { + type = lib.types.path; + default = designateConf; + description = "Designate configuration file."; + }; + + pools = lib.mkOption { + type = lib.types.path; + default = designatePools; + description = "Designate pools.yaml containing the Knot target."; + }; + + knot = { + address = lib.mkOption { + type = lib.types.str; + default = "192.168.200.23"; + description = "IP address of the external Knot DNS server."; + }; + sshHost = lib.mkOption { + type = lib.types.str; + default = cfg.knot.address; + description = "SSH host used to manage Knot's dynamic zone configuration."; + }; + sshUser = lib.mkOption { + type = lib.types.str; + default = "designate-knot"; + description = "Restricted account used to run knotc on the Knot server."; + }; + sshPort = lib.mkOption { + type = lib.types.port; + default = 22; + description = "SSH port of the external Knot server."; + }; + sshIdentityFile = lib.mkOption { + type = lib.types.str; + default = "/var/lib/designate/.ssh/id_ed25519"; + description = "Runtime path to Designate's SSH private key."; + }; + sshKnownHostsFile = lib.mkOption { + type = lib.types.str; + default = "/etc/ssh/ssh_known_hosts"; + description = "Known-hosts file used to authenticate the Knot server."; + }; + knotcBinPath = lib.mkOption { + type = lib.types.str; + default = "${pkgs.knot-dns}/bin/knotc"; + description = "Path to knotc on the external Knot server."; + }; + }; + }; + + config = lib.mkIf cfg.enable { + users.users.designate = { + group = "designate"; + isSystemUser = true; + }; + users.groups.designate = { }; + + security.sudo.enable = true; + security.sudo.extraConfig = '' + designate ALL = (root) NOPASSWD: ${designate}/bin/designate-rootwrap ${rootwrapConf} * + ''; + + systemd.tmpfiles.settings."10-designate" = { + "/etc/designate/designate.conf"."L+".argument = "${cfg.config}"; + "/etc/designate/pools.yaml"."L+".argument = "${cfg.pools}"; + "/var/lib/designate".d = { + user = "designate"; + group = "designate"; + mode = "0755"; + }; + "/var/lib/designate/.ssh".d = { + user = "designate"; + group = "designate"; + mode = "0755"; + }; + "/var/lib/designate/tmp".D = { + user = "designate"; + group = "designate"; + mode = "0755"; + }; + "/var/log/designate".d = { + user = "designate"; + group = "designate"; + mode = "0755"; + }; + }; + + systemd.services.designate-api = service "designate-api"; + systemd.services.designate-central = service "designate-central"; + systemd.services.designate-mdns = service "designate-mdns"; + systemd.services.designate-producer = service "designate-producer"; + systemd.services.designate-worker = lib.recursiveUpdate (service "designate-worker") { + path = [ + designate + pkgs.openssh + pkgs.sudo + ]; + }; + + networking.firewall.allowedTCPPorts = [ 5354 ]; + networking.firewall.allowedUDPPorts = [ 5354 ]; + + systemd.services.designate-pool-update = { + description = "Update the OpenStack Designate pools"; + after = [ "designate-central.service" ]; + requires = [ "designate-central.service" ]; + wantedBy = [ "multi-user.target" ]; + path = [ designate ]; + restartTriggers = [ cfg.pools ]; + serviceConfig = { + Type = "oneshot"; + User = "designate"; + Group = "designate"; + ExecStart = "${designate}/bin/designate-manage --config-file=${cfg.config} pool update --file=${cfg.pools}"; + }; + }; + }; +} diff --git a/modules/controller/openstack-controller.nix b/modules/controller/openstack-controller.nix index eb224bb..e18529d 100644 --- a/modules/controller/openstack-controller.nix +++ b/modules/controller/openstack-controller.nix @@ -7,6 +7,7 @@ horizon, cinder, python-openstackclient, + designate, }: { config, @@ -41,6 +42,7 @@ let mariadb -N -e "drop database nova;" || true mariadb -N -e "drop database nova_cell0;" || true mariadb -N -e "drop database neutron;" || true + mariadb -N -e "drop database designate;" || true ''; databaseSetupScript = pkgs.writeShellScript "database-setup.sh" '' @@ -86,6 +88,12 @@ let mariadb -N -e "ALTER USER 'neutron'@'%' IDENTIFIED BY 'neutron';" mariadb -N -e "GRANT ALL PRIVILEGES ON neutron.* TO 'neutron'@'%';" + # Designate + mariadb -N -e "CREATE DATABASE IF NOT EXISTS designate CHARACTER SET utf8 COLLATE utf8_general_ci;" + mariadb -N -e "CREATE USER IF NOT EXISTS 'designate'@'%' IDENTIFIED BY 'designate';" + mariadb -N -e "ALTER USER 'designate'@'%' IDENTIFIED BY 'designate';" + mariadb -N -e "GRANT ALL PRIVILEGES ON designate.* TO 'designate'@'%';" + # fix mariadb permissions mariadb -N -e "delete from mysql.user where user = ''';" @@ -242,6 +250,39 @@ let EOF ''; + designateStartScript = pkgs.writeShellScript "designate.sh" '' + export PATH=${ + lib.makeBinPath [ + designate + pkgs.openstackclient + pkgs.util-linux + ] + }:$PATH + + source /root/os-setup/.env + + set -euxo pipefail + + runuser --user designate -- \ + ${designate}/bin/designate-manage --config-file ${config.designate.config} database sync + + openstack user show designate >/dev/null 2>&1 || \ + openstack user create --domain default --password designate designate + openstack role add --project service --user designate admin + + # dynamic service endpoint updates are not implemented / configured in keystone currently + #openstack service show designate >/dev/null 2>&1 || \ + # openstack service create --name designate --description "DNS" dns + # + #for interface in public internal admin; do + # if ! openstack endpoint list \ + # --service designate --interface "$interface" -f value -c ID | grep -q .; then + # openstack endpoint create --region RegionOne \ + # dns "$interface" http://controller:9001/ + # fi + #done + ''; + checkControllerScript = pkgs.writeShellScript "check-controller.sh" '' export PATH=${ lib.makeBinPath [ @@ -273,6 +314,7 @@ in (import ./neutron.nix { inherit neutron; }) (import ./horizon.nix { inherit horizon; }) (import ./cinder.nix { inherit cinder; }) # only cinder management component + (import ./designate.nix { inherit designate; }) ]; config = { @@ -293,6 +335,7 @@ in install -m 0700 ${placementStartScript} /root/os-setup/004-placement.sh install -m 0700 ${novaStartScript} /root/os-setup/006-nova.sh install -m 0700 ${neutronStartScript} /root/os-setup/005-neutron.sh + install -m 0700 ${designateStartScript} /root/os-setup/007-designate.sh install -m 0700 ${checkControllerScript} /root/os-setup/100-check-controller.sh ''; @@ -416,5 +459,26 @@ in ExecStart = "+/root/os-setup/005-neutron.sh"; }; }; + + systemd.services.designate = + lib.mkIf (config.designate.enable && !config.openstack.production_setup) + { + description = "OpenStack Designate setup"; + after = [ "keystone-all.service" ]; + wantedBy = [ "multi-user.target" ]; + environment = adminEnv; + path = [ + pkgs.gnugrep + pkgs.openstackclient + pkgs.util-linux + designate + ]; + serviceConfig = { + Type = "oneshot"; + User = "root"; + Group = "root"; + ExecStart = "+/root/os-setup/007-designate.sh"; + }; + }; }; } diff --git a/modules/default.nix b/modules/default.nix index ef09b5d..8a2307a 100644 --- a/modules/default.nix +++ b/modules/default.nix @@ -9,6 +9,7 @@ horizon cinder python-openstackclient + designate ; placement = openstackPkgs.openstack-placement; }; @@ -17,5 +18,7 @@ storageModule = import ./storage/cinder-storage-node.nix { inherit (openstackPkgs) cinder; }; + knotDesignateModule = import ./knot-designate.nix; + testModules = import ./testing { inherit (openstackPkgs) python-openstackclient; }; } diff --git a/modules/knot-designate.nix b/modules/knot-designate.nix new file mode 100644 index 0000000..9f8db58 --- /dev/null +++ b/modules/knot-designate.nix @@ -0,0 +1,105 @@ +{ + config, + lib, + pkgs, + ... +}: + +let + cfg = config.services.knot-designate; +in +{ + options.services.knot-designate = { + enable = lib.mkEnableOption "an external Knot DNS secondary for OpenStack Designate"; + + mdnsAddress = lib.mkOption { + type = lib.types.str; + example = "10.0.0.39"; + description = "Address of the Designate MiniDNS server used for IXFR/AXFR."; + }; + + mdnsPort = lib.mkOption { + type = lib.types.port; + default = 5354; + description = "Port of the Designate MiniDNS server."; + }; + + sshUser = lib.mkOption { + type = lib.types.str; + default = "designate-knot"; + description = "Account through which Designate runs knotc."; + }; + + sshPort = lib.mkOption { + type = lib.types.port; + default = 22; + description = "SSH port on which Designate manages Knot."; + }; + + sshAuthorizedKeys = lib.mkOption { + type = lib.types.listOf lib.types.str; + default = [ ]; + description = "SSH public keys allowed to manage Knot for Designate."; + }; + }; + + config = lib.mkIf cfg.enable { + services.knot = { + enable = true; + settings = { + server.listen = [ + "0.0.0.0@53" + "::@53" + ]; + remote.designate-mdns.address = "${cfg.mdnsAddress}@${toString cfg.mdnsPort}"; + acl.designate-mdns = { + address = [ cfg.mdnsAddress ]; + action = [ "notify" ]; + }; + template.designate = { + master = "designate-mdns"; + acl = "designate-mdns"; + storage = "/var/lib/knot/zones"; + semantic-checks = true; + }; + }; + }; + + # Designate adds and removes zones dynamically. The configuration database + # preserves those entries while +nopurge refreshes the declarative base + # configuration on restart. + systemd.services.knot = { + preStart = '' + ${pkgs.knot-dns}/bin/knotc \ + --confdb=/var/lib/knot/confdb \ + conf-import /etc/knot/knot.conf +nopurge + ''; + serviceConfig = { + ExecStart = lib.mkForce "${pkgs.knot-dns}/bin/knotd --confdb=/var/lib/knot/confdb --socket=/run/knot/knot.sock"; + UMask = lib.mkForce "0007"; + }; + }; + + users.groups.${cfg.sshUser} = { }; + users.users.${cfg.sshUser} = { + isSystemUser = true; + group = cfg.sshUser; + extraGroups = [ "knot" ]; + home = "/var/lib/${cfg.sshUser}"; + createHome = true; + shell = pkgs.bashInteractive; + openssh.authorizedKeys.keys = cfg.sshAuthorizedKeys; + }; + + services.openssh = { + enable = true; + ports = [ cfg.sshPort ]; + }; + + networking.firewall.allowedTCPPorts = [ + 53 + cfg.sshPort + ]; + networking.firewall.allowedUDPPorts = [ 53 ]; + }; +} diff --git a/packages/designate-knot3-backend.py b/packages/designate-knot3-backend.py new file mode 100644 index 0000000..1e05aff --- /dev/null +++ b/packages/designate-knot3-backend.py @@ -0,0 +1,112 @@ +# Copyright 2026 CobaltCore contributors +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 + +"""Remote Knot DNS 3 backend for Designate. + +Knot is configured as a secondary for Designate MiniDNS. This backend only +manages Knot's dynamic zone configuration over SSH; record data is transferred +directly from MiniDNS to Knot using IXFR/AXFR. +""" + +import subprocess + +from oslo_concurrency import lockutils +from oslo_log import log as logging + +from designate.backend import base +from designate import exceptions +from designate import utils + + +LOG = logging.getLogger(__name__) + + +class Knot3Backend(base.Backend): + __plugin_name__ = 'knot3' + __backend_status__ = 'untested' + + def __init__(self, target): + super().__init__(target) + + self.ssh = self.options.get('ssh_bin_path', 'ssh') + self.ssh_host = self.options.get('ssh_host', self.host) + self.ssh_port = int(self.options.get('ssh_port', 22)) + self.ssh_user = self.options.get('ssh_user', 'designate-knot') + self.ssh_identity = self.options.get('ssh_identity_file') + self.ssh_known_hosts = self.options.get( + 'ssh_known_hosts_file', '/etc/ssh/ssh_known_hosts') + self.knotc = self.options.get('knotc_bin_path', 'knotc') + self.confdb = self.options.get( + 'confdb_path', '/var/lib/knot/confdb') + self.socket = self.options.get( + 'control_socket', '/run/knot/knot.sock') + self.template = self.options.get('template', 'designate') + + def _execute(self, *args): + command = [ + self.ssh, + '-o', 'BatchMode=yes', + '-p', str(self.ssh_port), + ] + if self.ssh_identity: + command.extend(['-i', self.ssh_identity]) + if self.ssh_known_hosts: + command.extend([ + '-o', 'UserKnownHostsFile=%s' % self.ssh_known_hosts]) + command.extend([ + '--', + '%s@%s' % (self.ssh_user, self.ssh_host), + self.knotc, + # '--confdb=%s' % self.confdb, + '--socket=%s' % self.socket, + ]) + command.extend(args) + + try: + return utils.execute(*command, timeout=self.timeout, run_as_root=False) + except (utils.processutils.ProcessExecutionError, + subprocess.TimeoutExpired) as error: + raise exceptions.Backend(error) + + @lockutils.synchronized('designate-knot3', external=True) + def _change_config(self, action, zone_name): + """Change one zone in Knot's persistent configuration database.""" + self._execute('conf-begin') + try: + item = 'zone[%s]' % zone_name.rstrip('.') + self._execute(action, item) + if action == 'conf-set': + self._execute( + 'conf-set', '%s.template' % item, self.template) + self._execute('conf-commit') + except Exception: + try: + self._execute('conf-abort') + except exceptions.Backend: + LOG.exception('Unable to abort the Knot configuration change') + raise + + def create_zone(self, context, zone): + LOG.debug('Creating zone %s in Knot', zone.name) + self._change_config('conf-set', zone.name) + + def update_zone(self, context, zone): + LOG.debug('Refreshing zone %s in Knot', zone.name) + self._execute('zone-refresh', zone.name.rstrip('.')) + + def delete_zone(self, context, zone, zone_params=None): + LOG.debug('Deleting zone %s from Knot', zone.name) + self._change_config('conf-unset', zone.name) + + # Once the configuration entry is gone, remove the secondary's copy. + # A failed purge does not mean that the zone is still being served. + try: + self._execute( + '--force', 'zone-purge', zone.name.rstrip('.'), '+orphan') + except exceptions.Backend: + LOG.warning('Unable to purge data for Knot zone %s', zone.name) diff --git a/packages/designate.nix b/packages/designate.nix index e34e2d5..022409d 100644 --- a/packages/designate.nix +++ b/packages/designate.nix @@ -77,6 +77,18 @@ python3Packages.buildPythonPackage (rec { pbr ]; + postPatch = '' + cp ${./designate-knot3-backend.py} designate/backend/impl_knot3.py + + substituteInPlace etc/designate/rootwrap.d/bind9.filters \ + --replace-fail "/usr/sbin/rndc" "rndc" + + substituteInPlace setup.cfg \ + --replace-fail \ + "infoblox = designate.backend.impl_infoblox:InfobloxBackend" \ + $'infoblox = designate.backend.impl_infoblox:InfobloxBackend\n\tknot3 = designate.backend.impl_knot3:Knot3Backend' + ''; + propagatedBuildInputs = [ (alembic.override { inherit sqlalchemy; }) dnspython From d108a90ada8aaffce439f5cef5c81f84fd4d41de Mon Sep 17 00:00:00 2001 From: Paul Kroeher Date: Fri, 28 Aug 2026 14:30:20 +0200 Subject: [PATCH 46/71] nix: add e2fsprogs to nova-compute systemd unit Signed-off-by: Paul Kroeher On-behalf-of: SAP paul.kroeher@sap.com --- modules/compute/nova.nix | 1 + 1 file changed, 1 insertion(+) diff --git a/modules/compute/nova.nix b/modules/compute/nova.nix index 1ac16ca..a7e74ba 100644 --- a/modules/compute/nova.nix +++ b/modules/compute/nova.nix @@ -220,6 +220,7 @@ in lvm2 openiscsi nfs-utils + e2fsprogs ] ++ cfg.extraPkgs; environment.PYTHONPATH = "${nova_env}/${pkgs.python3.sitePackages}"; From deca0a417783ecc9b3c7c403e61cc2b31a5d56a2 Mon Sep 17 00:00:00 2001 From: Paul Kroeher Date: Fri, 28 Aug 2026 15:05:31 +0200 Subject: [PATCH 47/71] nix: fix neutron runtime directory setup on controller Runtime direcoties shouldn't be recreated upon the next reboot. The content should be persist. Signed-off-by: Paul Kroeher On-behalf-of: SAP paul.kroeher@sap.com --- modules/controller/neutron.nix | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/modules/controller/neutron.nix b/modules/controller/neutron.nix index a757f4a..7fd1fef 100644 --- a/modules/controller/neutron.nix +++ b/modules/controller/neutron.nix @@ -186,7 +186,7 @@ in systemd.tmpfiles.settings = { "10-neutron" = { "/var/log/neutron" = { - D = { + d = { group = "neutron"; mode = "0755"; user = "neutron"; @@ -218,21 +218,21 @@ in }; }; "/var/lock/neutron" = { - D = { + d = { group = "neutron"; mode = "0755"; user = "neutron"; }; }; "/var/lib/neutron" = { - D = { + d = { group = "neutron"; mode = "0755"; user = "neutron"; }; }; "/var/lib/neutron/dhcp" = { - D = { + d = { group = "neutron"; mode = "0755"; user = "neutron"; From d1406d584e787ed55debdd5420198d938657c9d7 Mon Sep 17 00:00:00 2001 From: Paul Kroeher Date: Mon, 7 Sep 2026 16:20:51 +0200 Subject: [PATCH 48/71] nix: storage module - make path of exported filesystem configurable Signed-off-by: Paul Kroeher On-behalf-of: SAP paul.kroeher@sap.com --- modules/storage/cinder-storage-node.nix | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/modules/storage/cinder-storage-node.nix b/modules/storage/cinder-storage-node.nix index 2d9cbf9..8babe1e 100644 --- a/modules/storage/cinder-storage-node.nix +++ b/modules/storage/cinder-storage-node.nix @@ -164,6 +164,13 @@ in IP address of the storage node. ''; }; + storagePath = mkOption { + type = types.str; + default = "/exports"; + description = '' + Exported filesystem path on the storage node. + ''; + }; }; options.cinder-storage-node = { @@ -278,7 +285,7 @@ in group = "cinder"; mode = "0644"; argument = '' - ${config.openstack.storageIP}:/exports + ${config.openstack.storageIP}:${config.openstack.storagePath} ''; }; }; From f152934354295e32961ce996be04c1ec17ad823e Mon Sep 17 00:00:00 2001 From: Paul Kroeher Date: Mon, 7 Sep 2026 16:47:19 +0200 Subject: [PATCH 49/71] nix: storage module - add new configuration option: controllerHostname Signed-off-by: Paul Kroeher On-behalf-of: SAP paul.kroeher@sap.com --- modules/storage/cinder-storage-node.nix | 31 +++++++++++++++---------- 1 file changed, 19 insertions(+), 12 deletions(-) diff --git a/modules/storage/cinder-storage-node.nix b/modules/storage/cinder-storage-node.nix index 8babe1e..dd6695b 100644 --- a/modules/storage/cinder-storage-node.nix +++ b/modules/storage/cinder-storage-node.nix @@ -35,14 +35,14 @@ let cinderConfLvm = pkgs.writeText "cinder.conf" '' [DEFAULT] - transport_url = rabbit://openstack:openstack@controller + transport_url = rabbit://openstack:openstack@${cfg.controllerHostname} auth_strategy = keystone my_ip = 10.0.0.20 enabled_backends = lvm volumes_dir = /var/lib/cinder/volumes state_path = /var/lib/cinder rootwrap_config = ${rootwrapConf} - glance_api_servers = http://controller:9292 + glance_api_servers = http://${cfg.controllerHostname}:9292 verify_glance_signatures = disabled log_dir = /var/log/cinder iscsi_ip_address = $my_ip @@ -50,12 +50,12 @@ let iscsi_target_prefix = iqn.2010-10.org.openstack: [database] - connection = mysql+pymysql://cinder:cinder@controller/cinder + connection = mysql+pymysql://cinder:cinder@${cfg.controllerHostname}/cinder [keystone_authtoken] - www_authenticate_uri = http://controller:5000 - auth_url = http://controller:5000 - memcached_servers = controller:11211 + www_authenticate_uri = http://${cfg.controllerHostname}:5000 + auth_url = http://${cfg.controllerHostname}:5000 + memcached_servers = ${cfg.controllerHostname}:11211 auth_type = password project_domain_name = default user_domain_name = default @@ -82,24 +82,24 @@ let cinderConfNfs = pkgs.writeText "cinder.conf" '' [DEFAULT] - transport_url = rabbit://openstack:openstack@controller + transport_url = rabbit://openstack:openstack@${cfg.controllerHostname} auth_strategy = keystone my_ip = 10.0.0.20 enabled_backends = nfs volumes_dir = /var/lib/cinder/volumes state_path = /var/lib/cinder rootwrap_config = ${rootwrapConf} - glance_api_servers = http://controller:9292 + glance_api_servers = http://${cfg.controllerHostname}:9292 verify_glance_signatures = disabled log_dir = /var/log/cinder [database] - connection = mysql+pymysql://cinder:cinder@controller/cinder + connection = mysql+pymysql://cinder:cinder@${cfg.controllerHostname}/cinder [keystone_authtoken] - www_authenticate_uri = http://controller:5000 - auth_url = http://controller:5000 - memcached_servers = controller:11211 + www_authenticate_uri = http://${cfg.controllerHostname}:5000 + auth_url = http://${cfg.controllerHostname}:5000 + memcached_servers = ${cfg.controllerHostname}:11211 auth_type = password project_domain_name = default user_domain_name = default @@ -177,6 +177,13 @@ in enable = mkEnableOption "Enable OpenStack Cinder storage node." // { default = true; }; + controllerHostname = mkOption { + type = types.str; + default = "controller"; + description = '' + Hostname of the OpenStack controller. + ''; + }; config = mkOption { default = if (cfg.backend == "lvm") then cinderConfLvm else cinderConfNfs; description = '' From b738f7badabc15b2407ae1d3513afa9b26d29248 Mon Sep 17 00:00:00 2001 From: Paul Kroeher Date: Mon, 7 Sep 2026 16:52:05 +0200 Subject: [PATCH 50/71] nix: storage module - move configuration option into global namespace Signed-off-by: Paul Kroeher On-behalf-of: SAP paul.kroeher@sap.com --- modules/generic/global-options.nix | 8 +++++++ modules/storage/cinder-storage-node.nix | 31 ++++++++++--------------- 2 files changed, 20 insertions(+), 19 deletions(-) diff --git a/modules/generic/global-options.nix b/modules/generic/global-options.nix index ffe2bc2..0a77e85 100644 --- a/modules/generic/global-options.nix +++ b/modules/generic/global-options.nix @@ -1,6 +1,14 @@ { lib, config, ... }: with lib; { + options.openstack.controllerHostname = lib.mkOption { + type = types.str; + default = "controller"; + description = '' + Hostname of the OpenStack controller. + ''; + }; + options.openstack.production_setup = lib.mkOption { type = lib.types.bool; default = false; diff --git a/modules/storage/cinder-storage-node.nix b/modules/storage/cinder-storage-node.nix index dd6695b..a99caff 100644 --- a/modules/storage/cinder-storage-node.nix +++ b/modules/storage/cinder-storage-node.nix @@ -35,14 +35,14 @@ let cinderConfLvm = pkgs.writeText "cinder.conf" '' [DEFAULT] - transport_url = rabbit://openstack:openstack@${cfg.controllerHostname} + transport_url = rabbit://openstack:openstack@${config.openstack.controllerHostname} auth_strategy = keystone my_ip = 10.0.0.20 enabled_backends = lvm volumes_dir = /var/lib/cinder/volumes state_path = /var/lib/cinder rootwrap_config = ${rootwrapConf} - glance_api_servers = http://${cfg.controllerHostname}:9292 + glance_api_servers = http://${config.openstack.controllerHostname}:9292 verify_glance_signatures = disabled log_dir = /var/log/cinder iscsi_ip_address = $my_ip @@ -50,12 +50,12 @@ let iscsi_target_prefix = iqn.2010-10.org.openstack: [database] - connection = mysql+pymysql://cinder:cinder@${cfg.controllerHostname}/cinder + connection = mysql+pymysql://cinder:cinder@${config.openstack.controllerHostname}/cinder [keystone_authtoken] - www_authenticate_uri = http://${cfg.controllerHostname}:5000 - auth_url = http://${cfg.controllerHostname}:5000 - memcached_servers = ${cfg.controllerHostname}:11211 + www_authenticate_uri = http://${config.openstack.controllerHostname}:5000 + auth_url = http://${config.openstack.controllerHostname}:5000 + memcached_servers = ${config.openstack.controllerHostname}:11211 auth_type = password project_domain_name = default user_domain_name = default @@ -82,24 +82,24 @@ let cinderConfNfs = pkgs.writeText "cinder.conf" '' [DEFAULT] - transport_url = rabbit://openstack:openstack@${cfg.controllerHostname} + transport_url = rabbit://openstack:openstack@${config.openstack.controllerHostname} auth_strategy = keystone my_ip = 10.0.0.20 enabled_backends = nfs volumes_dir = /var/lib/cinder/volumes state_path = /var/lib/cinder rootwrap_config = ${rootwrapConf} - glance_api_servers = http://${cfg.controllerHostname}:9292 + glance_api_servers = http://${config.openstack.controllerHostname}:9292 verify_glance_signatures = disabled log_dir = /var/log/cinder [database] - connection = mysql+pymysql://cinder:cinder@${cfg.controllerHostname}/cinder + connection = mysql+pymysql://cinder:cinder@${config.openstack.controllerHostname}/cinder [keystone_authtoken] - www_authenticate_uri = http://${cfg.controllerHostname}:5000 - auth_url = http://${cfg.controllerHostname}:5000 - memcached_servers = ${cfg.controllerHostname}:11211 + www_authenticate_uri = http://${config.openstack.controllerHostname}:5000 + auth_url = http://${config.openstack.controllerHostname}:5000 + memcached_servers = ${config.openstack.controllerHostname}:11211 auth_type = password project_domain_name = default user_domain_name = default @@ -177,13 +177,6 @@ in enable = mkEnableOption "Enable OpenStack Cinder storage node." // { default = true; }; - controllerHostname = mkOption { - type = types.str; - default = "controller"; - description = '' - Hostname of the OpenStack controller. - ''; - }; config = mkOption { default = if (cfg.backend == "lvm") then cinderConfLvm else cinderConfNfs; description = '' From 5d9e374b6b7774ab539f3b3c7617fed706dc3c31 Mon Sep 17 00:00:00 2001 From: Paul Kroeher Date: Mon, 7 Sep 2026 17:01:01 +0200 Subject: [PATCH 51/71] nix: compute module - use new configuration option: controllerHostname Signed-off-by: Paul Kroeher On-behalf-of: SAP paul.kroeher@sap.com --- modules/compute/neutron.nix | 2 +- modules/compute/nova.nix | 16 ++++++++-------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/modules/compute/neutron.nix b/modules/compute/neutron.nix index 329dc7d..8eeb800 100644 --- a/modules/compute/neutron.nix +++ b/modules/compute/neutron.nix @@ -25,7 +25,7 @@ let debug = false # File name for the paste.deploy config for api service (string value) api_paste_config = ${neutron}/etc/neutron/api-paste.ini - transport_url = rabbit://openstack:openstack@controller + transport_url = rabbit://openstack:openstack@${config.openstack.controllerHostname} log_dir = /var/log/neutron [agent] diff --git a/modules/compute/nova.nix b/modules/compute/nova.nix index a7e74ba..f578cf2 100644 --- a/modules/compute/nova.nix +++ b/modules/compute/nova.nix @@ -26,7 +26,7 @@ let rootwrap_config = ${rootwrapConf} compute_driver = libvirt.LibvirtDriver my_ip = ${config.openstack.myIp} - transport_url = rabbit://openstack:openstack@controller + transport_url = rabbit://openstack:openstack@${config.openstack.controllerHostname} [api] auth_strategy = keystone @@ -38,12 +38,12 @@ let connection = sqlite:////var/lib/nova/nova.sqlite [glance] - api_servers = http://controller:9292 + api_servers = http://${config.openstack.controllerHostname}:9292 [keystone_authtoken] - www_authenticate_uri = http://controller:5000/ - auth_url = http://controller:5000/ - memcached_servers = controller:11211 + www_authenticate_uri = http://${config.openstack.controllerHostname}:5000/ + auth_url = http://${config.openstack.controllerHostname}:5000/ + memcached_servers = ${config.openstack.controllerHostname}:11211 auth_type = password project_domain_name = Default user_domain_name = Default @@ -55,7 +55,7 @@ let virt_type = kvm [neutron] - auth_url = http://controller:5000 + auth_url = http://${config.openstack.controllerHostname}:5000 auth_type = password project_domain_name = Default user_domain_name = Default @@ -76,13 +76,13 @@ let project_name = service auth_type = password user_domain_name = Default - auth_url = http://controller:5000/v3 + auth_url = http://${config.openstack.controllerHostname}:5000/v3 username = placement password = placement [service_user] send_service_user_token = true - auth_url = http://controller:5000/ + auth_url = http://${config.openstack.controllerHostname}:5000/ auth_strategy = keystone auth_type = password project_domain_name = Default From 1396dc2e935de2ffca4cd0c2ec4bffa8091fb90c Mon Sep 17 00:00:00 2001 From: Paul Kroeher Date: Mon, 7 Sep 2026 17:08:36 +0200 Subject: [PATCH 52/71] nix: controller module use new configuration option: controllerHostname Signed-off-by: Paul Kroeher On-behalf-of: SAP paul.kroeher@sap.com --- modules/controller/cinder.nix | 12 +++--- modules/controller/designate.nix | 12 +++--- modules/controller/glance.nix | 10 ++--- modules/controller/keystone.nix | 46 ++++++++++----------- modules/controller/neutron.nix | 14 +++---- modules/controller/nova.nix | 22 +++++----- modules/controller/openstack-controller.nix | 6 +-- modules/controller/placement.nix | 6 +-- modules/generic/controller-host-entry.nix | 1 + 9 files changed, 65 insertions(+), 64 deletions(-) diff --git a/modules/controller/cinder.nix b/modules/controller/cinder.nix index b2d3d89..acf5a8d 100644 --- a/modules/controller/cinder.nix +++ b/modules/controller/cinder.nix @@ -13,18 +13,18 @@ let cinderConf = pkgs.writeText "cinder-api.conf" '' [DEFAULT] - transport_url = rabbit://openstack:openstack@controller + transport_url = rabbit://openstack:openstack@${config.openstack.controllerHostname} auth_strategy = keystone - my_ip = controller + my_ip = ${config.openstack.controllerIP} verify_glance_signatures = disabled [database] - connection = mysql+pymysql://cinder:cinder@controller/cinder + connection = mysql+pymysql://cinder:cinder@${config.openstack.controllerHostname}/cinder [keystone_authtoken] - www_authenticate_uri = http://controller:5000 - auth_url = http://controller:5000 - memcached_servers = controller:11211 + www_authenticate_uri = http://${config.openstack.controllerHostname}:5000 + auth_url = http://${config.openstack.controllerHostname}:5000 + memcached_servers = ${config.openstack.controllerHostname}:11211 auth_type = password project_domain_name = default user_domain_name = default diff --git a/modules/controller/designate.nix b/modules/controller/designate.nix index 39bdbda..77100e4 100644 --- a/modules/controller/designate.nix +++ b/modules/controller/designate.nix @@ -11,17 +11,17 @@ let designateConf = pkgs.writeText "designate.conf" '' [DEFAULT] - transport_url = rabbit://openstack:openstack@controller + transport_url = rabbit://openstack:openstack@${config.openstack.controllerHostname} auth_strategy = keystone log_dir = /var/log/designate root_helper = "/run/wrappers/bin/sudo ${designate}/bin/designate-rootwrap ${rootwrapConf}" [storage:sqlalchemy] - connection = mysql+pymysql://designate:designate@controller/designate + connection = mysql+pymysql://designate:designate@${config.openstack.controllerHostname}/designate [service:api] listen = 0.0.0.0:9001 - api_base_uri = http://controller:9001/ + api_base_uri = http://${config.openstack.controllerHostname}:9001/ api_paste_config = ${designate}/etc/designate/api-paste.ini auth_strategy = keystone enable_api_v2 = true @@ -36,9 +36,9 @@ let threads = 20 [keystone_authtoken] - www_authenticate_uri = http://controller:5000 - auth_url = http://controller:5000 - memcached_servers = controller:11211 + www_authenticate_uri = http://${config.openstack.controllerHostname}:5000 + auth_url = http://${config.openstack.controllerHostname}:5000 + memcached_servers = ${config.openstack.controllerHostname}:11211 auth_type = password project_domain_name = Default user_domain_name = Default diff --git a/modules/controller/glance.nix b/modules/controller/glance.nix index 5bff0d9..cde00ad 100644 --- a/modules/controller/glance.nix +++ b/modules/controller/glance.nix @@ -17,12 +17,12 @@ let log_dir = /var/log/glance [database] - connection = mysql+pymysql://glance:glance@controller/glance + connection = mysql+pymysql://glance:glance@${config.openstack.controllerHostname}/glance [keystone_authtoken] - www_authenticate_uri = http://controller:5000 - auth_url = http://controller:5000 - memcached_servers = controller:11211 + www_authenticate_uri = http://${config.openstack.controllerHostname}:5000 + auth_url = http://${config.openstack.controllerHostname}:5000 + memcached_servers = ${config.openstack.controllerHostname}:11211 auth_type = password project_domain_name = Default user_domain_name = Default @@ -40,7 +40,7 @@ let filesystem_store_datadir = /var/lib/glance/images/ [oslo_limit] - auth_url = http://controller:5000 + auth_url = http://${config.openstack.controllerHostname}:5000 auth_type = password user_domain_id = default username = glance diff --git a/modules/controller/keystone.nix b/modules/controller/keystone.nix index 937507b..b9f5617 100644 --- a/modules/controller/keystone.nix +++ b/modules/controller/keystone.nix @@ -14,40 +14,40 @@ let serviceEndPointTemplateConf = pkgs.writeText "default_catalog.templates" '' # config for templated.Catalog, using camelCase because I don't want to do # translations for keystone compat - catalog.RegionOne.identity.publicURL = http://controller:5000/v3 - catalog.RegionOne.identity.adminURL = http://controller:5000/v3 - catalog.RegionOne.identity.internalURL = http://controller:5000/v3 + catalog.RegionOne.identity.publicURL = http://${config.openstack.controllerHostname}:5000/v3 + catalog.RegionOne.identity.adminURL = http://${config.openstack.controllerHostname}:5000/v3 + catalog.RegionOne.identity.internalURL = http://${config.openstack.controllerHostname}:5000/v3 catalog.RegionOne.identity.name = Identity Service # fake compute service for now to help novaclient tests work - catalog.RegionOne.compute.publicURL = http://controller:8774/v2.1 - catalog.RegionOne.compute.adminURL = http://controller:8774/v2.1 - catalog.RegionOne.compute.internalURL = http://controller:8774/v2.1 + catalog.RegionOne.compute.publicURL = http://${config.openstack.controllerHostname}:8774/v2.1 + catalog.RegionOne.compute.adminURL = http://${config.openstack.controllerHostname}:8774/v2.1 + catalog.RegionOne.compute.internalURL = http://${config.openstack.controllerHostname}:8774/v2.1 catalog.RegionOne.compute.name = Compute Service V2.1 - catalog.RegionOne.image.publicURL = http://controller:9292 - catalog.RegionOne.image.adminURL = http://controller:9292 - catalog.RegionOne.image.internalURL = http://controller:9292 + catalog.RegionOne.image.publicURL = http://${config.openstack.controllerHostname}:9292 + catalog.RegionOne.image.adminURL = http://${config.openstack.controllerHostname}:9292 + catalog.RegionOne.image.internalURL = http://${config.openstack.controllerHostname}:9292 catalog.RegionOne.image.name = Image Service - catalog.RegionOne.network.publicURL = http://controller:9696 - catalog.RegionOne.network.adminURL = http://controller:9696 - catalog.RegionOne.network.internalURL = http://controller:9696 + catalog.RegionOne.network.publicURL = http://${config.openstack.controllerHostname}:9696 + catalog.RegionOne.network.adminURL = http://${config.openstack.controllerHostname}:9696 + catalog.RegionOne.network.internalURL = http://${config.openstack.controllerHostname}:9696 catalog.RegionOne.network.name = Network Service - catalog.RegionOne.placement.publicURL = http://controller:8778 - catalog.RegionOne.placement.adminURL = http://controller:8778 - catalog.RegionOne.placement.internalURL = http://controller:8778 + catalog.RegionOne.placement.publicURL = http://${config.openstack.controllerHostname}:8778 + catalog.RegionOne.placement.adminURL = http://${config.openstack.controllerHostname}:8778 + catalog.RegionOne.placement.internalURL = http://${config.openstack.controllerHostname}:8778 catalog.RegionOne.placement.name = Placement Service - catalog.RegionOne.volumev3.publicURL = http://controller:8776/v3 - catalog.RegionOne.volumev3.adminURL = http://controller:8776/v3 - catalog.RegionOne.volumev3.internalURL = http://controller:8776/v3 + catalog.RegionOne.volumev3.publicURL = http://${config.openstack.controllerHostname}:8776/v3 + catalog.RegionOne.volumev3.adminURL = http://${config.openstack.controllerHostname}:8776/v3 + catalog.RegionOne.volumev3.internalURL = http://${config.openstack.controllerHostname}:8776/v3 catalog.RegionOne.volumev3.name = Cinder Service - catalog.RegionOne.dns.publicURL = http://controller:9001/ - catalog.RegionOne.dns.adminURL = http://controller:9001/ - catalog.RegionOne.dns.internalURL = http://controller:9001/ + catalog.RegionOne.dns.publicURL = http://${config.openstack.controllerHostname}:9001/ + catalog.RegionOne.dns.adminURL = http://${config.openstack.controllerHostname}:9001/ + catalog.RegionOne.dns.internalURL = http://${config.openstack.controllerHostname}:9001/ catalog.RegionOne.dns.name = DNS Service ''; @@ -55,7 +55,7 @@ let [DEFAULT] log_dir = /var/log/keystone [database] - connection = mysql+pymysql://keystone:keystone@controller/keystone + connection = mysql+pymysql://keystone:keystone@${config.openstack.controllerHostname}/keystone [token] provider = fernet @@ -127,7 +127,7 @@ in services.nginx = { enable = true; virtualHosts = { - controller = { + ${config.openstack.controllerHostname} = { locations."/".proxyPass = "http://127.0.0.1:5001/"; listen = [ { diff --git a/modules/controller/neutron.nix b/modules/controller/neutron.nix index 7fd1fef..07cb810 100644 --- a/modules/controller/neutron.nix +++ b/modules/controller/neutron.nix @@ -25,24 +25,24 @@ let # neutron.conf is used as configuration file for neutron-metadata-agent as well neutronConf = pkgs.writeText "neutron.conf" '' [database] - connection = mysql+pymysql://neutron:neutron@controller/neutron + connection = mysql+pymysql://neutron:neutron@${config.openstack.controllerHostname}/neutron [DEFAULT] core_plugin = ml2 service_plugins = api_paste_config = ${neutron}/etc/neutron/api-paste.ini - transport_url = rabbit://openstack:openstack@controller + transport_url = rabbit://openstack:openstack@${config.openstack.controllerHostname} auth_strategy = keystone notify_nova_on_port_status_changes = true notify_nova_on_port_data_changes = true log_dir = /var/log/neutron - nova_metadata_host = controller + nova_metadata_host = ${config.openstack.controllerHostname} metadata_proxy_shared_secret = neutron_metadata_secret [keystone_authtoken] - www_authenticate_uri = http://controller:5000 - auth_url = http://controller:5000 - memcached_servers = controller:11211 + www_authenticate_uri = http://${config.openstack.controllerHostname}:5000 + auth_url = http://${config.openstack.controllerHostname}:5000 + memcached_servers = ${config.openstack.controllerHostname}:11211 auth_type = password project_domain_name = Default user_domain_name = Default @@ -57,7 +57,7 @@ let service_token_roles = admin [nova] - auth_url = http://controller:5000 + auth_url = http://${config.openstack.controllerHostname}:5000 auth_type = password project_domain_name = Default user_domain_name = Default diff --git a/modules/controller/nova.nix b/modules/controller/nova.nix index 12e6bda..818d26e 100644 --- a/modules/controller/nova.nix +++ b/modules/controller/nova.nix @@ -13,14 +13,14 @@ let novaConf = pkgs.writeText "nova.conf" '' [api_database] - connection = mysql+pymysql://nova:nova@controller/nova_api + connection = mysql+pymysql://nova:nova@${config.openstack.controllerHostname}/nova_api [database] - connection = mysql+pymysql://nova:nova@controller/nova + connection = mysql+pymysql://nova:nova@${config.openstack.controllerHostname}/nova [DEFAULT] - transport_url = rabbit://openstack:openstack@controller:5672/ - my_ip = 10.0.0.11 + transport_url = rabbit://openstack:openstack@${config.openstack.controllerHostname}:5672/ + my_ip = ${config.openstack.controllerIP} log_dir = /var/log/nova lock_path = /var/lock/nova state_path = /var/lib/nova @@ -29,9 +29,9 @@ let auth_strategy = keystone [keystone_authtoken] - www_authenticate_uri = http://controller:5000/ - auth_url = http://controller:5000/ - memcached_servers = controller:11211 + www_authenticate_uri = http://${config.openstack.controllerHostname}:5000/ + auth_url = http://${config.openstack.controllerHostname}:5000/ + memcached_servers = ${config.openstack.controllerHostname}:11211 auth_type = password project_domain_name = Default user_domain_name = Default @@ -41,7 +41,7 @@ let [service_user] send_service_user_token = true - auth_url = http://controller:5000/ + auth_url = http://${config.openstack.controllerHostname}:5000/ auth_strategy = keystone auth_type = password project_domain_name = Default @@ -59,7 +59,7 @@ let server_proxyclient_address = $my_ip [glance] - api_servers = http://controller:9292 + api_servers = http://${config.openstack.controllerHostname}:9292 [oslo_concurrency] lock_path = /var/lib/nova/tmp @@ -69,7 +69,7 @@ let project_name = service auth_type = password user_domain_name = Default - auth_url = http://controller:5000/v3 + auth_url = http://${config.openstack.controllerHostname}:5000/v3 username = placement password = placement @@ -77,7 +77,7 @@ let discover_hosts_in_cells_interval = 300 [neutron] - auth_url = http://controller:5000 + auth_url = http://${config.openstack.controllerHostname}:5000 auth_type = password project_domain_name = Default user_domain_name = Default diff --git a/modules/controller/openstack-controller.nix b/modules/controller/openstack-controller.nix index e18529d..ffae5cf 100644 --- a/modules/controller/openstack-controller.nix +++ b/modules/controller/openstack-controller.nix @@ -22,7 +22,7 @@ let OS_PROJECT_NAME = "admin"; OS_USER_DOMAIN_NAME = "Default"; OS_PROJECT_DOMAIN_NAME = "Default"; - OS_AUTH_URL = "http://controller:5000/v3"; + OS_AUTH_URL = "http://${config.openstack.controllerHostname}:5000/v3"; OS_IDENTITY_API_VERSION = "3"; }; @@ -278,7 +278,7 @@ let # if ! openstack endpoint list \ # --service designate --interface "$interface" -f value -c ID | grep -q .; then # openstack endpoint create --region RegionOne \ - # dns "$interface" http://controller:9001/ + # dns "$interface" http://${config.openstack.controllerHostname}:9001/ # fi #done ''; @@ -407,7 +407,7 @@ in }; # Placement service can be tested by executing - # curl http://controller:8778 + # curl http://${config.openstack.controllerHostname}:8778 # and receive some json with version info as result. systemd.services.placement = lib.mkIf (!config.openstack.production_setup) { description = "OpenStack Placement setup"; diff --git a/modules/controller/placement.nix b/modules/controller/placement.nix index d6a7eb1..d49a9da 100644 --- a/modules/controller/placement.nix +++ b/modules/controller/placement.nix @@ -16,8 +16,8 @@ let auth_strategy = keystone [keystone_authtoken] - www_authenticate_uri = http://controller:5000 - auth_url = http://controller:5000 + www_authenticate_uri = http://${config.openstack.controllerHostname}:5000 + auth_url = http://${config.openstack.controllerHostname}:5000 auth_type = password project_domain_name = Default user_domain_name = Default @@ -26,7 +26,7 @@ let password = placement [placement_database] - connection = mysql+pymysql://placement:placement@controller/placement + connection = mysql+pymysql://placement:placement@${config.openstack.controllerHostname}/placement ''; in { diff --git a/modules/generic/controller-host-entry.nix b/modules/generic/controller-host-entry.nix index 1a80a62..5321766 100644 --- a/modules/generic/controller-host-entry.nix +++ b/modules/generic/controller-host-entry.nix @@ -4,6 +4,7 @@ with lib; options.openstack = { controllerIP = mkOption { type = types.str; + default = "10.0.0.11"; description = '' IP address of the controller. Will be used to make a /etc/hosts entry to make the controller available via "controller". From 5717263d3535f20032d7307d3594a186208bda75 Mon Sep 17 00:00:00 2001 From: Paul Kroeher Date: Tue, 8 Sep 2026 11:51:15 +0200 Subject: [PATCH 53/71] nix: fix systemd-tmpfiles settings for a production deployment Signed-off-by: Paul Kroeher On-behalf-of: SAP paul.kroeher@sap.com --- modules/controller/cinder.nix | 6 +++--- modules/controller/glance.nix | 6 +++--- modules/controller/nova.nix | 6 +++--- modules/controller/placement.nix | 2 +- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/modules/controller/cinder.nix b/modules/controller/cinder.nix index acf5a8d..5cf1935 100644 --- a/modules/controller/cinder.nix +++ b/modules/controller/cinder.nix @@ -89,21 +89,21 @@ in systemd.tmpfiles.settings = { "10-cinder" = { "/var/lib/cinder/" = { - D = { + d = { user = "cinder"; group = "cinder"; mode = "0755"; }; }; "/var/lib/cinder/volumes" = { - D = { + d = { user = "cinder"; group = "cinder"; mode = "0755"; }; }; "/var/log/cinder/" = { - D = { + d = { user = "cinder"; group = "cinder"; mode = "0755"; diff --git a/modules/controller/glance.nix b/modules/controller/glance.nix index cde00ad..8a9755e 100644 --- a/modules/controller/glance.nix +++ b/modules/controller/glance.nix @@ -85,17 +85,17 @@ in systemd.tmpfiles.settings = { "10-glance" = { "/etc/glance/glance-api.conf" = { - L = { + "L+" = { argument = "${cfg.config}"; }; }; "/etc/glance/glance-api-paste.ini" = { - L = { + "L+" = { argument = "${glance}/etc/glance/glance-api-paste.ini"; }; }; "/etc/glance/schema-image.json" = { - L = { + "L+" = { argument = "${glance}/etc/glance/schema-image.json"; }; }; diff --git a/modules/controller/nova.nix b/modules/controller/nova.nix index 818d26e..756d511 100644 --- a/modules/controller/nova.nix +++ b/modules/controller/nova.nix @@ -122,12 +122,12 @@ in systemd.tmpfiles.settings = { "10-nova" = { "/etc/nova/nova.conf" = { - L = { + "L+" = { argument = "${cfg.config}"; }; }; "/etc/nova/api-paste.ini" = { - L = { + "L+" = { argument = "${cfg.novaPackage}/etc/nova/api-paste.ini"; }; }; @@ -153,7 +153,7 @@ in }; }; "/usr/share/novnc" = { - L = { + "L+" = { argument = "${pkgs.novnc}/share/webapps/novnc"; }; }; diff --git a/modules/controller/placement.nix b/modules/controller/placement.nix index d49a9da..4bd41dc 100644 --- a/modules/controller/placement.nix +++ b/modules/controller/placement.nix @@ -64,7 +64,7 @@ in systemd.tmpfiles.settings = { "10-placement" = { "/etc/placement/placement.conf" = { - L = { + "L+" = { argument = "${cfg.config}"; }; }; From ce865dde0b9759068cdd52c2aa15c9af78dd736d Mon Sep 17 00:00:00 2001 From: Paul Kroeher Date: Tue, 8 Sep 2026 11:52:28 +0200 Subject: [PATCH 54/71] nix: designate update Remove unused options and update default designate pool configuration. Signed-off-by: Paul Kroeher On-behalf-of: SAP paul.kroeher@sap.com --- modules/controller/designate.nix | 80 +++++++++++++------------------- 1 file changed, 33 insertions(+), 47 deletions(-) diff --git a/modules/controller/designate.nix b/modules/controller/designate.nix index 77100e4..84d284e 100644 --- a/modules/controller/designate.nix +++ b/modules/controller/designate.nix @@ -51,37 +51,53 @@ let ''; designatePools = pkgs.writeText "pools.yaml" '' - - name: default-knot - description: External Knot DNS 3 pool + - name: default + description: Designate bind backend pool attributes: {} ns_records: - - hostname: ns1.example.org. + - hostname: osdns.openstack.test. + priority: 2 + - hostname: controller.openstack.test. priority: 1 + # List out the nameservers for this pool. These are the actual BIND servers. + # We use these to verify changes have propagated to all nameservers. nameservers: + # controller bind + - host: ${config.openstack.controllerIP} + port: 53 + # dedicated dns server knot (replace it later) - host: ${cfg.knot.address} port: 53 + # List out the targets for this pool. For BIND, most often, there will be one + # entry for each BIND server. targets: - - type: knot3 - description: External Knot DNS 3 server + - type: bind9 + description: local bind server on controller + + # List out the designate-mdns servers from which BIND servers should + # request zone transfers (AXFRs) from. masters: - - host: ${config.openstack.myIp} + # IP address of controller + - host: ${config.openstack.controllerIP} port: 5354 + + # BIND Configuration options + # in our use case localhost == controller options: - host: ${cfg.knot.address} + host: 127.0.0.1 port: 53 - ssh_bin_path: ${pkgs.openssh}/bin/ssh - ssh_host: ${cfg.knot.sshHost} - ssh_port: ${toString cfg.knot.sshPort} - ssh_user: ${cfg.knot.sshUser} - ssh_identity_file: ${cfg.knot.sshIdentityFile} - ssh_known_hosts_file: ${cfg.knot.sshKnownHostsFile} - knotc_bin_path: ${cfg.knot.knotcBinPath} - confdb_path: /var/lib/knot/confdb - control_socket: /run/knot/knot.sock - template: designate + rndc_host: 127.0.0.1 + rndc_port: 953 + rndc_key_file: /etc/bind/rndc.key + # set relative path so rootwrap works + rndc_bin_path: rndc + + also_notifies: + - host: ${cfg.knot.address} + port: 53 ''; service = command: { @@ -145,36 +161,6 @@ in default = "192.168.200.23"; description = "IP address of the external Knot DNS server."; }; - sshHost = lib.mkOption { - type = lib.types.str; - default = cfg.knot.address; - description = "SSH host used to manage Knot's dynamic zone configuration."; - }; - sshUser = lib.mkOption { - type = lib.types.str; - default = "designate-knot"; - description = "Restricted account used to run knotc on the Knot server."; - }; - sshPort = lib.mkOption { - type = lib.types.port; - default = 22; - description = "SSH port of the external Knot server."; - }; - sshIdentityFile = lib.mkOption { - type = lib.types.str; - default = "/var/lib/designate/.ssh/id_ed25519"; - description = "Runtime path to Designate's SSH private key."; - }; - sshKnownHostsFile = lib.mkOption { - type = lib.types.str; - default = "/etc/ssh/ssh_known_hosts"; - description = "Known-hosts file used to authenticate the Knot server."; - }; - knotcBinPath = lib.mkOption { - type = lib.types.str; - default = "${pkgs.knot-dns}/bin/knotc"; - description = "Path to knotc on the external Knot server."; - }; }; }; From 3491051b6436cb85dfaab9539f03b50128f591c5 Mon Sep 17 00:00:00 2001 From: Paul Kroeher Date: Tue, 8 Sep 2026 13:55:46 +0200 Subject: [PATCH 55/71] nix: fix generated configuration file name Signed-off-by: Paul Kroeher On-behalf-of: SAP paul.kroeher@sap.com --- modules/controller/cinder.nix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/controller/cinder.nix b/modules/controller/cinder.nix index 5cf1935..4643379 100644 --- a/modules/controller/cinder.nix +++ b/modules/controller/cinder.nix @@ -11,7 +11,7 @@ with lib; let cfg = config.cinder; - cinderConf = pkgs.writeText "cinder-api.conf" '' + cinderConf = pkgs.writeText "cinder.conf" '' [DEFAULT] transport_url = rabbit://openstack:openstack@${config.openstack.controllerHostname} auth_strategy = keystone From 977a825fa141e7cb4270e94b7d97120be4cc1ef4 Mon Sep 17 00:00:00 2001 From: Paul Kroeher Date: Tue, 8 Sep 2026 14:12:42 +0200 Subject: [PATCH 56/71] nix: use configuration as my_ip value Signed-off-by: Paul Kroeher On-behalf-of: SAP paul.kroeher@sap.com --- modules/storage/cinder-storage-node.nix | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/storage/cinder-storage-node.nix b/modules/storage/cinder-storage-node.nix index a99caff..80b641c 100644 --- a/modules/storage/cinder-storage-node.nix +++ b/modules/storage/cinder-storage-node.nix @@ -37,7 +37,7 @@ let [DEFAULT] transport_url = rabbit://openstack:openstack@${config.openstack.controllerHostname} auth_strategy = keystone - my_ip = 10.0.0.20 + my_ip = ${config.openstack.storageIP} enabled_backends = lvm volumes_dir = /var/lib/cinder/volumes state_path = /var/lib/cinder @@ -84,7 +84,7 @@ let [DEFAULT] transport_url = rabbit://openstack:openstack@${config.openstack.controllerHostname} auth_strategy = keystone - my_ip = 10.0.0.20 + my_ip = ${config.openstack.storageIP} enabled_backends = nfs volumes_dir = /var/lib/cinder/volumes state_path = /var/lib/cinder From c5a195038477de5d6ba18e41464edddb556ce00b Mon Sep 17 00:00:00 2001 From: Paul Kroeher Date: Tue, 8 Sep 2026 14:14:18 +0200 Subject: [PATCH 57/71] nix: storage module - settings deploy some settings only if this module is not deployed alongside the controller module on the same host. Signed-off-by: Paul Kroeher On-behalf-of: SAP paul.kroeher@sap.com --- modules/storage/cinder-storage-node.nix | 59 ++++++++++++++----------- 1 file changed, 32 insertions(+), 27 deletions(-) diff --git a/modules/storage/cinder-storage-node.nix b/modules/storage/cinder-storage-node.nix index 80b641c..82ac499 100644 --- a/modules/storage/cinder-storage-node.nix +++ b/modules/storage/cinder-storage-node.nix @@ -232,35 +232,40 @@ in cinder ALL = (root) NOPASSWD: ${cinder_env}/bin/cinder-rootwrap ${rootwrapConf} * ''; + # set this attributes only if this storage module is deployed to a different host than the controller + # or: don't set this attributes if this storage module is deployed alongside the controller module systemd.tmpfiles.settings = { - "20-cinder" = { - "/var/lib/cinder/" = { - d = { - user = "cinder"; - group = "cinder"; - mode = "0755"; - }; - }; - "/var/lib/cinder/volumes" = { - d = { - user = "cinder"; - group = "cinder"; - mode = "0755"; - }; - }; - "/var/log/cinder/" = { - d = { - user = "cinder"; - group = "cinder"; - mode = "0755"; - }; - }; - "/etc/cinder/cinder.conf" = { - "L+" = { - argument = "${cfg.config}"; + "20-cinder" = + lib.mkIf + (!(config ? cinder && builtins.isBool config.cinder.enable && config.cinder.enable == true)) + { + "/var/lib/cinder/" = { + d = { + user = "cinder"; + group = "cinder"; + mode = "0755"; + }; + }; + "/var/lib/cinder/volumes" = { + d = { + user = "cinder"; + group = "cinder"; + mode = "0755"; + }; + }; + "/var/log/cinder/" = { + d = { + user = "cinder"; + group = "cinder"; + mode = "0755"; + }; + }; + "/etc/cinder/cinder.conf" = { + "L+" = { + argument = "${cfg.config}"; + }; + }; }; - }; - }; "20-cinder-backend" = if (cfg.backend == "lvm") then # LVM configuration files From 9da47ae61019ce641ad51e125776318145efa794 Mon Sep 17 00:00:00 2001 From: Paul Kroeher Date: Tue, 8 Sep 2026 14:53:49 +0200 Subject: [PATCH 58/71] nix: storage module - move rootwrap configuration into a config option Signed-off-by: Paul Kroeher On-behalf-of: SAP paul.kroeher@sap.com --- modules/storage/cinder-storage-node.nix | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/modules/storage/cinder-storage-node.nix b/modules/storage/cinder-storage-node.nix index 82ac499..af91fd5 100644 --- a/modules/storage/cinder-storage-node.nix +++ b/modules/storage/cinder-storage-node.nix @@ -41,7 +41,7 @@ let enabled_backends = lvm volumes_dir = /var/lib/cinder/volumes state_path = /var/lib/cinder - rootwrap_config = ${rootwrapConf} + rootwrap_config = /etc/cinder/rootwrap.conf glance_api_servers = http://${config.openstack.controllerHostname}:9292 verify_glance_signatures = disabled log_dir = /var/log/cinder @@ -88,7 +88,7 @@ let enabled_backends = nfs volumes_dir = /var/lib/cinder/volumes state_path = /var/lib/cinder - rootwrap_config = ${rootwrapConf} + rootwrap_config = /etc/cinder/rootwrap.conf glance_api_servers = http://${config.openstack.controllerHostname}:9292 verify_glance_signatures = disabled log_dir = /var/log/cinder @@ -209,6 +209,12 @@ in The nfs-server /etc/exports file. ''; }; + rootwrapConf = mkOption { + default = rootwrapConf; + description = '' + Cinder root wrap configuration file. + ''; + }; }; config = { @@ -230,6 +236,7 @@ in security.sudo.enable = true; security.sudo.extraConfig = '' cinder ALL = (root) NOPASSWD: ${cinder_env}/bin/cinder-rootwrap ${rootwrapConf} * + cinder ALL = (root) NOPASSWD: ${cinder_env}/bin/cinder-rootwrap /etc/cinder/rootwrap.conf * ''; # set this attributes only if this storage module is deployed to a different host than the controller @@ -295,6 +302,13 @@ in }; }; }; + "20-cinder-root-wrap" = { + "/etc/cinder/rootwrap.conf" = { + "L+" = { + argument = "${cfg.rootwrapConf}"; + }; + }; + }; }; # start iSCSI target daemon From 4d2036697c27dd517fc3e8445d04afedffb4d03f Mon Sep 17 00:00:00 2001 From: Paul Kroeher Date: Wed, 9 Sep 2026 16:42:57 +0200 Subject: [PATCH 59/71] nix: add option to specify live migration ip address Signed-off-by: Paul Kroeher On-behalf-of: SAP paul.kroeher@sap.com --- modules/generic/global-options.nix | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/modules/generic/global-options.nix b/modules/generic/global-options.nix index 0a77e85..ceee7da 100644 --- a/modules/generic/global-options.nix +++ b/modules/generic/global-options.nix @@ -26,4 +26,12 @@ with lib; My own ip address. ''; }; + + options.openstack.live_migration_inbound_addr = lib.mkOption { + default = "10.100.100.1"; + type = types.str; + description = '' + My own ip address of the migration network. Usually our internal 100G link. + ''; + }; } From f4a0492a656f3c36698fc308c97c0acd41181766 Mon Sep 17 00:00:00 2001 From: Paul Kroeher Date: Tue, 15 Sep 2026 09:49:23 +0200 Subject: [PATCH 60/71] nix: more failure resistant script Signed-off-by: Paul Kroeher On-behalf-of: SAP paul.kroeher@sap.com --- modules/compute/neutron.nix | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/modules/compute/neutron.nix b/modules/compute/neutron.nix index 8eeb800..323a851 100644 --- a/modules/compute/neutron.nix +++ b/modules/compute/neutron.nix @@ -67,12 +67,11 @@ let echo "br-provider didn't exist already. Proceed with basic setup." set -euxo pipefail ovs-vsctl add-br br-provider - ovs-vsctl add-port br-provider ${cfg.providerInterface} fi + ovs-vsctl --may-exist add-port br-provider ${cfg.providerInterface} # enable uplink provider interface ip link set dev ${cfg.providerInterface} up - ''; in From 8afbfa60d585e3165d61a67a6ecffb99eecb018b Mon Sep 17 00:00:00 2001 From: Paul Kroeher Date: Tue, 15 Sep 2026 09:50:01 +0200 Subject: [PATCH 61/71] nix: fix typo and more failsafe setup script Signed-off-by: Paul Kroeher On-behalf-of: SAP paul.kroeher@sap.com --- modules/controller/openstack-controller.nix | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/modules/controller/openstack-controller.nix b/modules/controller/openstack-controller.nix index ffae5cf..8bf6a75 100644 --- a/modules/controller/openstack-controller.nix +++ b/modules/controller/openstack-controller.nix @@ -47,6 +47,7 @@ let databaseSetupScript = pkgs.writeShellScript "database-setup.sh" '' export PATH=${lib.makeBinPath [ pkgs.mariadb ]}:$PATH + set -euxo pipefail # Keystone mariadb -N -e "CREATE DATABASE IF NOT EXISTS keystone;" @@ -291,7 +292,7 @@ let }:$PATH systemctl status neutron-server.service - systemctl status glance-api.server + systemctl status glance-api.service systemctl status uwsgi.service systemctl status cinder-scheduler.service systemctl status nova-api.service From 803ac1c244d668c60040cba42a1f55c5b7f409e8 Mon Sep 17 00:00:00 2001 From: Paul Kroeher Date: Tue, 15 Sep 2026 09:51:43 +0200 Subject: [PATCH 62/71] nix: standardize nfs configuration Use config option storagePath in CI/CD setup script and add guard for nfs-server. Signed-off-by: Paul Kroeher On-behalf-of: SAP paul.kroeher@sap.com --- modules/storage/cinder-storage-node.nix | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/modules/storage/cinder-storage-node.nix b/modules/storage/cinder-storage-node.nix index af91fd5..680e810 100644 --- a/modules/storage/cinder-storage-node.nix +++ b/modules/storage/cinder-storage-node.nix @@ -123,7 +123,7 @@ let ''; cinderDefaultNFSexports = pkgs.writeText "exports" '' - /exports 10.0.0.0/24(rw,no_root_squash,insecure) + ${config.openstack.storagePath} 10.0.0.0/24(rw,no_root_squash,insecure) ''; cinderVolumeSetupScript = pkgs.writeShellScript "cinder-volume-setup.sh" '' @@ -133,20 +133,22 @@ let ] }:$PATH - if [ -e /exports/.cinder-volume-setup-done-dont-delete-me ]; then + set -euxo pipefail + + if [ -e ${config.openstack.storagePath}/.cinder-volume-setup-done-dont-delete-me ]; then echo "cinder volume setup already done. Check content of this script." fi - mkdir /exports + mkdir ${config.openstack.storagePath} mkfs.ext4 -F -m 0 -L cinder /dev/vdb - mount /dev/vdb /exports + mount /dev/vdb ${config.openstack.storagePath} exportfs -rv - rm -rf /exports/lost+found - chown cinder /exports - chgrp cinder /exports + rm -rf ${config.openstack.storagePath}/lost+found + chown cinder ${config.openstack.storagePath} + chgrp cinder ${config.openstack.storagePath} systemctl restart cinder-volume.service - touch /exports/.cinder-volume-setup-done-dont-delete-me + touch ${config.openstack.storagePath}/.cinder-volume-setup-done-dont-delete-me ''; in @@ -346,7 +348,7 @@ in }; }; - services.nfs.server.enable = if (cfg.backend == "lvm") then false else true; + services.nfs.server.enable = cfg.enable && cfg.backend != "lvm"; services.nfs.server.exports = builtins.readFile cfg.exports; # run this service only in CI/CD setups From b7ce1c6c5f63cd817626c9cd77fa3ee2e6f13211 Mon Sep 17 00:00:00 2001 From: Paul Kroeher Date: Tue, 15 Sep 2026 14:42:52 +0200 Subject: [PATCH 63/71] nix: fix linting Signed-off-by: Paul Kroeher On-behalf-of: SAP paul.kroeher@sap.com --- modules/generic/global-options.nix | 2 +- packages/default.nix | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/modules/generic/global-options.nix b/modules/generic/global-options.nix index ceee7da..23f6bdc 100644 --- a/modules/generic/global-options.nix +++ b/modules/generic/global-options.nix @@ -1,4 +1,4 @@ -{ lib, config, ... }: +{ lib, ... }: with lib; { options.openstack.controllerHostname = lib.mkOption { diff --git a/packages/default.nix b/packages/default.nix index 329b0db..681add3 100644 --- a/packages/default.nix +++ b/packages/default.nix @@ -258,8 +258,7 @@ let python3Packages oslo-log oslo-serialization - - ; + ; }; jsonpath-rw-ext = callPackage ./jsonpath-rw-ext.nix { inherit python3Packages; }; keystone = callPackage ./keystone.nix { From 5314c682e92332239308457f3d82d551a0e6703a Mon Sep 17 00:00:00 2001 From: Paul Kroeher Date: Thu, 17 Sep 2026 12:39:54 +0200 Subject: [PATCH 64/71] nix: enable caching in keystone Signed-off-by: Paul Kroeher On-behalf-of: SAP paul.kroeher@sap.com --- modules/controller/keystone.nix | 9 +++++++-- packages/keystone.nix | 5 ++++- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/modules/controller/keystone.nix b/modules/controller/keystone.nix index b9f5617..2d533d8 100644 --- a/modules/controller/keystone.nix +++ b/modules/controller/keystone.nix @@ -57,6 +57,11 @@ let [database] connection = mysql+pymysql://keystone:keystone@${config.openstack.controllerHostname}/keystone + [cache] + enabled = true + backend = dogpile.cache.memcached + memcache_servers = 127.0.0.1:11211 + [token] provider = fernet @@ -101,14 +106,14 @@ in systemd.tmpfiles.settings = { "10-keystone" = { "/var/lib/keystone/" = { - D = { + d = { user = "keystone"; group = "keystone"; mode = "0755"; }; }; "/var/log/keystone/" = { - D = { + d = { user = "keystone"; group = "keystone"; mode = "0755"; diff --git a/packages/keystone.nix b/packages/keystone.nix index a6f2f93..210d6f3 100644 --- a/packages/keystone.nix +++ b/packages/keystone.nix @@ -21,11 +21,13 @@ let bandit bcrypt cryptography + dogpile-cache flask flask-restful freezegun hacking jsonschema + ldap ldappool lxml oauthlib @@ -35,7 +37,7 @@ let pycodestyle pymysql pysaml2 - ldap + python-memcached requests stestr tempest @@ -82,6 +84,7 @@ python3Packages.buildPythonPackage (rec { pymysql pysaml2 python-keystoneclient + python-memcached sqlalchemy webob ]; From 7d45eb5f565f163777d4e408a50ad9848ca879d5 Mon Sep 17 00:00:00 2001 From: Paul Kroeher Date: Thu, 17 Sep 2026 14:36:49 +0200 Subject: [PATCH 65/71] nix: fix keystone urls in all services Signed-off-by: Paul Kroeher On-behalf-of: SAP paul.kroeher@sap.com --- modules/compute/nova.nix | 8 ++++---- modules/controller/cinder.nix | 4 ++-- modules/controller/designate.nix | 4 ++-- modules/controller/glance.nix | 6 +++--- modules/controller/neutron.nix | 6 +++--- modules/controller/nova.nix | 8 ++++---- modules/controller/placement.nix | 4 ++-- modules/storage/cinder-storage-node.nix | 8 ++++---- 8 files changed, 24 insertions(+), 24 deletions(-) diff --git a/modules/compute/nova.nix b/modules/compute/nova.nix index f578cf2..69449a6 100644 --- a/modules/compute/nova.nix +++ b/modules/compute/nova.nix @@ -41,8 +41,8 @@ let api_servers = http://${config.openstack.controllerHostname}:9292 [keystone_authtoken] - www_authenticate_uri = http://${config.openstack.controllerHostname}:5000/ - auth_url = http://${config.openstack.controllerHostname}:5000/ + www_authenticate_uri = http://${config.openstack.controllerHostname}:5000/v3 + auth_url = http://${config.openstack.controllerHostname}:5000/v3 memcached_servers = ${config.openstack.controllerHostname}:11211 auth_type = password project_domain_name = Default @@ -55,7 +55,7 @@ let virt_type = kvm [neutron] - auth_url = http://${config.openstack.controllerHostname}:5000 + auth_url = http://${config.openstack.controllerHostname}:5000/v3 auth_type = password project_domain_name = Default user_domain_name = Default @@ -82,7 +82,7 @@ let [service_user] send_service_user_token = true - auth_url = http://${config.openstack.controllerHostname}:5000/ + auth_url = http://${config.openstack.controllerHostname}:5000/v3 auth_strategy = keystone auth_type = password project_domain_name = Default diff --git a/modules/controller/cinder.nix b/modules/controller/cinder.nix index 4643379..00b1f36 100644 --- a/modules/controller/cinder.nix +++ b/modules/controller/cinder.nix @@ -22,8 +22,8 @@ let connection = mysql+pymysql://cinder:cinder@${config.openstack.controllerHostname}/cinder [keystone_authtoken] - www_authenticate_uri = http://${config.openstack.controllerHostname}:5000 - auth_url = http://${config.openstack.controllerHostname}:5000 + www_authenticate_uri = http://${config.openstack.controllerHostname}:5000/v3 + auth_url = http://${config.openstack.controllerHostname}:5000/v3 memcached_servers = ${config.openstack.controllerHostname}:11211 auth_type = password project_domain_name = default diff --git a/modules/controller/designate.nix b/modules/controller/designate.nix index 84d284e..b08ddd7 100644 --- a/modules/controller/designate.nix +++ b/modules/controller/designate.nix @@ -36,8 +36,8 @@ let threads = 20 [keystone_authtoken] - www_authenticate_uri = http://${config.openstack.controllerHostname}:5000 - auth_url = http://${config.openstack.controllerHostname}:5000 + www_authenticate_uri = http://${config.openstack.controllerHostname}:5000/v3 + auth_url = http://${config.openstack.controllerHostname}:5000/v3 memcached_servers = ${config.openstack.controllerHostname}:11211 auth_type = password project_domain_name = Default diff --git a/modules/controller/glance.nix b/modules/controller/glance.nix index 8a9755e..3453806 100644 --- a/modules/controller/glance.nix +++ b/modules/controller/glance.nix @@ -20,8 +20,8 @@ let connection = mysql+pymysql://glance:glance@${config.openstack.controllerHostname}/glance [keystone_authtoken] - www_authenticate_uri = http://${config.openstack.controllerHostname}:5000 - auth_url = http://${config.openstack.controllerHostname}:5000 + www_authenticate_uri = http://${config.openstack.controllerHostname}:5000/v3 + auth_url = http://${config.openstack.controllerHostname}:5000/v3 memcached_servers = ${config.openstack.controllerHostname}:11211 auth_type = password project_domain_name = Default @@ -40,7 +40,7 @@ let filesystem_store_datadir = /var/lib/glance/images/ [oslo_limit] - auth_url = http://${config.openstack.controllerHostname}:5000 + auth_url = http://${config.openstack.controllerHostname}:5000/v3 auth_type = password user_domain_id = default username = glance diff --git a/modules/controller/neutron.nix b/modules/controller/neutron.nix index 07cb810..4a1d9a6 100644 --- a/modules/controller/neutron.nix +++ b/modules/controller/neutron.nix @@ -40,8 +40,8 @@ let metadata_proxy_shared_secret = neutron_metadata_secret [keystone_authtoken] - www_authenticate_uri = http://${config.openstack.controllerHostname}:5000 - auth_url = http://${config.openstack.controllerHostname}:5000 + www_authenticate_uri = http://${config.openstack.controllerHostname}:5000/v3 + auth_url = http://${config.openstack.controllerHostname}:5000/v3 memcached_servers = ${config.openstack.controllerHostname}:11211 auth_type = password project_domain_name = Default @@ -57,7 +57,7 @@ let service_token_roles = admin [nova] - auth_url = http://${config.openstack.controllerHostname}:5000 + auth_url = http://${config.openstack.controllerHostname}:5000/v3 auth_type = password project_domain_name = Default user_domain_name = Default diff --git a/modules/controller/nova.nix b/modules/controller/nova.nix index 756d511..d22744f 100644 --- a/modules/controller/nova.nix +++ b/modules/controller/nova.nix @@ -29,8 +29,8 @@ let auth_strategy = keystone [keystone_authtoken] - www_authenticate_uri = http://${config.openstack.controllerHostname}:5000/ - auth_url = http://${config.openstack.controllerHostname}:5000/ + www_authenticate_uri = http://${config.openstack.controllerHostname}:5000/v3 + auth_url = http://${config.openstack.controllerHostname}:5000/v3 memcached_servers = ${config.openstack.controllerHostname}:11211 auth_type = password project_domain_name = Default @@ -41,7 +41,7 @@ let [service_user] send_service_user_token = true - auth_url = http://${config.openstack.controllerHostname}:5000/ + auth_url = http://${config.openstack.controllerHostname}:5000/v3 auth_strategy = keystone auth_type = password project_domain_name = Default @@ -77,7 +77,7 @@ let discover_hosts_in_cells_interval = 300 [neutron] - auth_url = http://${config.openstack.controllerHostname}:5000 + auth_url = http://${config.openstack.controllerHostname}:5000/v3 auth_type = password project_domain_name = Default user_domain_name = Default diff --git a/modules/controller/placement.nix b/modules/controller/placement.nix index 4bd41dc..50df1f6 100644 --- a/modules/controller/placement.nix +++ b/modules/controller/placement.nix @@ -16,8 +16,8 @@ let auth_strategy = keystone [keystone_authtoken] - www_authenticate_uri = http://${config.openstack.controllerHostname}:5000 - auth_url = http://${config.openstack.controllerHostname}:5000 + www_authenticate_uri = http://${config.openstack.controllerHostname}:5000/v3 + auth_url = http://${config.openstack.controllerHostname}:5000/v3 auth_type = password project_domain_name = Default user_domain_name = Default diff --git a/modules/storage/cinder-storage-node.nix b/modules/storage/cinder-storage-node.nix index 680e810..e6ba8be 100644 --- a/modules/storage/cinder-storage-node.nix +++ b/modules/storage/cinder-storage-node.nix @@ -53,8 +53,8 @@ let connection = mysql+pymysql://cinder:cinder@${config.openstack.controllerHostname}/cinder [keystone_authtoken] - www_authenticate_uri = http://${config.openstack.controllerHostname}:5000 - auth_url = http://${config.openstack.controllerHostname}:5000 + www_authenticate_uri = http://${config.openstack.controllerHostname}:5000/v3 + auth_url = http://${config.openstack.controllerHostname}:5000/v3 memcached_servers = ${config.openstack.controllerHostname}:11211 auth_type = password project_domain_name = default @@ -97,8 +97,8 @@ let connection = mysql+pymysql://cinder:cinder@${config.openstack.controllerHostname}/cinder [keystone_authtoken] - www_authenticate_uri = http://${config.openstack.controllerHostname}:5000 - auth_url = http://${config.openstack.controllerHostname}:5000 + www_authenticate_uri = http://${config.openstack.controllerHostname}:5000/v3 + auth_url = http://${config.openstack.controllerHostname}:5000/v3 memcached_servers = ${config.openstack.controllerHostname}:11211 auth_type = password project_domain_name = default From fe90f2e02ceabf15035d66eac1a7d5d3d6d4072e Mon Sep 17 00:00:00 2001 From: Paul Kroeher Date: Thu, 17 Sep 2026 16:53:55 +0200 Subject: [PATCH 66/71] nix: set mariadb max_connections to 500 Signed-off-by: Paul Kroeher On-behalf-of: SAP paul.kroeher@sap.com --- modules/controller/generic.nix | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/modules/controller/generic.nix b/modules/controller/generic.nix index 615f8ba..a73156f 100644 --- a/modules/controller/generic.nix +++ b/modules/controller/generic.nix @@ -6,6 +6,11 @@ { services.mysql.enable = true; services.mysql.package = lib.mkDefault pkgs.mariadb; + services.mysql.settings = { + mysqld = { + max_connections = 500; + }; + }; services.rabbitmq = { enable = true; From 60dded9a17145f54ec884db1ee4bce24cd7ef2f6 Mon Sep 17 00:00:00 2001 From: Paul Kroeher Date: Fri, 18 Sep 2026 08:56:35 +0200 Subject: [PATCH 67/71] nix: removed knot3 backend Signed-off-by: Paul Kroeher On-behalf-of: SAP paul.kroeher@sap.com --- modules/knot-designate.nix | 105 -------------------------- packages/designate-knot3-backend.py | 112 ---------------------------- packages/designate.nix | 7 -- 3 files changed, 224 deletions(-) delete mode 100644 modules/knot-designate.nix delete mode 100644 packages/designate-knot3-backend.py diff --git a/modules/knot-designate.nix b/modules/knot-designate.nix deleted file mode 100644 index 9f8db58..0000000 --- a/modules/knot-designate.nix +++ /dev/null @@ -1,105 +0,0 @@ -{ - config, - lib, - pkgs, - ... -}: - -let - cfg = config.services.knot-designate; -in -{ - options.services.knot-designate = { - enable = lib.mkEnableOption "an external Knot DNS secondary for OpenStack Designate"; - - mdnsAddress = lib.mkOption { - type = lib.types.str; - example = "10.0.0.39"; - description = "Address of the Designate MiniDNS server used for IXFR/AXFR."; - }; - - mdnsPort = lib.mkOption { - type = lib.types.port; - default = 5354; - description = "Port of the Designate MiniDNS server."; - }; - - sshUser = lib.mkOption { - type = lib.types.str; - default = "designate-knot"; - description = "Account through which Designate runs knotc."; - }; - - sshPort = lib.mkOption { - type = lib.types.port; - default = 22; - description = "SSH port on which Designate manages Knot."; - }; - - sshAuthorizedKeys = lib.mkOption { - type = lib.types.listOf lib.types.str; - default = [ ]; - description = "SSH public keys allowed to manage Knot for Designate."; - }; - }; - - config = lib.mkIf cfg.enable { - services.knot = { - enable = true; - settings = { - server.listen = [ - "0.0.0.0@53" - "::@53" - ]; - remote.designate-mdns.address = "${cfg.mdnsAddress}@${toString cfg.mdnsPort}"; - acl.designate-mdns = { - address = [ cfg.mdnsAddress ]; - action = [ "notify" ]; - }; - template.designate = { - master = "designate-mdns"; - acl = "designate-mdns"; - storage = "/var/lib/knot/zones"; - semantic-checks = true; - }; - }; - }; - - # Designate adds and removes zones dynamically. The configuration database - # preserves those entries while +nopurge refreshes the declarative base - # configuration on restart. - systemd.services.knot = { - preStart = '' - ${pkgs.knot-dns}/bin/knotc \ - --confdb=/var/lib/knot/confdb \ - conf-import /etc/knot/knot.conf +nopurge - ''; - serviceConfig = { - ExecStart = lib.mkForce "${pkgs.knot-dns}/bin/knotd --confdb=/var/lib/knot/confdb --socket=/run/knot/knot.sock"; - UMask = lib.mkForce "0007"; - }; - }; - - users.groups.${cfg.sshUser} = { }; - users.users.${cfg.sshUser} = { - isSystemUser = true; - group = cfg.sshUser; - extraGroups = [ "knot" ]; - home = "/var/lib/${cfg.sshUser}"; - createHome = true; - shell = pkgs.bashInteractive; - openssh.authorizedKeys.keys = cfg.sshAuthorizedKeys; - }; - - services.openssh = { - enable = true; - ports = [ cfg.sshPort ]; - }; - - networking.firewall.allowedTCPPorts = [ - 53 - cfg.sshPort - ]; - networking.firewall.allowedUDPPorts = [ 53 ]; - }; -} diff --git a/packages/designate-knot3-backend.py b/packages/designate-knot3-backend.py deleted file mode 100644 index 1e05aff..0000000 --- a/packages/designate-knot3-backend.py +++ /dev/null @@ -1,112 +0,0 @@ -# Copyright 2026 CobaltCore contributors -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 - -"""Remote Knot DNS 3 backend for Designate. - -Knot is configured as a secondary for Designate MiniDNS. This backend only -manages Knot's dynamic zone configuration over SSH; record data is transferred -directly from MiniDNS to Knot using IXFR/AXFR. -""" - -import subprocess - -from oslo_concurrency import lockutils -from oslo_log import log as logging - -from designate.backend import base -from designate import exceptions -from designate import utils - - -LOG = logging.getLogger(__name__) - - -class Knot3Backend(base.Backend): - __plugin_name__ = 'knot3' - __backend_status__ = 'untested' - - def __init__(self, target): - super().__init__(target) - - self.ssh = self.options.get('ssh_bin_path', 'ssh') - self.ssh_host = self.options.get('ssh_host', self.host) - self.ssh_port = int(self.options.get('ssh_port', 22)) - self.ssh_user = self.options.get('ssh_user', 'designate-knot') - self.ssh_identity = self.options.get('ssh_identity_file') - self.ssh_known_hosts = self.options.get( - 'ssh_known_hosts_file', '/etc/ssh/ssh_known_hosts') - self.knotc = self.options.get('knotc_bin_path', 'knotc') - self.confdb = self.options.get( - 'confdb_path', '/var/lib/knot/confdb') - self.socket = self.options.get( - 'control_socket', '/run/knot/knot.sock') - self.template = self.options.get('template', 'designate') - - def _execute(self, *args): - command = [ - self.ssh, - '-o', 'BatchMode=yes', - '-p', str(self.ssh_port), - ] - if self.ssh_identity: - command.extend(['-i', self.ssh_identity]) - if self.ssh_known_hosts: - command.extend([ - '-o', 'UserKnownHostsFile=%s' % self.ssh_known_hosts]) - command.extend([ - '--', - '%s@%s' % (self.ssh_user, self.ssh_host), - self.knotc, - # '--confdb=%s' % self.confdb, - '--socket=%s' % self.socket, - ]) - command.extend(args) - - try: - return utils.execute(*command, timeout=self.timeout, run_as_root=False) - except (utils.processutils.ProcessExecutionError, - subprocess.TimeoutExpired) as error: - raise exceptions.Backend(error) - - @lockutils.synchronized('designate-knot3', external=True) - def _change_config(self, action, zone_name): - """Change one zone in Knot's persistent configuration database.""" - self._execute('conf-begin') - try: - item = 'zone[%s]' % zone_name.rstrip('.') - self._execute(action, item) - if action == 'conf-set': - self._execute( - 'conf-set', '%s.template' % item, self.template) - self._execute('conf-commit') - except Exception: - try: - self._execute('conf-abort') - except exceptions.Backend: - LOG.exception('Unable to abort the Knot configuration change') - raise - - def create_zone(self, context, zone): - LOG.debug('Creating zone %s in Knot', zone.name) - self._change_config('conf-set', zone.name) - - def update_zone(self, context, zone): - LOG.debug('Refreshing zone %s in Knot', zone.name) - self._execute('zone-refresh', zone.name.rstrip('.')) - - def delete_zone(self, context, zone, zone_params=None): - LOG.debug('Deleting zone %s from Knot', zone.name) - self._change_config('conf-unset', zone.name) - - # Once the configuration entry is gone, remove the secondary's copy. - # A failed purge does not mean that the zone is still being served. - try: - self._execute( - '--force', 'zone-purge', zone.name.rstrip('.'), '+orphan') - except exceptions.Backend: - LOG.warning('Unable to purge data for Knot zone %s', zone.name) diff --git a/packages/designate.nix b/packages/designate.nix index 022409d..5a81872 100644 --- a/packages/designate.nix +++ b/packages/designate.nix @@ -78,15 +78,8 @@ python3Packages.buildPythonPackage (rec { ]; postPatch = '' - cp ${./designate-knot3-backend.py} designate/backend/impl_knot3.py - substituteInPlace etc/designate/rootwrap.d/bind9.filters \ --replace-fail "/usr/sbin/rndc" "rndc" - - substituteInPlace setup.cfg \ - --replace-fail \ - "infoblox = designate.backend.impl_infoblox:InfobloxBackend" \ - $'infoblox = designate.backend.impl_infoblox:InfobloxBackend\n\tknot3 = designate.backend.impl_knot3:Knot3Backend' ''; propagatedBuildInputs = [ From 598f982c18e2cec3f4b8442658828b1f0ed6db5f Mon Sep 17 00:00:00 2001 From: Paul Kroeher Date: Fri, 18 Sep 2026 08:57:52 +0200 Subject: [PATCH 68/71] nix: run database cleanup in CI/CD Signed-off-by: Paul Kroeher On-behalf-of: SAP paul.kroeher@sap.com --- modules/controller/openstack-controller.nix | 1 + 1 file changed, 1 insertion(+) diff --git a/modules/controller/openstack-controller.nix b/modules/controller/openstack-controller.nix index 8bf6a75..b2a771b 100644 --- a/modules/controller/openstack-controller.nix +++ b/modules/controller/openstack-controller.nix @@ -351,6 +351,7 @@ in path = [ pkgs.mariadb ]; serviceConfig = { Type = "oneshot"; + ExecStartPre = "/root/os-setup/000-database-cleanup.sh"; ExecStart = "/root/os-setup/000-database-setup.sh"; }; }; From dc73faa7b97201e478ea326a59623e53b3b34587 Mon Sep 17 00:00:00 2001 From: Paul Kroeher Date: Fri, 18 Sep 2026 09:00:40 +0200 Subject: [PATCH 69/71] nix: bugifxes storage node Signed-off-by: Paul Kroeher On-behalf-of: SAP paul.kroeher@sap.com --- modules/storage/cinder-storage-node.nix | 2 ++ 1 file changed, 2 insertions(+) diff --git a/modules/storage/cinder-storage-node.nix b/modules/storage/cinder-storage-node.nix index e6ba8be..a339504 100644 --- a/modules/storage/cinder-storage-node.nix +++ b/modules/storage/cinder-storage-node.nix @@ -130,6 +130,7 @@ let export PATH=${ lib.makeBinPath [ pkgs.util-linux + pkgs.nfs-utils ] }:$PATH @@ -137,6 +138,7 @@ let if [ -e ${config.openstack.storagePath}/.cinder-volume-setup-done-dont-delete-me ]; then echo "cinder volume setup already done. Check content of this script." + exit 0 fi mkdir ${config.openstack.storagePath} From 1eb42cec7dd116c7201db20cd848d758326b9e53 Mon Sep 17 00:00:00 2001 From: Paul Kroeher Date: Fri, 18 Sep 2026 09:54:55 +0200 Subject: [PATCH 70/71] nix: add openstack packages into system path Signed-off-by: Paul Kroeher On-behalf-of: SAP paul.kroeher@sap.com --- modules/controller/openstack-controller.nix | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/modules/controller/openstack-controller.nix b/modules/controller/openstack-controller.nix index b2a771b..bf6e90e 100644 --- a/modules/controller/openstack-controller.nix +++ b/modules/controller/openstack-controller.nix @@ -322,6 +322,13 @@ in environment.systemPackages = [ python-openstackclient + cinder + designate + glance + keystone + neutron + nova + placement ]; system.activationScripts.openstack-setup-scripts.text = '' From b1f479da656e363b7ecf319eae791bad4adc2daa Mon Sep 17 00:00:00 2001 From: Paul Kroeher Date: Wed, 23 Sep 2026 11:43:57 +0200 Subject: [PATCH 71/71] nix: fix neutron-openvswitch-agent systemd unit dependency neutron-openvswitch-agent should start after network setup is completed by systemd-netword.service This fixes a runtime race condition during the reboot. Signed-off-by: Paul Kroeher On-behalf-of: SAP paul.kroeher@sap.com --- modules/compute/neutron.nix | 1 + 1 file changed, 1 insertion(+) diff --git a/modules/compute/neutron.nix b/modules/compute/neutron.nix index 323a851..0f6c122 100644 --- a/modules/compute/neutron.nix +++ b/modules/compute/neutron.nix @@ -167,6 +167,7 @@ in after = [ "network.target" "ovsdb.service" + "systemd-networkd.service" ]; wantedBy = [ "multi-user.target" ]; path = with pkgs; [