# NetBox Services Plugin A NetBox plugin for declarative service templates with requirement trees. ## Overview This plugin lets you define **service templates** that specify what NetBox resources a service requires, without attaching any resources to the template itself. Templates contain a **requirement tree** — a stacked, declarative description of what objects are needed (devices, interfaces, IP addresses, etc.) and how to select or allocate them. One or more **services** are then spawned from these templates. When a service is **resolved**, the resolver engine walks the requirement tree, evaluates selectors and allocators against live NetBox data, and creates **resource bindings** that link the service to real NetBox objects. ## Key Concepts - **ServiceTemplate** — Reusable template with a declarative requirement tree stored as JSON (editable as YAML or JSON in the UI). Locked once any service instances are spawned from it. - **Service** — A concrete instance spawned from a ServiceTemplate. Requires a tenant. - **ServiceResourceBinding** — Internal model (hidden from UI) that links a service to a real NetBox object via a generic foreign key. ## Tree Structure The requirement tree is a list of nodes. Each node specifies: | Field | Description | |-------|-------------| | `name` | Unique name for the node | | `object_type` | NetBox model as `app_label.model_name` (e.g. `dcim.device`) | | `quantity` | How many objects to select/allocate (default: 1) | | `selector` | How to find existing objects (see Selector Types) | | `allocation` | How to create new objects (see Allocation Strategies) | | `scope` | `service` (resolve once) or `parent` (resolve per selected parent) | | `children` | List of child nodes (recursive) | A node must have either a `selector` or an `allocation` (not both). ## Selector Types | Type | Description | |------|-------------| | `netbox_filter` | Apply NetBox filter parameters to find existing objects | | `reference` | Resolve a dotted context path (e.g. `parent.site`) | | `prefix` | Find ipam.Prefix objects by filter or reference | ## Allocation Strategies | Strategy | Description | |----------|-------------| | `first_free` | Allocate the first available IP from a resolved prefix | | `specific` | Use a specific object by address or context path | ## Reconciliation When a service is re-resolved, the resolver performs **set reconciliation**: - Objects in the new plan but not currently bound → **added** - Objects currently bound but not in the new plan → **removed** (binding only; the underlying NetBox object is not deleted) - Objects in both → **kept** unchanged For allocation nodes, existing bound objects of the same type are reused before allocating new ones (avoids duplicate IPs on re-resolve). ## Quick Start ```bash cd dev/ podman-compose up -d ``` Access at `http://localhost:8001` (admin/admin). ## Example A service template for "2 routers, each with a management IP from a prefix pool": ```yaml - name: routers object_type: dcim.device quantity: 2 selector: type: netbox_filter filters: role: router scope: service children: - name: mgmt-ip object_type: ipam.ipaddress quantity: 1 allocation: strategy: first_free prefix: type: prefix filters: site: hq tag: mgmt scope: parent ``` ## REST API - `GET/POST /api/plugins/services/service-templates/` — CRUD for templates - `GET/POST /api/plugins/services/services/` — CRUD for services - `POST /api/plugins/services/services//resolve/` — Resolve a service - Body: `{"mode": "dry_run"}` or `{"mode": "commit"}` ## License MIT