Docs
Reference

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') }}"
ParameterIs
Device nameThe device the port belongs to
KindThe port's object type: Interface, ConsolePort, ConsoleServerPort, PowerPort, PowerOutlet, FrontPort or RearPort
Port nameThe 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) }}'
ParameterIs
Device nameThe 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') }}"
ParameterIs
TypeThe object's type, as infra_type names it, such as Cable or Device
field=value, repeatedThe 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.

ParameterIs
Device nameThe device to find a port on
asThe choice's name on the deployment. Required
typeOnly ports of this interface type, as NetBox names it: 25gbase-x-sfp28
name_likeOnly 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') }}"
ParameterIs
ParentThe prefix to find space in, as text or as a variable holding a prefix
LengthThe prefix length wanted, as a number: 31
asThe choice's name on the deployment. Required
vrfThe 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') }}"
ParameterIs
Object typeThe type, as infra_type names it
Fieldsfield=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'
ParameterIs
DeviceThe device, usually parent
PositionThe 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.

ParameterIs
PathThe library path with its pin, such as dtl:device-types/Dell/PowerEdge-R640.yaml@fdf12828
DeviceOptional. 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.

ParameterIs
PathThe library path with its pin, such as dtl:module-types/Intel/X710-DA4.yaml@fdf12828
Module positionOptional. Fills the {module} placeholder in the module's interface names
DeviceOptional. 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.

ParameterIs
Referencenbd: and the design's catalogue name

On this page