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: $USER
  slug: $USER
  position: $USER_OPTIONAL
  face: $USER_OPTIONAL
  status: $USER
  serial: $USER_OPTIONAL
  site: $USER
  rack: $USER_OPTIONAL
infra:
- infra_type: Device
  name: '{{ variables.name }}'
  slug: '{{ variables.slug }}'
  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: $USER
  slug: $USER
  position: $USER_OPTIONAL
  face: $USER_OPTIONAL
  status: $USER
  serial: $USER_OPTIONAL
  site: $USER
  rack: $USER_OPTIONAL
infra:
- infra_type: Device
  name: '{{ variables.name }}'
  slug: '{{ variables.slug }}'
  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: $USER
  site_slug: $USER
  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'
      slug: '{{ 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 }}'
      slug: '{{ 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 and site_slug, and a default status.
  • 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 references when you add it, not what it builds. nbd validate <file> in the CLI checks the file 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 is one that NetBox has. See Write your own designs.

Next, deploy the rack into a branch.

On this page