Skip to content

Extending The Device Types Library

cani loads device types from three sources, in priority order:

  1. Built-in — embedded types shipped with the binary.
  2. Local directories — YAML files on disk (types_dirs).
  3. Remote git repos — cloned repositories (types_repos).

Types registered first win. If a slug already exists from a higher-priority source, the duplicate is skipped.

Configuration

All type sources are configured in ~/.cani/cani.yml:

# Local directories containing device type YAML files.
# Each directory should have subdirectories like device-types/, rack-types/, etc.
types_dirs: []

# Git repositories to clone for additional device types.
# The default is the netbox-community device-type library.
types_repos:
  - https://github.com/netbox-community/devicetype-library.git

# Whether to git pull on each run (false = clone once, never update).
types_repo_pull: false

Adding A Local Directory

Create a directory with the standard layout and add it to types_dirs:

my-types/
├── device-types/
│   └── my-custom-blade.yaml
├── rack-types/
│   └── my-custom-rack.yaml
├── module-types/
│   └── my-custom-gpu.yaml
└── cable-types/
    └── my-custom-cable.yaml
types_dirs:
  - /home/user/my-types

Types from local directories take priority over remote repos, so you can override community types with local definitions.

Adding A Git Repository

Add a repository URL to types_repos:

types_repos:
  - https://github.com/netbox-community/devicetype-library.git
  - https://github.com/my-org/custom-device-types.git

Repos are cloned into ~/.cani/types/<sanitized-repo-name>/ on first use. Set types_repo_pull: true to pull updates on each run.

Listing Loaded Types

After configuring sources, use the -L flag to verify which types are available:

# List all loaded device types
cani alpha add device -L

# List all loaded rack types
cani alpha add rack -L

# List all loaded module types
cani alpha add module -L

# List all loaded cable types
cani alpha add cable -L

The output includes slugs from all sources (built-in, local, and remote):

rack (2):
NAME                                 SLUG                                           PART NUMBER       SOURCE
-----------------------------------  ---------------------------------------------  ----------------  ----------
HPE 42U 800mmx1200mm G2 Enterprise…  hpe-42u-800mmx1200mm-g2-enterprise-shock-rack  P9K46A            builtin
HPE 48U 800mmx1200mm G2 Enterprise…  hpe-48u-800mmx1200mm-g2-enterprise-shock-rack  P9K58A            builtin

Cabinet (7):
NAME                                 SLUG                                           PART NUMBER       SOURCE
-----------------------------------  ---------------------------------------------  ----------------  ----------
EX2000                               hpe-eia-cabinet                                                  builtin
EX2000                               hpe-ex2000                                                       builtin
EX2500 1 Liquid Cooled Chassis       hpe-ex2500-1-liquid-cooled-chassis                               builtin
EX2500 2 Liquid Cooled Chassis       hpe-ex2500-2-liquid-cooled-chassis                               builtin
EX2500 3 Liquid Cooled Chassis       hpe-ex2500-3-liquid-cooled-chassis                               builtin
EX3000                               hpe-ex3000                                                       builtin
EX4000                               hpe-ex4000                                                       builtin

Use a slug from this list with any add command:

cani alpha add rack hpe-48u-800mmx1200mm-g2-enterprise-shock-rack --auto --accept

Priority Order Example

If the slug my-blade exists in both a local directory and a git repo:

  1. Built-in types load first — no match.
  2. Local types_dirs loads my-blade — registered.
  3. Git types_repos finds my-blade — skipped (already registered).

This lets you fork and customize community types without modifying the upstream repository.

Subdirectory Layout

Each source directory is scanned for these subdirectories:

Subdirectory Type Loaded
device-types/ CaniDeviceType
module-types/ CaniModuleType
cable-types/ CaniCableType
rack-types/ CaniRackType
inventory-types/ CaniFruType

Any .yaml or .yml file in these subdirectories is parsed and registered by slug.

Slugs For Nautobot-Format Libraries

Nautobot-format libraries (for example ones converted from the netbox-community library) may omit slug because Nautobot generates it on import. When a device or module type has no slug, cani derives one from manufacturer and model using the same rule as the Nautobot importer: lowercase "<manufacturer> <model>" and replace each run of non-word characters with -.

manufacturer model derived slug
HPE Aruba 10000-48Y6C Back-to-Front hpe-aruba-10000-48y6c-back-to-front
Juniper EX4100-24T juniper-ex4100-24t

An explicit slug always takes precedence. A file with no slug and without both manufacturer and model is skipped with a warning.

Module types may also name their ports with Nautobot's {module} placeholder, for example HSN {module}/0. When the module is installed in a module bay, cani replaces {module} with the bay's position from the parent device type (or the bay name when it has no position), as Nautobot does. A NIC in bay PCIe1 with position 1 gets port HSN 1/0; moving it to another bay with cani alpha update module --bay renames its ports.

Resolved inventory interface specs retain the original token-bearing names and labels in optional nameTemplate and labelTemplate JSON fields (name_template and label_template in YAML). These portable internal fields survive save/load cycles, including for customized port layouts; only the resolved names and labels are exported. Explicit interface-label edits replace the label template too, so a later module move does not undo the edit or restore a label that was cleared.

Moving a module also updates matching cable endpoint port names and the interface index while preserving interface UUIDs and endpoint ownership. Contradictory or unresolved cable references are not guessed or repaired.

Older inventories without template fields can recover them from a matching library layout. A customized port whose template was already lost remains a literal name rather than guessing which characters were once a placeholder. To restore rebaying for such a port, supply its original template, for example "nameTemplate": "custom-{module}/0", in the inventory interface spec.