✨ Link Host to VirtualMachine + validate Container image ID#239
Merged
Conversation
## Host ↔ VirtualMachine relationship
- `Host` model: add optional `OneToOneField` to `virtualization.VirtualMachine`
(`SET_NULL`, `related_name="docker_hosts"`). One Host may be linked to at
most one VirtualMachine; the link is optional on both sides.
- Migration `1044_host_virtual_machine`: `AddField` for the new FK.
- `HostTable`: add linkified `virtual_machine` column (available but not in
default columns).
- `HostSerializer` (API): expose `virtual_machine` via `VirtualMachineSerializer`.
- `HostFilterSet` / `filtersets.py`: `virtual_machine_id` filter.
- `HostView` queryset: `prefetch_related` extended with `virtual_machine`.
- `host.html` template: show the linked VM in the Host detail panel.
- `template_content.py` + `virtual_machine_host_table.html`: inject a
"Docker Hosts" table on the VirtualMachine detail page using NetBox's
`TemplateExtension` mechanism.
## Host add / edit form split
- `HostAddForm` (new, `NetBoxModelForm`): used exclusively at creation time.
Fields: `name`, `endpoint`, `virtual_machine`. Tags are suppressed via
`__init__` → `self.fields.pop("tags", None)` so that `TagsMixin.__init__`
still runs cleanly without a `KeyError` at line 143 of `mixins.py`.
- `HostForm` (existing, `NetBoxModelForm`): used for editing. Gains
`virtual_machine` field; keeps `tags`.
- `urls.py`: `hosts/add/` now routes to the new `HostAddView`.
- `HostAddView` (new, `ObjectEditView`): uses `HostAddForm`, always runs the
token-creation logic on POST (no dead `not "pk" in url_kwargs` guard needed).
- `HostEditView`: dead `alter_object` override removed (edit URLs always carry
a pk so the creation branch could never fire).
## Container validation
- `Container.clean()`: reject saving a Container whose Image has no `ImageID`
(i.e. the image has not been pulled yet). Error targets the `image` field:
"Image <name> has no ID yet. Pull or refresh it first."
## Tests
- `test_container_validation.py`:
- `test_that_container_image_must_have_an_id`: expects `ValidationError`
when the image has no `ImageID`.
- `test_that_container_image_with_id_passes_validation`: `clean()` succeeds
when `ImageID` is set. Adds `image1_pulled` fixture (`ImageID="sha256:abc123"`).
- `test_host_virtual_machine.py` (new):
- host without VM is valid (default `None`)
- host with VM is saved and reloaded correctly
- OneToOne constraint raises `IntegrityError` (wrapped in `transaction.atomic()`
savepoint so the test transaction stays intact)
- `SET_NULL` on VM deletion nullifies the host FK
- unlinking sets FK to `None` without deleting either object
- reverse accessor `vm.docker_host` returns the linked Host
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
lvenier
approved these changes
Jun 3, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Host ↔ VirtualMachine relationship
Hostmodel: add optionalOneToOneFieldtovirtualization.VirtualMachine(SET_NULL,related_name="docker_hosts"). One Host may be linked to at most one VirtualMachine; the link is optional on both sides.1044_host_virtual_machine:AddFieldfor the new FK.HostTable: add linkifiedvirtual_machinecolumn (available but not in default columns).HostSerializer(API): exposevirtual_machineviaVirtualMachineSerializer.HostFilterSet/filtersets.py:virtual_machine_idfilter.HostViewqueryset:prefetch_relatedextended withvirtual_machine.host.htmltemplate: show the linked VM in the Host detail panel.template_content.py+virtual_machine_host_table.html: inject a "Docker Hosts" table on the VirtualMachine detail page using NetBox'sTemplateExtensionmechanism.Host add / edit form split
HostAddForm(new,NetBoxModelForm): used exclusively at creation time. Fields:name,endpoint,virtual_machine. Tags are suppressed via__init__→self.fields.pop("tags", None)so thatTagsMixin.__init__still runs cleanly without aKeyErrorat line 143 ofmixins.py.HostForm(existing,NetBoxModelForm): used for editing. Gainsvirtual_machinefield; keepstags.urls.py:hosts/add/now routes to the newHostAddView.HostAddView(new,ObjectEditView): usesHostAddForm, always runs the token-creation logic on POST (no deadnot "pk" in url_kwargsguard needed).HostEditView: deadalter_objectoverride removed (edit URLs always carry a pk so the creation branch could never fire).Container validation
Container.clean(): reject saving a Container whose Image has noImageID(i.e. the image has not been pulled yet). Error targets theimagefield: "Image has no ID yet. Pull or refresh it first."Tests
test_container_validation.py:test_that_container_image_must_have_an_id: expectsValidationErrorwhen the image has noImageID.test_that_container_image_with_id_passes_validation:clean()succeeds whenImageIDis set. Addsimage1_pulledfixture (ImageID="sha256:abc123").test_host_virtual_machine.py(new):None)IntegrityError(wrapped intransaction.atomic()savepoint so the test transaction stays intact)SET_NULLon VM deletion nullifies the host FKNonewithout deleting either objectvm.docker_hostreturns the linked Host