Inventory service is a replacement for the older SMD service. It is an OpenCHAMI service developed using Fabrica.
Inventory service implements enough of the APIs on SMD to be a drop in replacement for SMD in OpenCHAMI. The goal is to support enough of the original SMD APIs, such that exiting clients of SMD do not need to change. For example, PCS should work with either SMD or Inventory service. Inventory service is not 100% backward compatible with SMD.
The primary way that Inventory service differs from SMD is that SMD supports discovering nodes. i.e. it makes the redfish calls to the Node’s Management processor and collects all the information about the node. Inventory service does not do this.
The rationales for creating Inventory service are as follows:
Fabrica based services assign a UID to each resource when it is created. SMD allows the user to pick the ID for each resource, with the exception that the ID is limited to the xname format. Inventory assigns UIDs at creation time in the same way as any other fabrica based service. It also enforces uniqueness for the fields that were IDs in SMD; however, it does not require that these IDs be xnames.
| Resource | Unique Spec field |
|---|---|
| Component | Spec.ID |
| ComponentEndpoint | Spec.ID |
| EthernetInterface | Spec.ID |
| Group | Spec.Label |
| Hardware | Spec.ID |
| RedfishEndpoint | Spec.ID |
| ServiceEndpoint | Spec.RedfishType + “-” + Spec.RedfishEndpointID |
The uniqueness of these fields are enforced in the Database schema.
The service can be run in multiple ways
This shows to how to run as a quadlet as is done in the OpenCHAMI Tutorial
Create the file inventory-service.container in /etc/containers/systemd with the following contents
[Unit]
Description=The inventory service container
PartOf=openchami.target
# Don’t start until the network has started
Requires=openchami-internal-network.service openchami-jwt-internal-network.service
After=openchami-internal-network.service openchami-jwt-internal-network.service
# Don’t start until JWKS is ready:
Wants=hydra-gen-jwks.service
After=hydra-gen-jwks.service
[Container]
ContainerName=inventory-service
HostName=inventory
Image=ghcr.io/openchami/inventory-service:0.1.0
# Environment Variables
EnvironmentFile=/etc/openchami/configs/openchami.env
# Secrets
# Secret=
# Networks for the Container to use
Network=openchami-internal.network
# Unsupported by generator options
# Proxy settings
PodmanArgs=--http-proxy=false
[Service]
Restart=alwaysmake buildrm -rf data
mkdir -p data./bin/inventory-service serve --database-url "file:data/inventory-service.db?_fk=1"See the learn by example section in Fabrica
rm -rf data
mkdir -p datadocker run --rm --name inventory-service -d -p 8080:8080 -v $(pwd)/data:/data ghcr.io/openchami/inventory-service:0.1.0fabrica style
curl -s -X GET http://localhost:8080/components | jqSMD style
curl -s -X GET http://localhost:8080/hsm/v2/State/Components | jqStop the inventory container
docker container stop inventory-servicemake build imagerm -rf data
mkdir -p datadocker run --rm --name inventory-service -d -p 8080:8080 -v $(pwd)/data:/data inventory-service:latestSMD style request for the component resource
curl -s -X GET http://localhost:8080/hsm/v2/State/Components | jq{
"Components": []
}Fabrica (OpenCHAMI) style request for the same resource.
curl -s -X GET http://localhost:8080/components | jqnullPost a Component using the SMD style API
curl -s -X POST http://localhost:8080/hsm/v2/State/Components \
-H "Content-Type: application/json" \
-d '{ "Components": [{
"ID": "x3000c0s19b1",
"Type": "NodeBMC",
"State": "Ready",
"Flag": "OK",
"Enabled": true,
"NetType": "Sling",
"Arch": "X86",
"Class": "River"
}]}'curl -s -X GET http://localhost:8080/components | jq[
{
"apiVersion": "v1",
"kind": "Component",
"metadata": {
"name": "x3000c0s19b1",
"uid": "component-673d2bde",
"createdAt": "2026-04-23T18:20:11.798823051Z",
"updatedAt": "2026-04-23T18:20:11.798823051Z"
},
"id": "x3000c0s19b1",
"spec": {
"ID": "x3000c0s19b1",
"Type": "NodeBMC",
"State": "Ready",
"Flag": "OK",
"Enabled": true,
"NetType": "Sling",
"Arch": "X86",
"Class": "River"
},
"status": {
"ready": false
}
}
]curl -s -X GET http://localhost:8080/hsm/v2/State/Components | jq{
"Components": [
{
"ID": "x3000c0s19b1",
"Type": "NodeBMC",
"State": "Ready",
"Flag": "OK",
"Enabled": true,
"NetType": "Sling",
"Arch": "X86",
"Class": "River"
}
]
}POSTing the same resource again
curl -s -X POST http://localhost:8080/hsm/v2/State/Components -H "Content-Type: application/json" -d '{ "Components": [{
"ID": "x3000c0s19b1",
"Type": "NodeBMC",
"State": "Ready",
"Flag": "OK",
"Enabled": true,
"NetType": "Sling",
"Arch": "X86",
"Class": "River"
}]}' | jq{
"error": "failed to save Component: failed to create Component: ent: constraint failed: UNIQUE constraint failed: resources.resource_type, resources.resource_id",
"code": 500
}The error comes directly from the Database and refers to fields in the database not in the json.
In the future we may improve this.
resource_type is the Kind field in the fabrica style API’s output.
resource_id is an internal field. For Components this contains the same value as the ID field.
Stop the inventory container
docker stop inventory-serviceThis shows how to run in the quickstart guide
Replace the SMD Init and Server Containers with the following
services:
###
# Inventory Service Container
###
inventory-service:
image: ghcr.io/openchami/inventory-service:0.1.0
container_name: inventory-service
hostname: inventory
networks:
- internalRunning make test-compare-to-smd starts a docker compose environment with both SMD and the Inventory Service.
It also starts a few instances of the
Redfish Interface Emulator (RIE).
This does does the following
Here is an example of running the parts of this test
make all
make start-inventory-and-smddocker ps --no-trunc --format "table {{.Names}}\t{{.Status}}\t{{.Command}}"NAMES STATUS COMMAND
smd Up 9 seconds (healthy) "/sbin/tini -- /smd"
postgres Up 22 seconds (healthy) "docker-entrypoint.sh postgres"
rf-x0c0s2b0 Up 22 seconds "python3 emulator.py"
rf-x0c0s1b0 Up 22 seconds "python3 emulator.py"
rf-x0c0s3b0 Up 22 seconds "python3 emulator.py"
rf-x0c0s4b0 Up 22 seconds "python3 emulator.py"
inventory-service Up 22 seconds "/usr/local/bin/inventory-service serve --port 8080 --database-url file:/data/inventory.db?_fk=1"Run the tests. SMD will discover the nodes, the resources will be POSTed to the Inventory Service, and then the Resources in SMD and Inventory Service will be compared
docker run --rm -it --network inventory_internal inventory-test:latest=========================================================== test session starts ============================================================
platform linux -- Python 3.14.2, pytest-9.0.2, pluggy-1.6.0
rootdir: /app
collected 1 item
test_compare_to_smd.py . [100%]
============================================================ 1 passed in 20.05s ============================================================The inventory-test:latest image can be used to make curl calls to the Inventory Service and to SMD.
SMD style REST call to Inventory
docker run --rm -t --network inventory_internal inventory-test:latest sh -c 'curl -s -X GET http://inventory:8080/hsm/v2/State/Components' | jq -c '.Components[] | { "ID": .ID, "Type": .Type }'{"ID":"x0c0s1e0","Type":"NodeEnclosure"}
{"ID":"x0c0s1b0n0","Type":"Node"}
{"ID":"x0c0s1b0","Type":"NodeBMC"}
{"ID":"x0c0s3e0","Type":"NodeEnclosure"}
{"ID":"x0c0s3b0n0","Type":"Node"}
{"ID":"x0c0s3b0n1","Type":"Node"}
{"ID":"x0c0s3b0","Type":"NodeBMC"}
{"ID":"x0c0s4e0","Type":"NodeEnclosure"}
{"ID":"x0c0s4b0n0","Type":"Node"}
{"ID":"x0c0s4b0n1","Type":"Node"}
{"ID":"x0c0s4b0","Type":"NodeBMC"}
{"ID":"x0c0s2e0","Type":"NodeEnclosure"}
{"ID":"x0c0s2b0n0","Type":"Node"}
{"ID":"x0c0s2b0n1","Type":"Node"}
{"ID":"x0c0s2b0","Type":"NodeBMC"}OpenCHAMI (Fabrica) style REST call to Inventory
docker run --rm -t --network inventory_internal inventory-test:latest sh -c 'curl -s -X GET http://inventory:8080/components' | jq -c '.[] | { "kind": .kind, "metadata": { "name": .metadata.name }, "spec" : {"ID": .spec.ID, "Type": .spec.Type} }'{"kind":"Component","metadata":{"name":"x0c0s1e0"},"spec":{"ID":"x0c0s1e0","Type":"NodeEnclosure"}}
{"kind":"Component","metadata":{"name":"x0c0s1b0n0"},"spec":{"ID":"x0c0s1b0n0","Type":"Node"}}
{"kind":"Component","metadata":{"name":"x0c0s1b0"},"spec":{"ID":"x0c0s1b0","Type":"NodeBMC"}}
{"kind":"Component","metadata":{"name":"x0c0s3e0"},"spec":{"ID":"x0c0s3e0","Type":"NodeEnclosure"}}
{"kind":"Component","metadata":{"name":"x0c0s3b0n0"},"spec":{"ID":"x0c0s3b0n0","Type":"Node"}}
{"kind":"Component","metadata":{"name":"x0c0s3b0n1"},"spec":{"ID":"x0c0s3b0n1","Type":"Node"}}
{"kind":"Component","metadata":{"name":"x0c0s3b0"},"spec":{"ID":"x0c0s3b0","Type":"NodeBMC"}}
{"kind":"Component","metadata":{"name":"x0c0s4e0"},"spec":{"ID":"x0c0s4e0","Type":"NodeEnclosure"}}
{"kind":"Component","metadata":{"name":"x0c0s4b0n0"},"spec":{"ID":"x0c0s4b0n0","Type":"Node"}}
{"kind":"Component","metadata":{"name":"x0c0s4b0n1"},"spec":{"ID":"x0c0s4b0n1","Type":"Node"}}
{"kind":"Component","metadata":{"name":"x0c0s4b0"},"spec":{"ID":"x0c0s4b0","Type":"NodeBMC"}}
{"kind":"Component","metadata":{"name":"x0c0s2e0"},"spec":{"ID":"x0c0s2e0","Type":"NodeEnclosure"}}
{"kind":"Component","metadata":{"name":"x0c0s2b0n0"},"spec":{"ID":"x0c0s2b0n0","Type":"Node"}}
{"kind":"Component","metadata":{"name":"x0c0s2b0n1"},"spec":{"ID":"x0c0s2b0n1","Type":"Node"}}
{"kind":"Component","metadata":{"name":"x0c0s2b0"},"spec":{"ID":"x0c0s2b0","Type":"NodeBMC"}}REST call to SMD
docker run --rm -t --network inventory_internal inventory-test:latest sh -c 'curl -s -X GET http://smd:27779/hsm/v2/State/Components' | jq -c '.Components[] | { "ID": .ID, "Type": .Type }'make stop-inventory-and-smd| Resource | Inventory (Fabrica Style) | Inventory (SMD Style) |
|---|---|---|
| Component | /components | /hsm/v2/Status/Components |
| ComponentEndpoint | /componentendpoints | /hsm/v2/Inventory/ComponentEndpoints |
| EthernetInterface | /ethernetinterfaces | /hsm/v2/Inventory/EthernetInterfaces |
| Group | /groups | /hsm/v2/groups |
| Hardware | /hardwares | /hsm/v2/Inventory/Hardware |
| RedfishEndpoint | /redfishendpoints | /hsm/v2/Inventory/RedfishEndpoints |
| ServiceEndpoint | /serviceendpoints | /hsm/v2/Inventory/ServiceEndpoints |
| health | /health | /hsm/v2/service/ready |
| liveness | /hsm/v2/service/liveness |