Skip to content

Data Models

netbox-packer defines five Django models. All extend NetBoxModel and support NetBox's standard features (change logging, tags, custom fields, object permissions).


PackerInstallerConfig

An OS-installer configuration file stored verbatim in the database. Used as the content source for a PackerBuild. The checksum field (SHA-256 hex digest) is computed automatically on every save() and written to PackerTemplate.installer_config_checksum_at_build at build time so staleness detection can identify out-of-date templates.

Field Type Notes
name CharField(100)
os_family CharField(20) Choices from OSFamilyChoices
installer_type CharField(20) autoinstall, kickstart, preseed, or cloud_config
content TextField Verbatim installer payload (e.g. the full #cloud-config YAML)
version CharField(40) Default "1.0.0"
checksum CharField(64) SHA-256 hex of content; auto-computed, not editable
description TextField Blank allowed

Unique constraint: (name, version).

installer_type values:

Value Label
autoinstall Cloud-init autoinstall (Ubuntu)
kickstart Anaconda kickstart (RHEL-family)
preseed d-i preseed (Debian)
cloud_config Cloud-config YAML (#cloud-config, Proxmox/generic)

Only cloud_config triggers the proxbox-api cloud-init bake path. All other types are run via packer build.


PackerTemplate

A Proxmox VM template definition with lifecycle tracking. Ties together an installer config, target node/storage, and monitoring-agent injection preferences.

Field Type Default Notes
name CharField(100)
os_family CharField(20) Choices from OSFamilyChoices
os_version CharField(40) e.g. 24.04. The web form renders this as a dropdown grouped by OS family (OS_VERSIONS_BY_FAMILY in choices.py); the model/API stay free-form so automation can send any version.
proxmox_template_id PositiveIntegerField Proxmox VMID for the resulting template
proxmox_endpoint URLField blank Proxmox API URL (used to derive SSH host)
proxmox_node CharField(100) Proxmox node name or IP
storage_pool CharField(100) blank Proxmox storage pool name (default: local)
storage_pool_type CharField(20) blank Choices from StoragePoolTypeChoices
storage_format CharField(10) blank Choices from StorageFormatChoices
cloud_init_ready BooleanField True Template supports cloud-init
min_cpu_type CharField(40) blank Minimum CPU type required
build_status CharField(20) pending pending, building, ready, failed
built_at DateTimeField null Set by PackerBuildJob on success
packer_template_ref CharField(255) blank Path/ref to the HCL2 Packer template file
max_age_days PositiveIntegerField null Trigger staleness after N days
auto_rebuild BooleanField False Auto-rebuild stale templates
description TextField blank
installer_config FK → PackerInstallerConfig null SET_NULL; drives build type
installer_config_checksum_at_build CharField(64) blank Snapshot of checksum at last successful build
base_image_url_at_build URLField(500) blank Read-only resolved URL used by the last successful cloud-image build
base_image_sha256_at_build CharField(64) blank Read-only resolved digest used by the last successful cloud-image build
provisions_service CharField(64) blank Read-only, migration-managed service marker; downstream code follows a VM's source_packer_template lineage to this value

Template form vs. model fields

The template add/edit form intentionally hides machine-managed lifecycle fields (built_at, packer_template_ref, installer_config_checksum_at_build, base_image_url_at_build, base_image_sha256_at_build, and provisions_service) — they are written by PackerBuildJob, not by operators. The base-image snapshots are available read-only through the REST API. The form also renders os_version as an OS-family-grouped dropdown and carries help text on the key cloud-init-template fields (os_version, proxmox_template_id, storage_pool, cloud_init_ready, installer_config).

Monitoring agent injection fields (migrations 0008 and 0023)

These fields control what PackerBuildJob._inject_monitoring_agents() adds to cloud_config content at build time. They have no effect on non-cloud_config installer types.

Field Type Default Notes
install_qemu_guest_agent BooleanField True Inject qemu-guest-agent package + systemctl enable --now runcmd into the cloud-config; skipped if qemu-guest-agent already appears in the installer config's packages list
install_zabbix_agent2 BooleanField True Inject Zabbix Agent 2 bootstrap script into write_files + runcmd; skipped entirely when "zabbix-agent2" appears anywhere in the installer config (e.g. the Zabbix server seed manages its own agent)
zabbix_server CharField(255) "zabbix.nmulti.cloud" ServerActive= value in the injected zabbix_agent2.conf; validated against hostname/IP + optional :port, comma-separated; no spaces or shell metacharacters
install_nms_agent BooleanField False Inject the pinned static NMS host agent, config, and systemd unit; structural deduplication skips only when all managed files and the exact bootstrap command are present, and completes partial state
nms_agent_backend_url URLField "https://backend.nms.nmulti.cloud" HTTPS-only bootstrap/heartbeat/OTLP base URL; rendering also rejects credentials, query strings, and fragments

The Akvorado seed is the first template to set install_nms_agent=True. Its provisions_service="akvorado" marker also causes the injected local RPC allowlist to contain exactly akvorado.service. The injection reuses the agent's secure-prefix bootstrap flow and does not bake a token, signing key, or new trust mechanism.

HCP Packer fields

hcp_bucket_name, hcp_channel_name, hcp_iteration_id, hcp_build_id, hcp_last_synced_at — store HCP Packer registry metadata. All blank by default.

Computed properties

Property Returns
age_days Days since built_at, or None when not yet built
is_stale True when the age policy is exceeded, installer content changed, or the desired base-image pin differs from the successful-build snapshots
derived_vms VirtualMachine queryset where custom_field_data__source_packer_template == self.pk

PackerBuild

A single build-run record for a PackerTemplate. Created by PackerTemplateViewSet.build() and executed asynchronously by PackerBuildJob.

Not a jobs-assignable object type

PackerBuild is not registered as a NetBox jobs-assignable object type. Always enqueue with PackerBuildJob.enqueue(build_id=build.pk) and never pass instance=build. Passing instance= raises "Jobs cannot be assigned to this object type" and the Build button will silently do nothing.

Build triggers must call the shared dispatch_build(build) helper immediately after creating the row and setting the template to build_status="building". If enqueue fails, the helper marks the build failed, appends an error to the build log, and sets the template to failed unless another build remains active. Local Packer subprocesses enforce PACKER_BUILD_TIMEOUT_SECONDS with a watchdog that kills silent packer init / packer build stalls even when no new output arrives.

Field Type Default Notes
template FK → PackerTemplate CASCADE
triggered_by CharField(100) blank Username or trigger source
queued_at DateTimeField auto_now_add
started_at DateTimeField null Set when job starts running
finished_at DateTimeField null Set on success or failure
status CharField(20) queued queued, running, success, failed, cancelled
variable_overrides JSONField {} Per-build overrides (e.g. image_url, ssh_host)
log TextField blank Accumulated build output
exit_code IntegerField null Exit code from packer build or proxbox-api response
result_template_id IntegerField null Proxmox VMID of the completed template
selected_node CharField(100) blank Proxmox node selected by select_build_node()
base_image_url_at_build URLField(500) blank Read-only resolved URL used by this successful cloud-image build
base_image_sha256_at_build CharField(64) blank Read-only resolved digest used by this successful cloud-image build

PackerBuildTarget

A multi-cluster target entry for distributing builds across Proxmox nodes. select_build_node() iterates enabled targets in ascending priority order, skipping nodes at MAX_CONCURRENT_BUILDS_PER_NODE capacity. Falls back to the template's primary node when no targets exist or all targets are exhausted.

Field Type Default Notes
template FK → PackerTemplate CASCADE; related_name="build_targets"
proxmox_endpoint URLField blank Proxmox API URL for this target
proxmox_node CharField(100) Node name or IP
priority PositiveIntegerField 10 Lower = higher priority
enabled BooleanField True Disabled targets are skipped

Unique constraint: (template, proxmox_node).


PackerPluginSettings

Singleton settings row for the plugin. Exactly one row exists; use PackerPluginSettings.get_solo() to retrieve it.

Field Type Default Notes
singleton_key CharField(32) "default" Not editable; forced to "default" on every save()
branching_enabled BooleanField False When True, PackerStalenessCheckJob uses netbox-branching for stale updates
branch_name_prefix CharField(64) "packer-stale" Prefix for auto-created branch names
branch_on_conflict CharField(16) "fail" "fail" or "acknowledge" — behavior on branching merge conflicts
proxbox_api_url URLField blank Base URL of the proxbox-api backend; required for cloud_config builds
proxbox_api_key_encrypted CharField(512) blank Fernet-encrypted API key; not editable directly — use set_proxbox_api_key()
fileserver_package_read_user CharField(255) blank Plaintext username for the File Server image's read-only package index
fileserver_package_read_token_encrypted CharField(512) blank Fernet-encrypted package-read token; not editable directly — use set_fileserver_package_read_token()

Key-management methods

settings_row = PackerPluginSettings.get_solo()

# Store a new API key (encrypts in-place using settings.SECRET_KEY)
settings_row.set_proxbox_api_key("my-secret-key")
settings_row.save()

# Retrieve the decrypted key at job time
api_key = settings_row.get_proxbox_api_key()

# Store and retrieve the File Server package-index credential
settings_row.fileserver_package_read_user = "nms-pkg-reader"
settings_row.set_fileserver_package_read_token("<gitea-package-read-token>")
settings_row.save()
package_read_token = settings_row.get_fileserver_package_read_token()

The Fernet cipher is derived from settings.SECRET_KEY (SHA-256 → base64url). There is no dependency on netbox-nms for key management.