Docs
Tutorial

The compute rack

Read the compute rack, a 42U rack built from other designs, and add it and the designs it uses to the catalogue.

The compute rack is a 42U rack for the site you created in A second design. It holds a top-of-rack switch with a 10G uplink module, ten servers with a 10G network card each, and the cables between them: two data cables and a management cable from each server to the switch.

On a NetBox that has none of its types yet, the rack is 271 objects: the rack itself, eleven devices and their modules, their interfaces, power ports, console ports and module bays, 30 cables, and the device types, module types, rack type, roles and manufacturers they need. You write about 60 lines of YAML for it, because the rack is built from other designs.

The designs it uses

The rack uses two device designs from the catalogue: one for the switch and one for each server. Add them first, because the catalogue refuses a design whose parts it doesn't have, and it says so for every place the part is used.

Add this one as devices/Cisco_3850_w_module, the way you added the first two designs:

name: Cisco_3850_w_module
netbox_versions: ["4.6.*", "4.7.*"]
description: Cisco Catalyst 3850-48P with 4x10G Network Module
variables:
  name: {type: netbox_new, model: Device, field: name, description: The device's name in NetBox}
  position: {type: int, min: 1, max: 100, required: false, description: "The lowest rack unit it occupies, when racked"}
  face: {type: netbox_field, model: Device, field: face, required: false, description: Which face of the rack it is mounted on}
  status: {type: netbox_field, model: Device, field: status, description: The device's status}
  serial: {type: netbox_new, model: Device, field: serial, required: false, description: "The serial number, when known"}
  site: {type: netbox_ref, model: Site, description: The site the device stands in}
  rack: {type: netbox_ref, model: Rack, required: false, description: "The rack it is mounted in, when racked"}
infra:
- infra_type: Device
  name: '{{ variables.name }}'
  position: '{{ variables.position }}'
  face: '{{ variables.face }}'
  status: '{{ variables.status }}'
  serial: '{{ variables.serial }}'
  site: '{{ variables.site }}'
  role:
    infra_type: DeviceRole
    name: TOR Switch
    slug: tor-switch
    color: 2196f3
  rack: '{{ variables.rack }}'
  device_type: 'dtl:device-types/Cisco/WS-C3850-48P-E.yaml@fdf12828'
  modules:
  - infra_type: Module
    device: '{{ parent }}'
    module_bay: '{{ nbd.nbt.get_module_bay(parent, 1) }}'
    module_type: 'dtl:module-types/Cisco/C3850-NM-4-10G.yaml@fdf12828'
    module_position: 1

And this one as devices/Dell_R640_w_module:

name: Dell_R640_w_module
netbox_versions: ["4.6.*", "4.7.*"]
description: Dell PowerEdge R640 with Intel X710-DA4 10GbE NIC
variables:
  name: {type: netbox_new, model: Device, field: name, description: The device's name in NetBox}
  position: {type: int, min: 1, max: 100, required: false, description: "The lowest rack unit it occupies, when racked"}
  face: {type: netbox_field, model: Device, field: face, required: false, description: Which face of the rack it is mounted on}
  status: {type: netbox_field, model: Device, field: status, description: The device's status}
  serial: {type: netbox_new, model: Device, field: serial, required: false, description: "The serial number, when known"}
  site: {type: netbox_ref, model: Site, description: The site the device stands in}
  rack: {type: netbox_ref, model: Rack, required: false, description: "The rack it is mounted in, when racked"}
infra:
- infra_type: Device
  name: '{{ variables.name }}'
  position: '{{ variables.position }}'
  face: '{{ variables.face }}'
  status: '{{ variables.status }}'
  serial: '{{ variables.serial }}'
  site: '{{ variables.site }}'
  role:
    infra_type: DeviceRole
    name: Compute Server
    slug: compute-server
    color: 4caf50
  rack: '{{ variables.rack }}'
  device_type: 'dtl:device-types/Dell/PowerEdge-R640.yaml@fdf12828'
  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'
    module_position: PCIE1

Each describes one device with its role, its device type and the module in it. The device type and module type come from the NetBox Device Type Library: dtl:device-types/Dell/PowerEdge-R640.yaml@fdf12828 brings in the PowerEdge R640 exactly as the library defines it at that commit, with its interfaces, power ports and module bays. The @fdf12828 pins the library at one commit, so the design builds the same device type next year as today.

Check the catalogue name as you add each: devices/Dell_R640_w_module must hold the Dell YAML. The catalogue takes whatever you paste under whatever name you give it, and a rack built from the wrong file fails later, at a cable that can't find the port it expects.

The rack

Add this as tutorial/compute_rack:

name: Compute Rack
netbox_versions: ["4.6.*", "4.7.*"]
description: A 42U compute rack in an existing site, with a top-of-rack switch, ten servers and their cabling.
variables:
  rack_name: {type: netbox_new, model: Rack, field: name, description: "The rack's name, such as R1"}
  site_slug: {type: netbox_ref, model: Site, description: "The site the rack stands in, by its slug"}
  status: active
infra:
- infra_type: Rack
  name: '{{ variables.rack_name }}'
  site: '{{ variables.site_slug }}'
  rack_type: dtl:rack-types/APC/AR3350.yaml@fdf12828
  status: '{{ variables.status }}'
  devices:
  - design: nbd:devices/Cisco_3850_w_module
    template_vars:
      name: '{{ variables.rack_name }}-TOR-01'
      position: 42
      face: front
      status: active
      site: '{{ parent.site }}'
      rack: '{{ parent }}'
  - nbd_loop:
      over: {start: 1, end: 10}
      as: i
    design: nbd:devices/Dell_R640_w_module
    template_vars:
      name: '{{ variables.rack_name }}-COMPUTE-{{ loop.i | zeropad }}'
      position: '{{ loop.i }}'
      face: front
      status: active
      site: '{{ parent.site }}'
      rack: '{{ parent }}'
  cables:
  - nbd_loop:
      over: {start: 1, end: 10}
      as: i
    items:
    - infra_type: Cable
      label: '{{ variables.rack_name }}-COMPUTE-{{ loop.i | zeropad }}-DATA-A'
      type: cat6a
      status: connected
      termination_a: "{{ nbd.lookup.endpoint('{{ variables.rack_name }}-COMPUTE-{{ loop.i | zeropad }}', 'Interface', 'Ethernet/PCIE1/1') }}"
      termination_b: "{{ nbd.lookup.endpoint('{{ variables.rack_name }}-TOR-01', 'Interface', 'GigabitEthernet1/0/{{ loop.i * 2 - 1 }}') }}"
    - infra_type: Cable
      label: '{{ variables.rack_name }}-COMPUTE-{{ loop.i | zeropad }}-DATA-B'
      type: cat6a
      status: connected
      termination_a: "{{ nbd.lookup.endpoint('{{ variables.rack_name }}-COMPUTE-{{ loop.i | zeropad }}', 'Interface', 'Ethernet/PCIE1/2') }}"
      termination_b: "{{ nbd.lookup.endpoint('{{ variables.rack_name }}-TOR-01', 'Interface', 'GigabitEthernet1/0/{{ loop.i * 2 }}') }}"
  - nbd_loop:
      over: {start: 1, end: 10}
      as: i
    infra_type: Cable
    label: '{{ variables.rack_name }}-COMPUTE-{{ loop.i | zeropad }}-MGMT'
    type: cat6
    status: connected
    termination_a: "{{ nbd.lookup.endpoint('{{ variables.rack_name }}-COMPUTE-{{ loop.i | zeropad }}', 'Interface', 'iDRAC9') }}"
    termination_b: "{{ nbd.lookup.endpoint('{{ variables.rack_name }}-TOR-01', 'Interface', 'GigabitEthernet1/0/{{ loop.i + 20 }}') }}"

Reading it from the top:

  • The rack takes two variables you give when you plan, rack_name, a new rack's name, and site_slug, a site your NetBox has, and a default status. The server design's variables say what each is too: a device's name, a rack unit between 1 and 100, a status the device can have, the site and rack it stands in.
  • The rack's type comes from the Device Type Library, like the device types.
  • devices places the switch at the top of the rack and the ten servers below it, each built by its device design. nbd_loop repeats the server ten times, numbering them 01 to 10.
  • cables connects each server's two data ports and its management port to the switch, using a function to find each port.

Each of these has a Concepts page if you want the detail now: Composition, Variables and Functions. The tutorial carries on without them.

Check a design before you add it

The catalogue checks a design's structure and references when you add it, not what it builds. The Add and Edit dialogs go further: as you paste or type, the design is read against the newest NetBox that Designs knows, and an object type or field that NetBox doesn't have is listed under the YAML, with where it is, before you can add it. nbd validate <file> in the CLI does the same offline: its structure, its variables, that every function it calls exists and, given the NetBox version of your target (--netbox-version 4.7.0), that every object type and field is one that NetBox has. See Write your own designs.

Next, deploy the rack into a branch.

On this page