From be83e34f9b6cd2bb4abda704a6b017182f422023 Mon Sep 17 00:00:00 2001 From: Kiara Grouwstra Date: Mon, 27 Oct 2025 10:55:42 +0100 Subject: [PATCH] WIP: add netbox Signed-off-by: Kiara Grouwstra --- .../check/data-model-tf-proxmox/nixosTest.nix | 72 ++++++++++++++++++- deployment/data-model.nix | 60 ++++++++++++++++ deployment/run/tf-netbox-get-ip/main.tf | 17 +++++ deployment/run/tf-netbox-get-ip/tf.nix | 47 ++++++++++++ deployment/run/tf-netbox-get-ip/variables.tf | 0 deployment/run/tf-netbox-store-ips/main.tf | 15 ++++ deployment/run/tf-netbox-store-ips/tf.nix | 47 ++++++++++++ .../run/tf-netbox-store-ips/variables.tf | 9 +++ npins/sources.json | 16 +++++ 9 files changed, 282 insertions(+), 1 deletion(-) create mode 100644 deployment/run/tf-netbox-get-ip/main.tf create mode 100644 deployment/run/tf-netbox-get-ip/tf.nix create mode 100644 deployment/run/tf-netbox-get-ip/variables.tf create mode 100644 deployment/run/tf-netbox-store-ips/main.tf create mode 100644 deployment/run/tf-netbox-store-ips/tf.nix create mode 100644 deployment/run/tf-netbox-store-ips/variables.tf diff --git a/deployment/check/data-model-tf-proxmox/nixosTest.nix b/deployment/check/data-model-tf-proxmox/nixosTest.nix index dd5c25a2..ceda72b1 100644 --- a/deployment/check/data-model-tf-proxmox/nixosTest.nix +++ b/deployment/check/data-model-tf-proxmox/nixosTest.nix @@ -3,10 +3,13 @@ pkgs, modulesPath, sources, + config, ... }: let inherit (pkgs) system; + netboxUser = "netbox"; + netboxPassword = "netbox"; backendPort = builtins.toString 8080; tfBackend = fragment: { address = "http://localhost:${backendPort}/state/${fragment}"; @@ -43,13 +46,45 @@ let vmDatastoreId = "local"; cdDatastoreId = "local"; ipv4Gateway = "192.168.10.1"; - ipv4Address = "192.168.10.236/24"; + # ipv4Address = "192.168.10.236/24"; + ipv4Address = null; ipv6Gateway = ""; ipv6Address = ""; # dynamically get the id from the template upload step templateId = null; }; }).default.tf-proxmox-vm; + inherit + (pkgs.callPackage ../../run { + inherit sources system; + }) + tf-netbox-store-ips + tf-netbox-get-ip + ; + netbox-store-ips = + (lib.evalModules { + modules = [ + { + options = { inherit tf-netbox-store-ips; }; + config.tf-netbox-store-ips = { + httpBackend = tfBackend "proxmox-test/store-ips"; + startAddress = "192.168.10.236/24"; + endAddress = "192.168.10.240/24"; + }; + } + ]; + }).config.tf-netbox-store-ips; + netbox-get-ip = + (lib.evalModules { + modules = [ + { + options = { inherit tf-netbox-get-ip; }; + config.tf-netbox-get-ip = { + httpBackend = tfBackend "proxmox-test/get-ip"; + }; + } + ]; + }).config.tf-netbox-get-ip; in { _class = "nixosTest"; @@ -125,8 +160,11 @@ in pkgs.pve-manager pkgs.openssl pkgs.jq + pkgs.netbox (pkgs.callPackage ../../run/tf-proxmox-template/tf.nix { }) (pkgs.callPackage ../../run/tf-proxmox-vm/tf.nix { }) + (pkgs.callPackage ../../run/tf-netbox-store-ips/tf.nix { }) + (pkgs.callPackage ../../run/tf-netbox-get-ip/tf.nix { }) ]; # needed only when building from deployer @@ -158,9 +196,39 @@ in KMS_KEY = "tsjxw9NjKUBUlzbTnD7orqIAdEmpGYRARvxD51jtY+o="; }; }; + services.netbox = { + enable = true; + # FIXME randomly generate this + secretKeyFile = pkgs.writeText "netbox-secret" "634da8232803a8155a58584d3186127000207e079d600fc10a890e5cd59c2f4b8f0e0654005944d2ce87f5be9c22ceebec66"; + # listenAddress = "[::1]"; + port = 8001; + }; }; extraTestScript = '' + deployer.succeed(""" + DJANGO_SUPERUSER_PASSWORD='${netboxPassword}' netbox-manage createsuperuser --noinput --user '${netboxUser}' --email 'test@domain.tld' >&2 + """) + # FIXME use https + netbox_token = deployer.succeed(""" + resp=$(curl -X POST -H "Content-Type: application/json" -H "Accept: application/json" http://localhost:8001/api/users/tokens/provision/ --data '{"username": "${netboxUser}", "password": "${netboxPassword}" }') + echo "resp: $resp" >&2 + echo $resp | jq .key + """).strip() + deployer.succeed(f""" + export NETBOX_SERVER_URL="localhost:8001" + export NETBOX_API_TOKEN="{netbox_token}" + export NETBOX_ALLOW_INSECURE_HTTPS="true" + # export NETBOX_CA_CERT_FILE="" + ${lib.getExe netbox-store-ips.run} + """) + ipv4 = deployer.succeed(f""" + export NETBOX_SERVER_URL="localhost:8001" + export NETBOX_API_TOKEN="{netbox_token}" + export NETBOX_ALLOW_INSECURE_HTTPS="true" + # export NETBOX_CA_CERT_FILE="" + ${lib.getExe netbox-get-ip.run} | jq -r '.ipv4.value' + """).strip() pve.wait_for_unit("pveproxy.service") assert "running" in pve.succeed("pveproxy status") pve.succeed("mkdir -p /run/pve") @@ -223,6 +291,7 @@ in export PROXMOX_VE_INSECURE="true" export SSL_CERT_FILE=/tmp/pve-ca-bundle.crt export PROXMOX_VE_API_TOKEN="root@pam!template={template_token}" + export TF_VAR_ipv4_address="{ipv4}" ${lib.getExe template-deployment.run} | jq -r '.id.value' """).strip() @@ -233,6 +302,7 @@ in export SSL_CERT_FILE=/tmp/pve-ca-bundle.crt export PROXMOX_VE_API_TOKEN="root@pam!vm={vm_token}" export TF_VAR_template_id="{template_id}" + export TF_VAR_ipv4_address="{ipv4}" ${lib.getExe vm-deployment.run} | jq -r '.ipv4.value[0]' """ diff --git a/deployment/data-model.nix b/deployment/data-model.nix index 3efe927d..e711e421 100644 --- a/deployment/data-model.nix +++ b/deployment/data-model.nix @@ -588,6 +588,66 @@ let }; }); }; + tf-netbox-store-ips = mkOption { + description = "Store a range of IPs in a Netbox instance."; + type = submodule (tf-netbox-store-ips: { + options = { + inherit httpBackend; + startAddress = mkOption { + description = "Start of the IP range."; + type = types.str; + example = "10.0.0.1/24"; + }; + endAddress = mkOption { + description = "End of the IP range."; + type = types.str; + example = "10.0.0.50/24"; + }; + run = mkOption { + type = types.package; + default = + let + inherit (tf-netbox-store-ips.config) + httpBackend + startAddress + endAddress + ; + in + tfApply { + inherit httpBackend; + directory = "tf-netbox-store-ips"; + environment = { + start_address = startAddress; + end_address = endAddress; + }; + }; + }; + }; + }); + }; + tf-netbox-get-ip = mkOption { + description = "Get an available IP from a Netbox instance."; + type = submodule (tf-netbox-get-ip: { + options = { + inherit httpBackend; + run = mkOption { + type = types.package; + default = + let + inherit (tf-netbox-get-ip.config) + httpBackend + ; + in + tfApply { + inherit httpBackend; + directory = "tf-netbox-get-ip"; + environment = { + }; + }; + }; + }; + }); + }; }; in { diff --git a/deployment/run/tf-netbox-get-ip/main.tf b/deployment/run/tf-netbox-get-ip/main.tf new file mode 100644 index 00000000..f0c5284f --- /dev/null +++ b/deployment/run/tf-netbox-get-ip/main.tf @@ -0,0 +1,17 @@ +terraform { + required_providers { + netbox = { + source = "e-breuninger/netbox" + version = "= 5.0.0" + } + } + backend "http" { + } +} + +resource "netbox_available_ip_address" "get_ip" { +} + +output "ipv4" { + value = netbox_available_ip_address.get_ip.ip_address +} diff --git a/deployment/run/tf-netbox-get-ip/tf.nix b/deployment/run/tf-netbox-get-ip/tf.nix new file mode 100644 index 00000000..115d5cbe --- /dev/null +++ b/deployment/run/tf-netbox-get-ip/tf.nix @@ -0,0 +1,47 @@ +# FIXME: use overlays so this gets imported just once? +{ + pkgs, +}: +# FIXME centralize overlays +# XXX using recent revision for https://github.com/NixOS/nixpkgs/pull/447849 +let + sources = import ../../../npins; + mkProvider = + args: + pkgs.terraform-providers.mkProvider ( + { mkProviderFetcher = { repo, ... }: sources.${repo}; } // args + ); +in +( + (pkgs.callPackage "${sources.nixpkgs-unstable}/pkgs/by-name/op/opentofu/package.nix" { }) + .overrideAttrs + (old: rec { + patches = (old.patches or [ ]) ++ [ + # TF with back-end poses a problem for nix: initialization involves both + # mutation (nix: only inside build) and a network call (nix: not inside build) + ../../check/data-model-tf/02-opentofu-sandboxed-init.patch + ]; + # versions > 1.9.0 need go 1.24+ + version = "1.9.0"; + src = pkgs.fetchFromGitHub { + owner = "opentofu"; + repo = "opentofu"; + tag = "v${version}"; + hash = "sha256-e0ZzbQdex0DD7Bj9WpcVI5roh0cMbJuNr5nsSVaOSu4="; + }; + vendorHash = "sha256-fMTbLSeW+pw6GK8/JLZzG2ER90ss2g1FSDX5+f292do="; + }) +).withPlugins + (_: [ + (mkProvider { + owner = "e-breuninger"; + repo = "terraform-provider-netbox"; + rev = "v5.0.0"; + spdx = "MPL-2.0"; + # hash = "sha256-iCaCt8ZbkxCk43QEyj3PeHYuKPCPVU2oQ78aumH/l6k="; + hash = null; + vendorHash = "sha256-Q3H/6mpkWn1Gw0NRMtKtkBRGHjPJZGBFdGwfalyQ4Z0="; + homepage = "https://registry.terraform.io/providers/e-breuninger/netbox"; + provider-source-address = "registry.opentofu.org/e-breuninger/netbox"; + }) + ]) diff --git a/deployment/run/tf-netbox-get-ip/variables.tf b/deployment/run/tf-netbox-get-ip/variables.tf new file mode 100644 index 00000000..e69de29b diff --git a/deployment/run/tf-netbox-store-ips/main.tf b/deployment/run/tf-netbox-store-ips/main.tf new file mode 100644 index 00000000..44f703cd --- /dev/null +++ b/deployment/run/tf-netbox-store-ips/main.tf @@ -0,0 +1,15 @@ +terraform { + required_providers { + netbox = { + source = "e-breuninger/netbox" + version = "= 5.0.0" + } + } + backend "http" { + } +} + +resource "netbox_ip_range" "ips" { + start_address = var.start_address + end_address = var.end_address +} diff --git a/deployment/run/tf-netbox-store-ips/tf.nix b/deployment/run/tf-netbox-store-ips/tf.nix new file mode 100644 index 00000000..115d5cbe --- /dev/null +++ b/deployment/run/tf-netbox-store-ips/tf.nix @@ -0,0 +1,47 @@ +# FIXME: use overlays so this gets imported just once? +{ + pkgs, +}: +# FIXME centralize overlays +# XXX using recent revision for https://github.com/NixOS/nixpkgs/pull/447849 +let + sources = import ../../../npins; + mkProvider = + args: + pkgs.terraform-providers.mkProvider ( + { mkProviderFetcher = { repo, ... }: sources.${repo}; } // args + ); +in +( + (pkgs.callPackage "${sources.nixpkgs-unstable}/pkgs/by-name/op/opentofu/package.nix" { }) + .overrideAttrs + (old: rec { + patches = (old.patches or [ ]) ++ [ + # TF with back-end poses a problem for nix: initialization involves both + # mutation (nix: only inside build) and a network call (nix: not inside build) + ../../check/data-model-tf/02-opentofu-sandboxed-init.patch + ]; + # versions > 1.9.0 need go 1.24+ + version = "1.9.0"; + src = pkgs.fetchFromGitHub { + owner = "opentofu"; + repo = "opentofu"; + tag = "v${version}"; + hash = "sha256-e0ZzbQdex0DD7Bj9WpcVI5roh0cMbJuNr5nsSVaOSu4="; + }; + vendorHash = "sha256-fMTbLSeW+pw6GK8/JLZzG2ER90ss2g1FSDX5+f292do="; + }) +).withPlugins + (_: [ + (mkProvider { + owner = "e-breuninger"; + repo = "terraform-provider-netbox"; + rev = "v5.0.0"; + spdx = "MPL-2.0"; + # hash = "sha256-iCaCt8ZbkxCk43QEyj3PeHYuKPCPVU2oQ78aumH/l6k="; + hash = null; + vendorHash = "sha256-Q3H/6mpkWn1Gw0NRMtKtkBRGHjPJZGBFdGwfalyQ4Z0="; + homepage = "https://registry.terraform.io/providers/e-breuninger/netbox"; + provider-source-address = "registry.opentofu.org/e-breuninger/netbox"; + }) + ]) diff --git a/deployment/run/tf-netbox-store-ips/variables.tf b/deployment/run/tf-netbox-store-ips/variables.tf new file mode 100644 index 00000000..61068fff --- /dev/null +++ b/deployment/run/tf-netbox-store-ips/variables.tf @@ -0,0 +1,9 @@ +variable "start_address" { + description = "Start of the IP range, e.g. 10.0.0.1/24." + type = string +} + +variable "end_address" { + description = "End of the IP range, e.g. 10.0.0.50/24." + type = string +} diff --git a/npins/sources.json b/npins/sources.json index 3f7e07a9..f9806543 100644 --- a/npins/sources.json +++ b/npins/sources.json @@ -206,6 +206,22 @@ "url": "https://github.com/SaumonNet/proxmox-nixos/archive/ce8768f43b4374287cd8b88d8fa9c0061e749d9a.tar.gz", "hash": "116zplxh64wxbq81wsfkmmssjs1l228kvhxfi9d434xd54k6vr35" }, + "terraform-provider-netbox": { + "type": "GitRelease", + "repository": { + "type": "GitHub", + "owner": "e-breuninger", + "repo": "terraform-provider-netbox" + }, + "pre_releases": false, + "version_upper_bound": null, + "release_prefix": null, + "submodules": false, + "version": "v5.0.0", + "revision": "40184568f1e7a626b44d5887d7d298866204733d", + "url": "https://api.github.com/repos/e-breuninger/terraform-provider-netbox/tarball/v5.0.0", + "hash": "1acpzxhvl6mz8fl4smcgy0l2wxkqrwywl13lwfj114svqsvq49l8" + }, "terraform-provider-proxmox": { "type": "Git", "repository": {