Skip to main content

Topology & Catalog API

This page covers the three main configuration APIs: Topology (sites, rooms, aisles, racks, devices), Catalog (device and rack templates), and Checks (health check library).

All write operations persist changes to YAML files on disk and reload global backend state automatically.


Topology

Sites

GET /api/sites

Returns all sites with their room lists.

curl http://localhost:8000/api/sites
[
{
"id": "dc1",
"name": "Paris DC1",
"description": "Main datacenter",
"location": {
"lat": 48.8566,
"lon": 2.3522
},
"rooms": ["r001", "r002"]
}
]

POST /api/topology/sites

Creates a new site. The id field is optional — if omitted, one is auto-generated from the name.

curl -X POST http://localhost:8000/api/topology/sites \
-H "Content-Type: application/json" \
-d '{"id": "dc1", "name": "Paris DC1"}'

Request body:

FieldTypeRequiredDescription
idstringnoSite identifier (auto-generated if omitted)
namestringyesHuman-readable site name
descriptionstringnoOptional description
location.latnumbernoLatitude for world map pin
location.lonnumbernoLongitude for world map pin
{
"site": {
"id": "dc1",
"name": "Paris DC1",
"rooms": []
}
}
tip

Supply location.lat and location.lon to make the site appear as a pin on the World Map view.


DELETE /api/topology/sites/{site_id}

Deletes a site and all of its rooms, aisles, racks, and devices. This is irreversible.

curl -X DELETE http://localhost:8000/api/topology/sites/dc1
{
"status": "deleted",
"site_id": "dc1"
}
warning

This cascades through the entire hierarchy: all rooms, aisles, racks, and devices under the site are permanently removed from YAML.


Rooms

GET /api/rooms

Returns all rooms across all sites, including their aisle and rack hierarchy.

curl http://localhost:8000/api/rooms
[
{
"id": "r001",
"name": "Server Room A",
"site_id": "dc1",
"aisles": [
{
"id": "a01",
"name": "Aisle A01",
"racks": [
{ "id": "a01-r01", "name": "Rack A01-R01" },
{ "id": "a01-r02", "name": "Rack A01-R02" }
]
}
],
"standalone_racks": [
{ "id": "standalone-r01", "name": "Standalone Rack" }
]
}
]

GET /api/rooms/{room_id}/layout

Returns the full room object including layout metadata used to render the floor plan (grid dimensions, compass orientation, door position).

curl http://localhost:8000/api/rooms/r001/layout
{
"id": "r001",
"name": "Server Room A",
"site_id": "dc1",
"layout": {
"grid": {
"rows": 6,
"cols": 4
},
"compass": "north",
"door": {
"wall": "south",
"position": 2
}
},
"aisles": ["a01", "a02"],
"standalone_racks": []
}

POST /api/topology/sites/{site_id}/rooms

Creates a new room under the specified site.

curl -X POST http://localhost:8000/api/topology/sites/dc1/rooms \
-H "Content-Type: application/json" \
-d '{"id": "r001", "name": "Server Room A", "description": "Main hall"}'

Request body:

FieldTypeRequiredDescription
idstringnoRoom identifier (auto-generated if omitted)
namestringyesHuman-readable room name
descriptionstringnoOptional description
{
"room": {
"id": "r001",
"name": "Server Room A",
"description": "Main hall",
"aisles": [],
"standalone_racks": []
},
"site_id": "dc1"
}

DELETE /api/topology/rooms/{room_id}

Deletes a room and all aisles, racks, and devices it contains.

curl -X DELETE http://localhost:8000/api/topology/rooms/r001
{
"status": "deleted",
"room_id": "r001"
}

Aisles

POST /api/topology/rooms/{room_id}/aisles/create

Creates one or more aisles in a room in a single request.

curl -X POST http://localhost:8000/api/topology/rooms/r001/aisles/create \
-H "Content-Type: application/json" \
-d '{
"aisles": [
{"id": "a01", "name": "Aisle A01"},
{"name": "Aisle A02"}
]
}'

Request body:

FieldTypeRequiredDescription
aislesarrayyesList of aisle objects to create
aisles[].idstringnoAisle identifier (auto-generated if omitted)
aisles[].namestringyesHuman-readable aisle name
{
"room_id": "r001",
"aisles": [
{ "id": "a01", "name": "Aisle A01", "racks": [] },
{ "id": "a02", "name": "Aisle A02", "racks": [] }
]
}

PUT /api/topology/rooms/{room_id}/aisles

Reorganizes the aisles in a room and assigns racks to each aisle. Use this to reorder aisles or move racks between aisles.

curl -X PUT http://localhost:8000/api/topology/rooms/r001/aisles \
-H "Content-Type: application/json" \
-d '{
"aisles": {
"a01": ["a01-r01", "a01-r02"],
"a02": ["a02-r01"]
}
}'

Request body:

FieldTypeRequiredDescription
aislesobjectyesMap of aisle ID to ordered list of rack IDs
{
"status": "ok",
"room_id": "r001"
}

DELETE /api/topology/aisles/{aisle_id}

Deletes an aisle and all racks and devices it contains.

curl -X DELETE http://localhost:8000/api/topology/aisles/a01
{
"status": "deleted",
"aisle_id": "a01"
}

Racks

PUT /api/topology/aisles/{aisle_id}/racks

Reorders the racks within an aisle. The room_id is required for context.

curl -X PUT http://localhost:8000/api/topology/aisles/a01/racks \
-H "Content-Type: application/json" \
-d '{
"room_id": "r001",
"racks": ["a01-r01", "a01-r02", "a01-r03"]
}'

Request body:

FieldTypeRequiredDescription
room_idstringyesParent room ID
racksarrayyesOrdered list of rack IDs
{
"status": "ok",
"aisle_id": "a01",
"racks": ["a01-r01", "a01-r02", "a01-r03"]
}

GET /api/racks/{rack_id}

Returns a rack with all of its devices, including device templates and instance definitions.

curl http://localhost:8000/api/racks/a01-r01
{
"id": "a01-r01",
"name": "Rack A01-R01",
"u_height": 42,
"template_id": "standard-42u",
"devices": [
{
"id": "compute-blade-01",
"name": "Compute Blade 01",
"template_id": "bs-x440-a5",
"u_position": 1,
"instance": "compute[001-020]"
},
{
"id": "top-of-rack-switch",
"name": "ToR Switch",
"template_id": "qib-36p",
"u_position": 40,
"instance": ["switch01", "switch02"]
}
]
}

POST /api/topology/aisles/{aisle_id}/racks

Creates a new rack in the specified aisle.

curl -X POST http://localhost:8000/api/topology/aisles/a01/racks \
-H "Content-Type: application/json" \
-d '{
"id": "a01-r01",
"name": "Rack A01-R01",
"u_height": 42,
"template_id": "standard-42u"
}'

Request body:

FieldTypeRequiredDescription
idstringnoRack identifier (auto-generated if omitted)
namestringyesHuman-readable rack name
u_heightintegernoRack height in rack units. Default: 42, range: 1–100
template_idstringnoRack template ID for built-in infrastructure (PDUs, HMC, etc.)
{
"rack_id": "a01-r01",
"name": "Rack A01-R01",
"aisle_id": "a01"
}

PUT /api/topology/racks/{rack_id}/template

Assigns or removes a rack template. Pass null as template_id to detach the current template.

# Assign template
curl -X PUT http://localhost:8000/api/topology/racks/a01-r01/template \
-H "Content-Type: application/json" \
-d '{"template_id": "standard-42u"}'

# Remove template
curl -X PUT http://localhost:8000/api/topology/racks/a01-r01/template \
-H "Content-Type: application/json" \
-d '{"template_id": null}'
{
"status": "ok",
"rack_id": "a01-r01",
"template_id": "standard-42u"
}
note

Rack templates define built-in infrastructure like PDUs and HMC cooling modules. Removing a template removes those components from rack views.


Devices

GET /api/racks/{rack_id}/devices/{device_id}

Returns a device with full topology context: the device definition, its resolved template, and parent rack, aisle, room, and site.

curl http://localhost:8000/api/racks/a01-r01/devices/compute-blade-01
{
"device": {
"id": "compute-blade-01",
"name": "Compute Blade 01",
"template_id": "bs-x440-a5",
"u_position": 1,
"instance": "compute[001-020]"
},
"template": {
"id": "bs-x440-a5",
"name": "BullSequana X440 A5",
"type": "server",
"u_height": 10,
"layout": { "type": "grid", "rows": 5, "cols": 4 }
},
"rack": {
"id": "a01-r01",
"name": "Rack A01-R01"
},
"aisle": {
"id": "a01",
"name": "Aisle A01"
},
"room": {
"id": "r001",
"name": "Server Room A"
},
"site": {
"id": "dc1",
"name": "Paris DC1"
}
}

POST /api/topology/racks/{rack_id}/devices

Adds a device to a rack. The instance field maps the device to Prometheus instance labels — it can be a pattern string, an explicit list, or a slot map.

curl -X POST http://localhost:8000/api/topology/racks/a01-r01/devices \
-H "Content-Type: application/json" \
-d '{
"id": "compute-blade-01",
"name": "Compute Blade 01",
"template_id": "bs-x440-a5",
"u_position": 1,
"instance": "compute[001-020]"
}'

Request body:

FieldTypeRequiredDescription
idstringnoDevice identifier (auto-generated if omitted)
namestringyesHuman-readable device name
template_idstringyesDevice template ID
u_positionintegeryesBottom U position in the rack (1 = lowest)
instancestring / array / objectnoPrometheus instance mapping (see below)

instance formats:

// Pattern string — expands to compute001, compute002, ..., compute020
"instance": "compute[001-020]"

// Explicit list
"instance": ["compute001", "compute002", "compute003"]

// Slot map — maps visual slot number to instance name
"instance": { "1": "compute001", "2": "compute002", "3": "compute003" }
{
"status": "ok",
"rack_id": "a01-r01",
"device_id": "compute-blade-01"
}
warning

Returns 400 Bad Request if u_position overlaps with an existing device in the rack. Check the GET /api/racks/{rack_id} response to inspect current placements before adding.


PUT /api/topology/racks/{rack_id}/devices/{device_id}

Moves a device to a new U position within the same rack.

curl -X PUT http://localhost:8000/api/topology/racks/a01-r01/devices/compute-blade-01 \
-H "Content-Type: application/json" \
-d '{"u_position": 5}'

Request body:

FieldTypeRequiredDescription
u_positionintegeryesNew bottom U position in the rack
{
"status": "ok",
"rack_id": "a01-r01",
"device_id": "compute-blade-01",
"u_position": 5
}
warning

Returns 400 Bad Request if the new position overlaps another device. The existing device is not moved on failure.


DELETE /api/topology/racks/{rack_id}/devices/{device_id}

Removes a device from a rack.

curl -X DELETE http://localhost:8000/api/topology/racks/a01-r01/devices/compute-blade-01
{
"status": "ok",
"rack_id": "a01-r01",
"device_id": "compute-blade-01"
}

PUT /api/topology/racks/{rack_id}/devices

Replaces all devices in a rack atomically. Used by the Rack Editor to persist a full drag-and-drop session.

curl -X PUT http://localhost:8000/api/topology/racks/a01-r01/devices \
-H "Content-Type: application/json" \
-d '{
"devices": [
{
"id": "compute-blade-01",
"name": "Compute Blade 01",
"template_id": "bs-x440-a5",
"u_position": 1,
"instance": "compute[001-020]"
},
{
"id": "top-of-rack-switch",
"name": "ToR Switch",
"template_id": "qib-36p",
"u_position": 40,
"instance": ["switch01", "switch02"]
}
]
}'
{
"status": "ok",
"rack_id": "a01-r01",
"devices": 2
}
warning

This replaces the entire device list for the rack. Any device not included in the request body will be deleted. Prefer the individual POST / PUT / DELETE endpoints for surgical changes.


Catalog

The catalog stores device templates, rack templates, and rack component templates. Templates define hardware dimensions, visual layouts, health checks, and metrics — they are referenced by topology devices and racks.

GET /api/catalog

Returns the full catalog with all template types.

curl http://localhost:8000/api/catalog
{
"device_templates": [
{
"id": "bs-x440-a5",
"name": "BullSequana X440 A5",
"type": "server",
"u_height": 10,
"layout": {
"type": "grid",
"rows": 5,
"cols": 4,
"matrix": [[1,2,3,4],[5,6,7,8],[9,10,11,12],[13,14,15,16],[17,18,19,20]]
},
"checks": ["node_up", "ipmi_temp_warn", "ipmi_temp_crit"],
"metrics": ["node_temperature", "node_power"]
}
],
"rack_templates": [
{
"id": "standard-42u",
"name": "Standard 42U Rack",
"u_height": 42,
"infrastructure": {
"rack_components": ["pdu-left", "pdu-right"]
},
"checks": ["pdu_active_power"]
}
],
"rack_component_templates": [
{
"id": "pdu-vertiv-24c",
"name": "Vertiv PDU 24C",
"type": "pdu",
"location": "side",
"u_height": 1,
"checks": ["pdu_active_power", "pdu_current"],
"metrics": ["pdu_active_power", "pdu_current", "pdu_voltage"]
}
]
}

POST /api/catalog/templates

Creates a new template. Set kind to "device", "rack", or "rack_component" to indicate the template type.

curl -X POST http://localhost:8000/api/catalog/templates \
-H "Content-Type: application/json" \
-d '{
"kind": "device",
"template": {
"id": "my-2u-server",
"name": "Generic 2U Server",
"type": "server",
"u_height": 2,
"checks": ["node_up", "ipmi_temp_warn"],
"metrics": ["node_temperature", "node_power"]
}
}'

Request body:

FieldTypeRequiredDescription
kindstringyesTemplate type: "device", "rack", or "rack_component"
template.idstringyesUnique template identifier
template.namestringyesHuman-readable template name
template.typestringyes (device)Device kind: server, switch, storage, pdu, cooling
template.u_heightintegeryesHeight in rack units
template.layoutobjectnoFront-panel grid layout for chassis devices
template.rear_layoutobjectnoRear-panel grid layout
template.checksarraynoList of check IDs applied to devices using this template
template.metricsarraynoList of metric IDs shown on device detail views
{
"status": "ok",
"kind": "device",
"id": "my-2u-server"
}
note

Returns 400 Bad Request if a template with the same id already exists. Use PUT /api/catalog/templates to update an existing template.


PUT /api/catalog/templates

Updates an existing template. The body format is identical to POST. The existing template is replaced in full.

curl -X PUT http://localhost:8000/api/catalog/templates \
-H "Content-Type: application/json" \
-d '{
"kind": "device",
"template": {
"id": "my-2u-server",
"name": "Generic 2U Server (updated)",
"type": "server",
"u_height": 2,
"checks": ["node_up", "ipmi_temp_warn", "ipmi_temp_crit"],
"metrics": ["node_temperature", "node_power", "node_cpu_load"]
}
}'
{
"status": "ok",
"kind": "device",
"id": "my-2u-server"
}

DELETE /api/catalog/templates/device/{template_id}

Deletes a device template by ID.

curl -X DELETE http://localhost:8000/api/catalog/templates/device/my-2u-server
{
"status": "ok",
"deleted": "my-2u-server"
}
warning

Deleting a template that is still referenced by topology devices will cause those devices to lose their template. Always verify topology references before deleting a template.


POST /api/catalog/templates/validate

Validates a template definition without saving it. Useful for editors and CI pipelines to check template correctness before committing.

curl -X POST http://localhost:8000/api/catalog/templates/validate \
-H "Content-Type: application/json" \
-d '{
"kind": "device",
"template": {
"id": "my-2u-server",
"name": "Generic 2U Server",
"type": "server",
"u_height": 2
}
}'

On success:

{
"status": "ok"
}

On failure (HTTP 400):

{
"detail": "Validation error: u_height must be between 1 and 100"
}

Checks

Health checks are defined in YAML files under config/checks/library/. Each check specifies a PromQL expression, evaluation scope, and threshold rules that map metric values to health severities.

GET /api/checks

Returns all checks loaded from the library.

curl http://localhost:8000/api/checks
{
"checks": [
{
"id": "node_up",
"name": "Node Up",
"kind": "server",
"scope": "node",
"expr": "up{job=\"node\", instance=~\"$instances\"}",
"output": "bool",
"rules": [
{ "op": "==", "value": 0, "severity": "CRIT" }
]
},
{
"id": "ipmi_temp_warn",
"name": "IPMI Temperature Warning",
"kind": "server",
"scope": "node",
"expr": "ipmi_temperature_celsius{instance=~\"$instances\"}",
"output": "numeric",
"rules": [
{ "op": ">=", "value": 70, "severity": "WARN" },
{ "op": ">=", "value": 85, "severity": "CRIT" }
]
}
]
}

Check fields:

FieldDescription
idUnique check identifier, referenced by templates
nameHuman-readable label shown in the UI
kindDevice type filter: server, switch, storage, pdu, cooling
scopeEvaluation scope: node, chassis, or rack
exprPromQL expression with placeholders ($instances, $chassis, $racks)
outputResult type: bool (0/1) or numeric (threshold comparison)
rulesOrdered list of threshold rules mapping values to severities

GET /api/checks/files

Returns the list of YAML files that make up the checks library.

curl http://localhost:8000/api/checks/files
{
"files": [
{
"name": "up.yaml",
"path": "config/checks/library/up.yaml",
"relative": "up.yaml"
},
{
"name": "ipmi.yaml",
"path": "config/checks/library/ipmi.yaml",
"relative": "ipmi.yaml"
},
{
"name": "pdu.yaml",
"path": "config/checks/library/pdu.yaml",
"relative": "pdu.yaml"
}
]
}

GET /api/checks/files/{filename}

Reads the raw YAML content of a specific checks file. Used by the Checks Library editor.

curl http://localhost:8000/api/checks/files/up.yaml
{
"name": "up.yaml",
"content": "checks:\n - id: node_up\n name: Node Up\n kind: server\n scope: node\n expr: 'up{job=\"node\", instance=~\"$instances\"}'\n output: bool\n rules:\n - op: \"==\"\n value: 0\n severity: CRIT\n"
}

PUT /api/checks/files/{filename}

Writes a new YAML content to a checks file. The backend validates the YAML structure and check definitions before saving. If validation fails, the file is not modified.

curl -X PUT http://localhost:8000/api/checks/files/up.yaml \
-H "Content-Type: application/json" \
-d '{
"content": "checks:\n - id: node_up\n name: Node Up\n kind: server\n scope: node\n expr: '\''up{job=\"node\", instance=~\"$instances\"}'\''\\n output: bool\n rules:\n - op: \"==\"\n value: 0\n severity: CRIT\n"
}'

Request body:

FieldTypeRequiredDescription
contentstringyesFull YAML content of the checks file
{
"status": "ok",
"name": "up.yaml"
}
warning

Returns 400 Bad Request if the YAML is syntactically invalid or if check definitions fail schema validation (missing required fields, unknown severity values, etc.). The original file is preserved on failure.


POST /api/checks/test

Tests a PromQL expression by substituting variable placeholders and executing the query against Prometheus. Useful for validating expressions in the Checks Library editor before saving.

curl -X POST http://localhost:8000/api/checks/test \
-H "Content-Type: application/json" \
-d '{
"expr": "up{job=\"node\", instance=~\"$instances\"}",
"variables": {
"instances": "compute001|compute002"
}
}'

Request body:

FieldTypeRequiredDescription
exprstringyesPromQL expression with $instances, $chassis, $racks placeholders
variablesobjectyesPlaceholder substitutions (key = placeholder name without $)
{
"expr": "up{job=\"node\", instance=~\"compute001|compute002\"}",
"prometheus": {
"status": "success",
"data": {
"resultType": "vector",
"result": [
{
"metric": { "instance": "compute001", "job": "node" },
"value": [1706745600, "1"]
},
{
"metric": { "instance": "compute002", "job": "node" },
"value": [1706745600, "0"]
}
]
}
}
}
tip

Use this endpoint to verify that your PromQL expression returns the expected shape of data before adding a check to the library. A result with "value": "0" for node_up means the node is reported as down.

Performance

Check test queries bypass the planner cache and execute directly against Prometheus, so they always return fresh data. Do not use this endpoint in automated polling loops.