Functions
Every function a design can call, with its parameters and an example.
A function is called inside an expression while the design renders. It looks in the design's own objects first, then in what Designs read from the target NetBox. A function that finds nothing refuses the render, saying what it looked for.
The nbd.lookup.* functions find what the design or NetBox already has, and don't allocate: a design that uses them states the names and addresses it wants. The nbd.select.* functions choose: among several things that fit, one, deterministically, and the deployment keeps the choice. See Selectors.
nbd.lookup.endpoint
Finds a port on a device. Cables use it for their ends.
termination_a: "{{ nbd.lookup.endpoint('SW-01', 'Interface', 'GigabitEthernet1/0/1') }}"| Parameter | Is |
|---|---|
| Device name | The device the port belongs to |
| Kind | The port's object type: Interface, ConsolePort, ConsoleServerPort, PowerPort, PowerOutlet, FrontPort or RearPort |
| Port name | The port's name on that device |
nbd.lookup.mgmt_interface
Finds a device's management interface: the interface marked management-only, preferring one whose name starts with mgmt or management when there are several. More than one candidate after that refuses the render.
interface: '{{ nbd.lookup.mgmt_interface(variables.name) }}'| Parameter | Is |
|---|---|
| Device name | The device whose management interface to find |
nbd.lookup.object
Finds any object by its type and the values of its fields.
cable: "{{ nbd.lookup.object('Cable', label='Router to Switch') }}"| Parameter | Is |
|---|---|
| Type | The object's type, as infra_type names it, such as Cable or Device |
field=value, repeated | The values the object must have |
Selectors
A selector finds a value the design doesn't state, because it's a fact about the NetBox at plan time: which port on the switch is free. Where a lookup refuses two matches, a selector chooses one, the same way every time.
What a selector chose is a choice of the deployment, under the name the design gives it with as=. The next plan offers that choice first and keeps it while it still fits, or while what uses it is the deployment's own (the port it chose is now cabled, by this deployment's cable), so a deployment converges on the same port rather than moving each time. A choice that no longer fits is replaced, and the plan says so. The plan lists every choice: server-port: Ethernet1/7 on tor-r12 - chosen on this plan; 11 others free, then kept from the deployment's last apply. Tearing down or releasing the deployment forgets its choices.
A selector is usually called once, in a computed variable, and the variable used wherever the value is needed:
variables:
port: "{{ nbd.select.free_port('tor-r12', as='server-port', type='25gbase-x-sfp28') }}"
infra:
- infra_type: Interface
name: "{{ variables.port.name }}"
device: tor-r12
mode: access
untagged_vlan: "{{ variables.vlan }}"
- infra_type: Cable
status: connected
termination_a: ["{{ variables.port }}"]
termination_b: ["{{ nbd.lookup.endpoint('srv-01', 'Interface', 'eth0') }}"]nbd.select.free_port
Finds a port on a device that nothing uses: not management-only, not a virtual, LAG or bridge interface, with no cable in NetBox or in the design, and no IP address. The lowest-numbered port that fits is taken (Ethernet1/9 before Ethernet1/10). No free port refuses the render, saying how many ports the device has.
| Parameter | Is |
|---|---|
| Device name | The device to find a port on |
as | The choice's name on the deployment. Required |
type | Only ports of this interface type, as NetBox names it: 25gbase-x-sfp28 |
name_like | Only ports whose name matches this pattern, with * and ? as in a shell: Ethernet1/* |
nbd.select.free_prefix
Finds an unused prefix of a given length inside a parent prefix, and gives it as text: 10.0.0.8/31. Unused means no prefix in NetBox or in the design overlaps it, and no IP address falls in it. The first free one in address order is taken. The host(N) filter writes its addresses: {{ variables.link | host(0) }} is 10.0.0.8/31, host(1) is 10.0.0.9/31.
variables:
link: "{{ nbd.select.free_prefix('10.0.0.0/24', 31, as='link') }}"
infra:
- infra_type: Prefix
prefix: "{{ variables.link }}"
status: active
role: point-to-point
- infra_type: IPAddress
address: "{{ variables.link | host(0) }}"
status: active
interface: "{{ nbd.lookup.endpoint('leaf-01', 'Interface', 'Ethernet1/49') }}"
- infra_type: IPAddress
address: "{{ variables.link | host(1) }}"
status: active
interface: "{{ nbd.lookup.endpoint('spine-01', 'Interface', 'Ethernet1/1') }}"| Parameter | Is |
|---|---|
| Parent | The prefix to find space in, as text or as a variable holding a prefix |
| Length | The prefix length wanted, as a number: 31 |
as | The choice's name on the deployment. Required |
vrf | The VRF the parent and its children are in, by name, where the same prefix exists in several |
nbd.select.one
Finds exactly one object by its fields, in the design or NetBox. Two matches refuse the render naming them; none refuses it saying where it looked. A lookup that insists on being sure.
tor: "{{ nbd.select.one('Device', role='tor', rack='R12') }}"| Parameter | Is |
|---|---|
| Object type | The type, as infra_type names it |
| Fields | field=value pairs the object must have. A reference matches by the referenced object's name or slug; present and absent match a reference that is set or not |
nbd.nbt.get_module_bay
Finds a device's module bay by its position. A device type from the Device Type Library creates its bays, and a module goes in one:
modules:
- infra_type: Module
device: '{{ parent }}'
module_bay: "{{ nbd.nbt.get_module_bay(parent, 'PCIE1') }}"
module_type: 'dtl:module-types/Intel/X710-DA4.yaml@fdf12828'| Parameter | Is |
|---|---|
| Device | The device, usually parent |
| Position | The bay's position, such as 1 or PCIE1 |
nbd.dtl.device_type
Brings in a device type from the NetBox Device Type Library, with its manufacturer and the components it defines: interfaces, console ports, console server ports, power ports, power outlets, front and rear ports with the mappings between them, device bays and module bays. A dtl: reference in a device_type field does the same, and is the usual way to write it. See Composition.
A component name in the library file may carry {vc_position}, as a stackable switch's interfaces do. It is filled in as NetBox would fill it: the device's vc_position when the device is in a virtual chassis, else the number after the colon ({vc_position:1} gives 1), else 0. A power outlet's power_port names a power port of the same file, and is filled in the same way, so the outlet is fed from the port it names.
| Parameter | Is |
|---|---|
| Path | The library path with its pin, such as dtl:device-types/Dell/PowerEdge-R640.yaml@fdf12828 |
| Device | Optional. The device the components are for, usually parent |
nbd.dtl.module_type
Brings in a module type from the Device Type Library, with its manufacturer and interfaces. A dtl: reference in a module_type field does the same.
| Parameter | Is |
|---|---|
| Path | The library path with its pin, such as dtl:module-types/Intel/X710-DA4.yaml@fdf12828 |
| Module position | Optional. Fills the {module} placeholder in the module's interface names |
| Device | Optional. The device the module's interfaces belong to |
nbd.design.lookup
Loads a design from the catalogue by its nbd: reference. Including a design with design: nbd:<name> is the usual way to use one; see Composition.
| Parameter | Is |
|---|---|
| Reference | nbd: and the design's catalogue name |