Design proposal for an image_type discriminator on Docker templates plus a compute-side profile registry: vendor parameters graduate from environment markers into schema-gated fields, generic-feature applicability becomes declared capability data, existing markers stay as a compatibility fallback. Explicitly not a new node type. Four profiles already exist to shape it (iol-runner, XRd, SR Linux prototype, FRR appliance); implementation follows the current PR series.
6.7 KiB
Docker Image Types (Vendor Profiles) — Roadmap
Status: proposal, not implemented. This is the agreed direction for the next iteration of vendor Docker support. Four vendor profiles already exist across branches and prototypes — iol-runner (this branch), XRd and the SR Linux prototype (vendor/skip-init family), and the FRR registry appliance — enough evidence to justify the registry. Implement it as a follow-up to the current PR series, shaped by all four rather than by iol-runner alone.
Problem
All Docker nodes share one template type (template_type: "docker") and
one template schema. Vendor images (iol-runner, SR Linux, XRd, …) are
selected and configured today through environment markers — free-form
strings parsed by the compute (GNS3_IOL_RUNNER, GNS3_IOL_MEMORY,
GNS3_IOL_STARTUP_CONFIG, GNS3_SKIP_INIT, GNS3_UNIX_SOCKET_NIO, …).
Three pains have already surfaced:
- Invisible and unvalidated. The markers do not exist in the API
schema: no OpenAPI documentation, no validation, typo or bad value
silently falls back to a default (
GNS3_IOL_MEMORY=notanumber→ 2048). - The shared-schema dilemma. Adding a vendor-specific field to the
Docker template schema pollutes every Docker template with a field only
one vendor reads; not adding it pushes everything into environment
strings. The
startup_configdiscussion (ended in theGNS3_IOL_STARTUP_CONFIGknob) is the canonical example — the tension exists because there is no legitimate discriminator dimension. - Generic-feature applicability is implicit. Which generic Docker
features apply to which image is only documented in code comments:
mac_addressis meaningless under unix-socket NIO,/etc/networkseeding is dead weight for skip-init images,extra_configstargets under persisted volumes get a (correct-but-noisy) shadow warning.
Non-goal: new node types
A template_type/node_type per vendor ("iol", "srlinux", …) is
explicitly rejected. node_type is a top-level concept: it drives compute
module routing (/projects/{id}/{node_type}/nodes), capability
reporting, GUI node types and link handling. N vendor types that are all
Docker underneath would multiply routing and schema surface for zero
mechanism — vendor images are content, not mechanism, and GNS3's
appliance/template system is where content belongs.
Design
One discriminator field on Docker templates
{
"template_type": "docker",
"image_type": "iol-runner",
"image": "iol-xe/iol-xe:17-18-02"
}
image_type is optional; absent (or "generic") keeps today's plain
DockerVM behavior and marker sniffing. The profiles that exist today,
mapped to their current mechanisms and archetypes:
| Profile | Current mechanism | Archetype |
|---|---|---|
| FRR appliance | plain DockerVM: init.sh /etc/network + start_command (frrinit.sh), console on PID 1 |
generic |
| XRd | VendorDockerVM skip-init + GNS3_SHM_SIZE/GNS3_DEVICES, extra_configs, udev masking |
vendor skip-init |
| SR Linux (prototype) | VendorDockerVM skip-init + docker_exec console + GNS3_INTERFACE_NAMES |
vendor skip-init |
| iol-runner | IOLDockerVM: unix-socket NIO, per-start config generation, NVRAM startup-config |
iol-runner |
Possible first values: generic, iol-runner, and a value for the
skip-init vendor NOS family once its common shape settles (XRd and SR
Linux may end up sharing it or splitting — that is exactly what the
registry should decide with all four in front of us).
A compute-side registry
IMAGE_PROFILES = {
"iol-runner": {
"class": IOLDockerVM,
"fields": ("iol_memory", "startup_config"), # schema-gated
"capabilities": {...}, # see below
},
...
}
-
Class selection moves from environment sniffing (
Docker._select_node_class) to the field, with the existing markers kept as a fallback for templates created before the field existed (zero-migration compatibility). -
Vendor parameters graduate into schema fields, gated by pydantic conditional validation (accepted — and validated — only when
image_typematches). This resolves the shared-schema dilemma properly: the field exists, but only means something for its profile. The environment knobs remain as the wire-level compatibility entry. -
Capability declaration makes generic-feature applicability data instead of comments:
Capability plain Docker vendor skip-init iol-runner mac_addresshonoredyes yes no (IOL derives MACs from the app id) /etc/networkseedingyes no no extra_configstargets under volumesshadowed works (real-path binds) works console telnet/http/… docker_execPID 1 stdio (telnet) startup config extra_configsinjectionimage-specific nvram build ( nvram_import)Consumers: error/warning quality (don't warn about inapplicable features), the WebUI (vendor-aware template forms — a bonus, not a driver), and documentation generation.
The existing class hierarchy (DockerVM → VendorDockerVM →
IOLDockerVM) is unchanged — the registry only externalizes selection
and declaration.
Migration & compatibility
- Old templates (markers only) keep working via the fallback; a one-time
optional converter can rewrite markers →
image_type+ fields. - The controller-side materialization conventions
(
GNS3_IOL_STARTUP_CONFIGfile →startup_config_content, sent once) carry over unchanged — the field version references the same content pipeline.
Roadmap steps
- Add
image_typeto the Docker template schema (+ DB column + Alembic migration — see the three-place rule for template fields) and wire class selection through the registry, markers as fallback. - Move iol-runner knobs to gated fields (
iol_memory,startup_config), deprecating-but-supporting the env forms. - Introduce the capability table and use it to silence inapplicable
warnings (
mac_address,/etc/network, extra-config shadowing). - Revisit per-vendor schemas (a sub-model per profile) only if a profile grows more than a handful of fields — not before.
Open questions
- Field name:
image_typevsvendorvsrunner— decide when the second profile lands; the value vocabulary should name the runtime contract, not the vendor. - Should appliances (
.gns3a) carryimage_typeexplicitly, or should installation keep deriving it from the appliance's environment block? - Where the gated vendor fields live long-term: flat on the Docker
template (simple, gated) vs nested
{"image_type": ..., "settings": {...}}(cleaner, more schema churn).