State Models
The State Models feature provides four independent, lifecycle-managed state dimensions for tracking the operational context of Devices and Virtual Machines:
| Model | Applies To | State Values |
|---|---|---|
PowerState |
Device, VirtualMachine | Configurable via StateType / State (catalog keyed to power state) |
OSState |
Device, VirtualMachine | Configurable via StateType / State (catalog keyed to os state) |
RuntimeState |
Device, VirtualMachine | Configurable via StateType / State (catalog keyed to runtime state) |
AllocationState |
Device only | Configurable via StateType / State (catalog keyed to allocation state) |
Every state record is one-per-object: a device or VM can hold at most one record of each state type. Transitions are validated against StateTransitionRule entries, auto-timestamped, and attributed to the actor (user or system) that performed them. The full audit history lives in the NetBox change log.
Breaking change: PowerState now uses the state catalog
As of migration 0017/0018, PowerState.current_state and PowerState.previous_state are FK references to State, not fixed "on"/"off" strings. You must create a StateType named power state with the desired states (e.g. on, off) before creating PowerState records. Existing records are automatically back-filled from the old string values to matching State objects.
Architecture Overview
StateType ←──── State (many states per type)
│ ↑
└─── StateTransitionRule (from_state → to_states)
│
┌────────┴──────────────────────┐
PowerState OSState RuntimeState AllocationState
(device/VM) (device/VM) (device/VM) (device only)
StateTypeis a named category (e.g.power state,os state,runtime state,allocation state). Names are stored lower-case.Stateis one named value within a type (e.g.on,off,running,stopped). Exactly one state per type may be flaggedis_initial.StateTransitionRuledefines which target states are reachable from a given source state. If no rule exists for the current state, the transition is blocked.- All four lifecycle models use FK references to
Staterecords — there are no fixed enum values.
Quick Setup Guide
Before creating any state record, you must configure the state catalog for that lifecycle:
- Create a StateType — e.g.
power state,os state,runtime state,allocation state - Create States for that type — e.g.
on,off; mark oneis_initial = true - Create StateTransitionRules — define which states may follow which
Once configured, the state card on any Device or VM detail page will offer a one-click Create button that initialises the record at the is_initial state.
Minimum setup for PowerState (the most common first step):
# 1. Create the power state StateType
curl -sX POST http://<netbox>/api/plugins/ibm-netbox-plugins/state-types/ \
-H "Authorization: Token <token>" -H "Content-Type: application/json" \
-d '{"name": "power state"}'
# → id: 1
# 2. Create the on/off states (mark "off" as initial)
curl -sX POST http://<netbox>/api/plugins/ibm-netbox-plugins/states/ \
-H "Authorization: Token <token>" -H "Content-Type: application/json" \
-d '[
{"state_type": 1, "name": "off", "color": "9e9e9e", "is_initial": true},
{"state_type": 1, "name": "on", "color": "4caf50", "is_initial": false}
]'
# → ids: 1 (off), 2 (on)
# 3. Create transition rules (on↔off bidirectional)
curl -sX POST http://<netbox>/api/plugins/ibm-netbox-plugins/state-transition-rules/ \
-H "Authorization: Token <token>" -H "Content-Type: application/json" \
-d '{"state_type": 1, "from_state": 1, "to_states": [2]}' # off → on
curl -sX POST http://<netbox>/api/plugins/ibm-netbox-plugins/state-transition-rules/ \
-H "Authorization: Token <token>" -H "Content-Type: application/json" \
-d '{"state_type": 1, "from_state": 2, "to_states": [1]}' # on → off
Common Fields (all state models)
All state models inherit from StateModel, which itself extends NetBoxModel:
| Field | Type | Description |
|---|---|---|
id |
integer | Auto-generated primary key |
transitioned_at |
datetime (auto) | Timestamp of the last state change (auto_now) |
transitioned_by |
string (auto) | Username of the actor; set automatically from the active request |
transition_source |
string | Source of the transition; writable for all state models |
created |
datetime | Record creation timestamp |
last_updated |
datetime | Last DB-write timestamp |
tags |
array | NetBox tags |
custom_fields |
object | NetBox custom fields |
transition_source choices
| Value | Label |
|---|---|
user |
User (default) |
system |
System |
api |
API |
automation |
Automation |
monitoring |
Monitoring |
PowerState
Tracks the power state of a Device or Virtual Machine. Uses the state catalog keyed to StateType name power state.
State names accepted directly — no need to look up IDs
The current_state field accepts either a state name string (e.g. "on", "off") or an integer PK. Use whichever is more convenient. Responses always return the state name string. This applies to all four state models (PowerState, OSState, RuntimeState, AllocationState).
Model Fields
| Field | Type | Writable | Description |
|---|---|---|---|
assigned_object_type |
content type | ✅ create-only | dcim.device or virtualization.virtualmachine |
assigned_object_id |
positive integer | ✅ create-only | PK of the assigned Device or VM |
assigned_object |
nested object | read-only | Resolved Device or VM instance |
current_state |
string or integer | ✅ | State name (e.g. "on") or State PK; must belong to the power state StateType and satisfy the transition rule |
previous_state |
string | read-only | State name of the prior state; set automatically on update; null on first creation |
transitioned_by |
string | read-only | Set automatically from request user |
transition_source |
string | ✅ | user, system, api, automation, or monitoring |
Constraints
- One per object: A Device or VM may have at most one
PowerStaterecord. - State must change:
current_statemust differ from the value already in the database. Sending the same value returns400. - Read-only fields: Passing
previous_stateortransitioned_byin the request body returns400. - Transition rules apply: The
current_statemust satisfy the applicableStateTransitionRulefor thepower statetype. If no rule is defined for the current state, no transitions are allowed. - State catalog required: A
StateTypenamedpower statewith at least one state must exist before creating a PowerState record.
UI
Plugins → IBM NetBox Plugins → Power States
| Page | Description |
|---|---|
| List | Filterable, sortable table of all records |
| Detail | All fields + inline Transition dropdown (HTMX — no page reload) showing allowed next states |
| Add | Select object type, object, and initial state; metadata fields are hidden |
| Bulk Import | CSV import; required columns: assigned_object_type, assigned_object_id, current_state |
| Bulk Edit | Change current_state across multiple records |
| Bulk Delete | Delete multiple records |
The Transition dropdown on the Detail page POSTs to /plugins/ibm-netbox-plugins/power-states/<pk>/transition/.
API Endpoints
Base URL: /api/plugins/ibm-netbox-plugins/power-states/
POST — Create
POST /api/plugins/ibm-netbox-plugins/power-states/
Authorization: Token <token>
Content-Type: application/json
{
"assigned_object_type": "dcim.device",
"assigned_object_id": 191688,
"current_state": "off",
"transition_source": "api"
}
current_state accepts the state name directly. Integer PK is also accepted ("current_state": 1).
201 Created
{
"id": 42,
"url": "http://<netbox>/api/plugins/ibm-netbox-plugins/power-states/42/",
"display_url": "http://<netbox>/api/plugins/ibm-netbox-plugins/power-states/42/",
"display": "dal10-server-01 - off",
"assigned_object_type": "dcim.device",
"assigned_object_id": 191688,
"assigned_object": {
"id": 191688,
"url": "http://<netbox>/api/dcim/devices/191688/",
"display": "dal10-server-01",
"name": "dal10-server-01"
},
"current_state": "off",
"previous_state": null,
"transitioned_by": "admin",
"transition_source": "api",
"transitioned_at": "2025-06-01T10:30:00.000Z",
"tags": [],
"custom_fields": {},
"created": "2025-06-01T10:30:00.000Z",
"last_updated": "2025-06-01T10:30:00.000Z"
}
For a VirtualMachine, change assigned_object_type to "virtualization.virtualmachine".
GET — Retrieve by ID
GET /api/plugins/ibm-netbox-plugins/power-states/42/
Authorization: Token <token>
200 OK — same structure as the POST response above with current field values.
GET — List (with filters)
GET /api/plugins/ibm-netbox-plugins/power-states/?device=191688
Authorization: Token <token>
200 OK
{
"count": 1,
"next": null,
"previous": null,
"results": [
{
"id": 42,
"display": "dal10-server-01 - on",
"assigned_object_type": "dcim.device",
"assigned_object_id": 191688,
"current_state": "on",
"previous_state": "off",
"transitioned_by": "admin",
"transition_source": "api",
"transitioned_at": "2025-06-01T11:00:00.000Z"
}
]
}
Look up PowerState ID before PATCH / DELETE
The URL path {id} refers to the PowerState record's own id, not the device's id. Filter by ?device=<device_id> or ?assigned_object_id=<id> to find the PowerState id first.
PATCH — Transition state
Pass the target state as a name string or integer PK. The value must be allowed by the StateTransitionRule from the currently stored state.
PATCH /api/plugins/ibm-netbox-plugins/power-states/42/
Authorization: Token <token>
Content-Type: application/json
{
"current_state": "on"
}
200 OK — returns the full updated record with previous_state set to the prior value, transitioned_at updated, and transitioned_by set to the request user.
PUT — Full update
PUT /api/plugins/ibm-netbox-plugins/power-states/42/
Authorization: Token <token>
Content-Type: application/json
{
"assigned_object_type": "dcim.device",
"assigned_object_id": 191688,
"current_state": "off"
}
200 OK — same response shape as PATCH.
DELETE — Remove
DELETE /api/plugins/ibm-netbox-plugins/power-states/42/
Authorization: Token <token>
204 No Content — empty body. The Device or VM is not affected.
State Transition Example
Initial creation → current_state: "off", previous_state: null
PATCH "on" (or id=2) → current_state: "on", previous_state: "off"
PATCH "off" (or id=1) → current_state: "off", previous_state: "on"
PATCH "off" → 400 "Current state must differ from previous state."
PATCH "standby" → 400 'No state named "standby" in "power state". Available: off, on.'
PATCH "on" (from "on") → 400 'Invalid transition from "on". Allowed: off.'
PowerState Filter Parameters
| Parameter | Type | Example | Description |
|---|---|---|---|
id |
integer | ?id=42 |
Filter by PowerState record ID |
assigned_object_type |
integer | ?assigned_object_type=12 |
Content-type ID |
assigned_object_id |
integer | ?assigned_object_id=191688 |
PK of Device or VM |
device |
integer | ?device=191688 |
Shorthand — filter by Device ID |
virtual_machine |
integer | ?virtual_machine=5001 |
Shorthand — filter by VM ID |
current_state |
integer (multi) | ?current_state=1¤t_state=2 |
State ID(s) — filter params still use IDs; use ?current_state=<id> |
previous_state |
integer (multi) | ?previous_state=1 |
State ID(s) |
transition_source |
string (multi) | ?transition_source=api |
One or more of user, system, api, automation, monitoring |
transitioned_by |
string | ?transitioned_by=admin |
Username (exact) |
hostname |
string | ?hostname=dal10 |
Case-insensitive substring match on Device/VM hostname |
site |
integer (multi) | ?site=3 |
Site ID; matches both Devices and VMs |
location |
integer (multi) | ?location=7 |
Location ID; matches Devices only |
rack |
integer (multi) | ?rack=22 |
Rack ID; Devices only |
room |
integer (multi) | ?room=9 |
Room ID; Devices only |
q |
string | ?q=dal10 |
Free-text search across state fields + hostnames, sites, locations, racks, rooms |
limit |
integer | ?limit=50 |
Page size |
offset |
integer | ?offset=100 |
Pagination offset |
PowerState Error Reference
| Scenario | HTTP | Field | Message |
|---|---|---|---|
Passing previous_state in request |
400 | previous_state |
This field is set automatically and cannot be provided. |
Passing transitioned_by in request |
400 | transitioned_by |
This field is set automatically and cannot be provided. |
PATCH with same current_state |
400 | current_state |
Current state must differ from previous state. |
| State name not found in catalog | 400 | current_state |
No state named "<name>" in "power state". Available: <names>. |
| State ID not found | 400 | current_state |
No state found with ID <id>. |
| State ID belongs to wrong StateType | 400 | current_state |
State "<name>" (ID <id>) belongs to "<type>", not "power state". |
| Invalid transition (rule disallows it) | 400 | current_state |
Invalid transition from "<from>". Allowed: <states>. |
| Creating second PowerState for same object | 400 | non-field | A power state already exists for this object. Please edit the existing power state instead of creating a new one. |
assigned_object_id does not exist |
400 | assigned_object_id |
No <app>.<model> found with ID <id>. |
assigned_object_type not device or VM |
400 | assigned_object_type |
Invalid content type. |
PowerState id not found |
404 | — | No PowerState matches the given query. |
| Insufficient permissions | 403 | — | You do not have permission to perform this action. |
POST — Upsert by device serial (/device/set/)
This endpoint is designed for automation and monitoring callers that identify devices by serial number rather than NetBox PK. It is idempotent: sending the same state twice is safe and returns 200 without writing to the database.
URL: POST /api/plugins/ibm-netbox-plugins/power-states/device/set/
Request payload
| Field | Required | Type | Description |
|---|---|---|---|
serial |
✅ | string | Device serial number |
current_state |
✅ | string or integer | Target state name (e.g. "on") or State PK |
transition_source |
⬜ | string | One of user, system, api, automation, monitoring. Defaults to "user" |
Fields not accepted in the payload (set automatically):
| Field | Set by |
|---|---|
transitioned_by |
Pre-save signal from request.user.username |
previous_state |
Pre-save signal from the existing DB value |
transitioned_at |
auto_now=True on the model field |
Behaviour
| Scenario | HTTP status | Side effects |
|---|---|---|
| No existing PowerState for this device | 201 Created | Record created |
Existing PowerState, current_state differs |
200 OK | Record updated, previous_state set to prior value |
Existing PowerState, current_state already matches |
200 OK | No write — existing record returned as-is |
Required permissions
| Permission | Branch |
|---|---|
ibm_netbox_plugins.add_powerstate |
Create |
ibm_netbox_plugins.change_powerstate |
Update |
dcim.view_device |
All branches (implied by knowing the serial) |
Example — create
POST /api/plugins/ibm-netbox-plugins/power-states/device/set/
Authorization: Token <token>
Content-Type: application/json
{
"serial": "ABC123XYZ",
"current_state": "off",
"transition_source": "api"
}
201 Created
{
"id": 42,
"url": "http://<netbox>/api/plugins/ibm-netbox-plugins/power-states/42/",
"display": "dal10-server-01 - off",
"assigned_object_type": "dcim.device",
"assigned_object_id": 191688,
"assigned_object": { "id": 191688, "display": "dal10-server-01" },
"current_state": "off",
"previous_state": null,
"transitioned_by": "svc-automation",
"transition_source": "api",
"transitioned_at": "2025-06-01T10:30:00.000Z",
"tags": [],
"custom_fields": {},
"created": "2025-06-01T10:30:00.000Z",
"last_updated": "2025-06-01T10:30:00.000Z"
}
Example — update (transition off → on)
POST /api/plugins/ibm-netbox-plugins/power-states/device/set/
Authorization: Token <token>
Content-Type: application/json
{
"serial": "ABC123XYZ",
"current_state": "on",
"transition_source": "automation"
}
200 OK — same shape as above with current_state: "on", previous_state: "off".
Example — no-op (state already matches)
POST /api/plugins/ibm-netbox-plugins/power-states/device/set/
Authorization: Token <token>
Content-Type: application/json
{
"serial": "ABC123XYZ",
"current_state": "on"
}
200 OK — existing record returned unchanged. No database write occurs.
Error reference
| Scenario | HTTP | Field | Message |
|---|---|---|---|
serial missing |
400 | serial |
This field is required. |
current_state missing |
400 | current_state |
This field is required. |
| No device with that serial | 400 | serial |
No device found with serial '<value>'. |
| Multiple devices with same serial | 400 | serial |
Multiple devices found with serial '<value>'. Use the standard endpoint with assigned_object_id. |
| State name not in catalog | 400 | current_state |
No state named "<name>" in "power state". Available: <names>. |
| State ID not found | 400 | current_state |
No state found with ID <id>. |
| Invalid transition rule | 400 | current_state |
Invalid transition from "<from>". Allowed: <states>. |
Invalid transition_source value |
400 | transition_source |
'<value>' is not a valid choice. Valid choices are: user, system, api, automation, monitoring. |
| Insufficient permissions (create) | 403 | — | Permission denied |
| Insufficient permissions (update) | 403 | — | Permission denied |
| Concurrent create race | 409 | detail |
A power state was created concurrently for this device. Please retry. |
State Catalog (StateType, State, StateTransitionRule)
Before any state records can be created, a state catalog must be defined. These three management models are used together for all four lifecycle types.
The catalog follows a strict dependency order:
StateType → State → StateTransitionRule
You must create them in that order. A State cannot exist without a parent StateType, and a StateTransitionRule cannot exist without both a StateType and the State records it references.
The plugin identifies which catalog drives which lifecycle model by matching StateType.name against a reserved string (case-insensitive):
| Reserved Name (exact, stored lower-case) | Drives |
|---|---|
power state |
PowerState |
os state |
OSState |
runtime state |
RuntimeState |
allocation state |
AllocationState |
StateType
A StateType is a named category that groups a set of related states. It is the top-level container in the state catalog. All State and StateTransitionRule records belong to exactly one StateType.
Names are stored and matched case-insensitively. Creating a StateType with name OS State stores os state.
StateType Fields
| Field | Type | Writable | Description |
|---|---|---|---|
id |
integer | — | Auto PK |
name |
string | ✅ | Unique name; stored lower-case |
tags |
array | ✅ | NetBox tags |
custom_fields |
object | ✅ | NetBox custom fields |
StateType UI
Plugins → IBM NetBox Plugins → State Types
| Page | URL | Description |
|---|---|---|
| List | /plugins/ibm-netbox-plugins/state-types/ |
Table of all state types; columns: Name, State Count, Transition Rule Count |
| Detail | /plugins/ibm-netbox-plugins/state-types/<pk>/ |
Name, all states belonging to this type (name + color badge + is_initial flag), and all transition rules in a table |
| Add | /plugins/ibm-netbox-plugins/state-types/add/ |
Single field: Name |
| Edit | /plugins/ibm-netbox-plugins/state-types/<pk>/edit/ |
Rename the state type |
| Delete | /plugins/ibm-netbox-plugins/state-types/<pk>/delete/ |
Confirmation page; blocked if any states exist under it |
| Bulk Delete | /plugins/ibm-netbox-plugins/state-types/delete/ |
Delete multiple selected types |
Start here
Navigate to State Types first, create the type, then use the Add State button on the detail page to add states inline — the state form pre-fills the state_type field automatically.
StateType API
Base URL: /api/plugins/ibm-netbox-plugins/state-types/
POST — Create a StateType
POST /api/plugins/ibm-netbox-plugins/state-types/
Authorization: Token <token>
Content-Type: application/json
{
"name": "power state"
}
201 Created
{
"id": 1,
"url": "http://<netbox>/api/plugins/ibm-netbox-plugins/state-types/1/",
"display": "power state",
"name": "power state",
"tags": [],
"custom_fields": {},
"created": "2025-06-01T09:00:00.000Z",
"last_updated": "2025-06-01T09:00:00.000Z"
}
GET — List
GET /api/plugins/ibm-netbox-plugins/state-types/
PATCH — Update name
PATCH /api/plugins/ibm-netbox-plugins/state-types/1/
Content-Type: application/json
{ "name": "power state" }
DELETE
DELETE /api/plugins/ibm-netbox-plugins/state-types/1/
204 No Content on success. Deleting a StateType cascades to its State records. If PowerState / OSState / RuntimeState / AllocationState records reference those states, the FK PROTECT constraint will block deletion.
StateType Filter Parameters
| Parameter | Type | Description |
|---|---|---|
q |
string | Free-text search on name |
name |
string | Substring match on name |
StateType Error Reference
| Scenario | HTTP | Field | Message |
|---|---|---|---|
| Duplicate name (case-insensitive) | 400 | name |
A state type with this name already exists. |
State
A State is a single named value within a StateType (e.g. on, off, running, stopped, decommissioned). Each state has an optional color (shown as a colored badge in list and detail views) and an is_initial flag.
is_initial behaviour
- At most one state per
StateTypemay haveis_initial = true. - When a state record (PowerState, OSState, RuntimeState, AllocationState) is created via the quick-create button on a device/VM detail page, the
is_initialstate is assigned automatically ascurrent_state. - If no
is_initialstate exists, the plugin falls back to the first state alphabetically. - Setting
is_initialon a second state in the same type raises a400validation error.
State Fields
| Field | Type | Writable | Description |
|---|---|---|---|
id |
integer | — | Auto PK |
state_type |
integer (FK) | ✅ | The parent StateType ID |
name |
string | ✅ | Unique within the state type; stored lower-case |
color |
string | ✅ | Hex color (without #) used in badges, e.g. "4caf50" |
is_initial |
boolean | ✅ | At most one state per type may be true |
tags |
array | ✅ | NetBox tags |
custom_fields |
object | ✅ | NetBox custom fields |
State UI
Plugins → IBM NetBox Plugins → States
| Page | URL | Description |
|---|---|---|
| List | /plugins/ibm-netbox-plugins/states/ |
All states; filterable by state_type, name; columns: State Type, Name, Color, Initial |
| Add | /plugins/ibm-netbox-plugins/states/add/ |
Fields: State Type, Name, Color (color picker), Is Initial checkbox. After save, redirects back to the parent StateType detail page. |
| Edit | /plugins/ibm-netbox-plugins/states/<pk>/edit/ |
Same fields; save redirects back to the parent StateType detail page. |
| Delete | /plugins/ibm-netbox-plugins/states/<pk>/delete/ |
Blocked if the state is referenced in any StateTransitionRule. |
| Bulk Delete | /plugins/ibm-netbox-plugins/states/delete/ |
Delete multiple selected states. |
Adding states to a type quickly
From the StateType detail page, click Add State to open the add form with the state_type field pre-filled. Use Save and Add Another to create all states in one session without re-selecting the type each time.
State API
Base URL: /api/plugins/ibm-netbox-plugins/states/
POST — Create a State
POST /api/plugins/ibm-netbox-plugins/states/
Authorization: Token <token>
Content-Type: application/json
{
"state_type": 1,
"name": "off",
"color": "9e9e9e",
"is_initial": true
}
201 Created
{
"id": 1,
"url": "http://<netbox>/api/plugins/ibm-netbox-plugins/states/1/",
"display": "power state / off",
"state_type": 1,
"name": "off",
"color": "9e9e9e",
"is_initial": true,
"tags": [],
"custom_fields": {},
"created": "2025-06-01T09:05:00.000Z",
"last_updated": "2025-06-01T09:05:00.000Z"
}
Create multiple states for the same type
POST /api/plugins/ibm-netbox-plugins/states/
Content-Type: application/json
[
{ "state_type": 1, "name": "off", "color": "9e9e9e", "is_initial": true },
{ "state_type": 1, "name": "on", "color": "4caf50", "is_initial": false }
]
NetBox REST API supports bulk POST by sending a JSON array.
GET — List states for a specific type
GET /api/plugins/ibm-netbox-plugins/states/?state_type=power+state
PATCH — Update a state
PATCH /api/plugins/ibm-netbox-plugins/states/1/
Content-Type: application/json
{ "color": "f44336" }
DELETE
DELETE /api/plugins/ibm-netbox-plugins/states/1/
State deletion is blocked if transition rules reference it
A state cannot be deleted while it is referenced as a from_state or to_state in any StateTransitionRule. Delete the rules first, then the state.
State Filter Parameters
| Parameter | Type | Description |
|---|---|---|
q |
string | Free-text search on name and state_type name |
name |
string | Substring match |
state_type |
string (multi) | Filter by StateType name (exact) |
state_type_id |
integer (multi) | Filter by StateType ID |
State Error Reference
| Scenario | HTTP | Field | Message |
|---|---|---|---|
| Duplicate name within same StateType | 400 | name |
A state with this name already exists for this state type. |
Second is_initial=true for same type |
400 | is_initial |
Another state is already marked as the initial state for this state type. |
| Delete state referenced in transition rules | 400/409 | — | Cannot delete state "<name>" — it is used as the "from" state in N transition rule(s). |
StateTransitionRule
Defines which target states are reachable from a given source state within a StateType. There is at most one rule per (state_type, from_state) pair. A rule has one source and one or more targets.
If no rule is defined for the current state, the transition is blocked (no targets allowed). If the StateType itself does not exist, all transitions are permitted (permissive fallback for new installs).
StateTransitionRule Fields
| Field | Type | Writable | Description |
|---|---|---|---|
id |
integer | — | Auto PK |
state_type |
integer (FK) | ✅ | Parent StateType |
from_state |
integer (FK) | ✅ | The source State (must belong to state_type) |
to_states |
array of integers | ✅ | One or more target State PKs (must all belong to state_type) |
tags |
array | ✅ | NetBox tags |
custom_fields |
object | ✅ | NetBox custom fields |
StateTransitionRule UI
Plugins → IBM NetBox Plugins → State Transition Rules
| Page | URL | Description |
|---|---|---|
| List | /plugins/ibm-netbox-plugins/state-transition-rules/ |
All rules; filterable by state_type, from_state; columns: State Type, From State, To States |
| Detail | /plugins/ibm-netbox-plugins/state-transition-rules/<pk>/ |
Shows the full rule: state type, from state, and each target state as a badge |
| Add | /plugins/ibm-netbox-plugins/state-transition-rules/add/ |
Fields: State Type (dropdown), From State (dropdown filtered to the chosen type), To States (multi-select filtered to the chosen type). After save, redirects back to the parent StateType detail page. |
| Edit | /plugins/ibm-netbox-plugins/state-transition-rules/<pk>/edit/ |
Same fields; to_states can be extended or replaced. |
| Delete | /plugins/ibm-netbox-plugins/state-transition-rules/<pk>/delete/ |
Confirmation page; deleting a rule does not delete the states themselves. |
| Bulk Delete | /plugins/ibm-netbox-plugins/state-transition-rules/delete/ |
Delete multiple selected rules. |
One rule per (StateType, From State) pair
If you try to create a second rule for the same (state_type, from_state) combination, the form will reject it. To add more target states, edit the existing rule and extend the to_states list.
Deleting a rule makes the from_state terminal
Once a rule is deleted, the from_state becomes unreachable for further transitions. Any device/VM currently in that state will be stuck there. Only delete rules intentionally.
StateTransitionRule API
Base URL: /api/plugins/ibm-netbox-plugins/state-transition-rules/
POST — Create a rule
POST /api/plugins/ibm-netbox-plugins/state-transition-rules/
Authorization: Token <token>
Content-Type: application/json
{
"state_type": 1,
"from_state": 1,
"to_states": [2]
}
201 Created
{
"id": 1,
"url": "http://<netbox>/api/plugins/ibm-netbox-plugins/state-transition-rules/1/",
"display": "power state: off → ...",
"state_type": 1,
"from_state": 1,
"to_states": [2],
"tags": [],
"custom_fields": {},
"created": "2025-06-01T09:10:00.000Z",
"last_updated": "2025-06-01T09:10:00.000Z"
}
PATCH — Add/replace target states
PATCH /api/plugins/ibm-netbox-plugins/state-transition-rules/1/
Content-Type: application/json
{ "to_states": [2] }
DELETE
DELETE /api/plugins/ibm-netbox-plugins/state-transition-rules/1/
StateTransitionRule Filter Parameters
| Parameter | Type | Description |
|---|---|---|
q |
string | Free-text search on state_type name and from_state name |
state_type |
string (multi) | Filter by StateType name |
state_type_id |
integer (multi) | Filter by StateType ID |
from_state |
integer (multi) | Filter by from_state ID |
StateTransitionRule Error Reference
| Scenario | HTTP | Field | Message |
|---|---|---|---|
from_state doesn't belong to state_type |
400 | from_state |
From state must belong to the selected state type. |
A to_state doesn't belong to state_type |
400 | to_states |
States do not belong to the selected state type: <names> |
Duplicate (state_type, from_state) |
400 | non-field | Database unique constraint violation |
OSState
Tracks the operating-system lifecycle state of a Device or VirtualMachine. Uses the state catalog keyed to state type name os state.
OSState Fields
| Field | Type | Writable | Description |
|---|---|---|---|
assigned_object_type |
content type | ✅ create-only | dcim.device or virtualization.virtualmachine |
assigned_object_id |
positive integer | ✅ create-only | PK of the assigned Device or VM |
assigned_object |
nested object | read-only | Resolved Device or VM |
current_state |
string or integer | ✅ | State name (e.g. "provisioning") or State PK; must belong to os state StateType and satisfy the transition rule |
previous_state |
string | read-only | State name of the prior state; set automatically on update |
transitioned_by |
string | read-only | Set automatically |
transition_source |
string | ✅ | user, system, api, automation, or monitoring |
OSState Constraints
- One per object: A Device or VM may have at most one OSState record.
- Transition rules apply: The
current_statemust satisfy the applicableStateTransitionRule. If no rule is defined for the current state, no transitions are allowed. - State must change: Setting
current_stateto the same value as currently stored returns400. - State must belong to type
os state: Any State FK not of the correct StateType returns a validation error.
OSState API
Base URL: /api/plugins/ibm-netbox-plugins/os-states/
POST — Create
POST /api/plugins/ibm-netbox-plugins/os-states/
Authorization: Token <token>
Content-Type: application/json
{
"assigned_object_type": "dcim.device",
"assigned_object_id": 191688,
"current_state": "provisioning",
"transition_source": "api"
}
201 Created
{
"id": 7,
"url": "http://<netbox>/api/plugins/ibm-netbox-plugins/os-states/7/",
"display_url": "http://<netbox>/api/plugins/ibm-netbox-plugins/os-states/7/",
"display": "dal10-server-01 - provisioning",
"assigned_object_type": "dcim.device",
"assigned_object_id": 191688,
"assigned_object": {
"id": 191688,
"url": "http://<netbox>/api/dcim/devices/191688/",
"display": "dal10-server-01",
"name": "dal10-server-01"
},
"current_state": "provisioning",
"previous_state": null,
"transitioned_by": "admin",
"transition_source": "api",
"transitioned_at": "2025-06-01T10:00:00.000Z",
"tags": [],
"custom_fields": {},
"created": "2025-06-01T10:00:00.000Z",
"last_updated": "2025-06-01T10:00:00.000Z"
}
GET — Retrieve
GET /api/plugins/ibm-netbox-plugins/os-states/7/
Authorization: Token <token>
200 OK — same structure as above.
GET — List (filtered by device)
GET /api/plugins/ibm-netbox-plugins/os-states/?device=191688
Authorization: Token <token>
PATCH — Transition state
Pass the target state as a name string or integer PK. The value must satisfy the transition rule from the state currently stored.
PATCH /api/plugins/ibm-netbox-plugins/os-states/7/
Authorization: Token <token>
Content-Type: application/json
{
"current_state": "running",
"transition_source": "automation"
}
200 OK
{
"id": 7,
"current_state": "running",
"previous_state": "provisioning",
"transitioned_by": "admin",
"transition_source": "automation",
"transitioned_at": "2025-06-01T11:00:00.000Z"
}
DELETE
DELETE /api/plugins/ibm-netbox-plugins/os-states/7/
204 No Content
OSState State Transition Example
Given these states and rules for os state:
States: provisioning, running, stopped, decommissioned
Rules:
provisioning → [running]
running → [stopped]
stopped → [running, decommissioned]
decommissioned → [] (terminal — no rule defined, no further transitions)
Create OSState: current="provisioning", previous=null
PATCH "running": ✅ previous="provisioning"
PATCH "stopped": ✅ previous="running"
PATCH "running": ✅ previous="stopped"
PATCH "decommissioned": ✅ previous="running"
PATCH "running": ❌ 400 — Invalid transition from "decommissioned". Allowed: none.
PATCH "decommissioned": ❌ 400 — Current state must differ from the existing state.
OSState Filter Parameters
| Parameter | Type | Description |
|---|---|---|
q |
string | Free-text search on state name, transitioned_by, device/VM name |
assigned_object_type |
integer | Content-type ID |
assigned_object_id |
integer | PK of Device or VM |
device |
integer | Shorthand — filter by Device ID |
virtual_machine |
integer | Shorthand — filter by VM ID |
current_state |
integer (multi) | Filter by State ID(s) |
OSState Error Reference
| Scenario | HTTP | Field | Message |
|---|---|---|---|
| Duplicate OSState for same object | 400 | non-field | An OS state already exists for this object. |
| Invalid transition (rule disallows it) | 400 | current_state |
Invalid transition from "<from>". Allowed: <states>. |
Same current_state as stored |
400 | current_state |
Current state must differ from the existing state. |
assigned_object_id does not exist |
400 | assigned_object_id |
No <app>.<model> found with ID <id>. |
OSState id not found |
404 | — | No OSState matches the given query. |
RuntimeState
Tracks the runtime lifecycle state of a Device or VirtualMachine. Uses the state catalog keyed to state type name runtime state.
The model, API, and validation rules are identical to OSState. All constraints, filter parameters, and error messages follow the same patterns — substitute runtime state for os state, and /os-states/ with /runtime-states/ in all URLs.
RuntimeState Fields
Identical to OSState fields — assigned_object_type, assigned_object_id, assigned_object, current_state, previous_state, transitioned_by, transition_source.
RuntimeState API
Base URL: /api/plugins/ibm-netbox-plugins/runtime-states/
POST — Create
POST /api/plugins/ibm-netbox-plugins/runtime-states/
Authorization: Token <token>
Content-Type: application/json
{
"assigned_object_type": "virtualization.virtualmachine",
"assigned_object_id": 5001,
"current_state": "initialising",
"transition_source": "system"
}
201 Created — structure identical to OSState. current_state and previous_state in the response are state name strings.
PATCH — Transition
PATCH /api/plugins/ibm-netbox-plugins/runtime-states/3/
Content-Type: application/json
{ "current_state": "healthy" }
RuntimeState State Transition Example
Given these states and rules for runtime state:
States: initialising, healthy, degraded, failed
Rules:
initialising → [healthy, failed]
healthy → [degraded, failed]
degraded → [healthy, failed]
failed → [] (terminal)
Create: current="initialising", previous=null
PATCH "healthy": ✅ previous="initialising"
PATCH "degraded": ✅ previous="healthy"
PATCH "failed": ✅ previous="degraded"
PATCH "healthy": ❌ 400 — Invalid transition from "failed". Allowed: none.
RuntimeState Filter Parameters
Same as OSState: q, assigned_object_type, assigned_object_id, device, virtual_machine, current_state.
RuntimeState Error Reference
Same as OSState error messages with runtime state substituted.
AllocationState
Tracks the allocation lifecycle state of a Device only (VMs are not supported). Uses the state catalog keyed to state type name allocation state.
AllocationState Fields
| Field | Type | Writable | Description |
|---|---|---|---|
device |
integer (FK → Device) | ✅ create-only | The Device this state belongs to (one-to-one) |
current_state |
string or integer | ✅ | State name (e.g. "available") or State PK; must belong to allocation state StateType and satisfy the transition rule |
previous_state |
string | read-only | State name of the prior state; set automatically on update |
transitioned_by |
string | read-only | Set automatically |
transition_source |
string | ✅ | user, system, api, automation, or monitoring |
AllocationState Constraints
- One per device: A Device may have at most one AllocationState record.
- Transition rules apply: Same rule-based validation as OSState and RuntimeState.
- State must change: Same as other state models.
- Device only: VMs are not supported; use OSState or RuntimeState for VMs.
AllocationState API
Base URL: /api/plugins/ibm-netbox-plugins/allocation-states/
POST — Create
POST /api/plugins/ibm-netbox-plugins/allocation-states/
Authorization: Token <token>
Content-Type: application/json
{
"device": 191688,
"current_state": "available",
"transition_source": "user"
}
201 Created
{
"id": 15,
"url": "http://<netbox>/api/plugins/ibm-netbox-plugins/allocation-states/15/",
"display_url": "http://<netbox>/api/plugins/ibm-netbox-plugins/allocation-states/15/",
"display": "dal10-server-01 - available",
"device": 191688,
"current_state": "available",
"previous_state": null,
"transitioned_by": "admin",
"transition_source": "user",
"transitioned_at": "2025-06-01T10:00:00.000Z",
"tags": [],
"custom_fields": {},
"created": "2025-06-01T10:00:00.000Z",
"last_updated": "2025-06-01T10:00:00.000Z"
}
GET — Retrieve
GET /api/plugins/ibm-netbox-plugins/allocation-states/15/
GET — List (filtered)
GET /api/plugins/ibm-netbox-plugins/allocation-states/?device=191688
PATCH — Transition
PATCH /api/plugins/ibm-netbox-plugins/allocation-states/15/
Content-Type: application/json
{
"current_state": "reserved",
"transition_source": "api"
}
200 OK — same structure with updated fields.
DELETE
DELETE /api/plugins/ibm-netbox-plugins/allocation-states/15/
204 No Content
AllocationState State Transition Example
Given these states and rules for allocation state:
States: available, reserved, allocated, decommissioned
Rules:
available → [reserved, allocated]
reserved → [available, allocated]
allocated → [available, decommissioned]
decommissioned → [] (terminal)
Create: current="available", previous=null
PATCH "reserved": ✅ previous="available"
PATCH "allocated": ✅ previous="reserved"
PATCH "decommissioned": ✅ previous="allocated"
PATCH "available": ❌ 400 — Invalid transition from "decommissioned". Allowed: none.
AllocationState Filter Parameters
| Parameter | Type | Description |
|---|---|---|
q |
string | Free-text on state name, transitioned_by, device name |
device |
integer (multi) | Filter by Device ID(s) |
current_state |
integer (multi) | Filter by State ID(s) |
AllocationState Error Reference
| Scenario | HTTP | Field | Message |
|---|---|---|---|
| Duplicate AllocationState for same device | 400 | non-field | An allocation state already exists for this device. |
| Invalid transition | 400 | current_state |
Invalid transition from "<from>". Allowed: <states>. |
Same current_state as stored |
400 | current_state |
Current state must differ from the existing state. |
AllocationState id not found |
404 | — | No AllocationState matches the given query. |
UI — State Cards on Device / VM Detail Pages
When viewing a Device or Virtual Machine, each configured state dimension renders as an inline card:
┌──────────────────────────────────────────┐
│ Power State [ON] │
│ Last changed: 2025-06-01 11:00 by admin │
│ Source: api │
│ [Transition ▼] → Off │
└──────────────────────────────────────────┘
┌──────────────────────────────────────────┐
│ OS State [RUNNING] │
│ Previous: PROVISIONING │
│ Allowed transitions: STOPPED │
│ [Transition ▼] │
└──────────────────────────────────────────┘
- If no state record exists yet, the card shows a Create button. Clicking it POSTs to the quick-create endpoint and sets the
is_initialstate automatically. - For all four state types (Power, OS, Runtime, Allocation), the card shows a Transition dropdown listing the allowed next states from the applicable
StateTransitionRule. - Transitions via the card are HTMX-powered (no full page reload).
- The Transition URL pattern for PowerState is:
POST /plugins/ibm-netbox-plugins/power-states/<pk>/transition/
State Models Dashboard View
A combined view for a single device or VM is available at:
/plugins/ibm-netbox-plugins/state-models/<content_type>/<object_id>/
Where content_type is device or virtualmachine.
This page displays all four state dimensions side-by-side, plus a filterable state history panel.
| History Filter | Values |
|---|---|
| State Type | all, power (others TBD) |
| Time Range | 24h, 7d, 30d, 90d |
| Changed By | Username substring |
Signals
A pre_save signal fires automatically on every save of any state model and performs two actions:
- Sets
transitioned_byto the username of the currently authenticated request user (vianetbox.context.current_request). - Sets
previous_stateto the value ofcurrent_statecurrently in the database (the value being replaced). On first creation,previous_stateis set tonull.
This means transitioned_by and previous_state are always accurate regardless of whether the save originates from the UI, the API, a management command, or a direct ORM call with a request in context.
The signal is registered for all four lifecycle models: PowerState, OSState, RuntimeState, AllocationState.
Permissions
All state models follow standard NetBox model permissions:
| Permission | Codename |
|---|---|
| View | ibm_netbox_plugins.view_powerstate |
| Add | ibm_netbox_plugins.add_powerstate |
| Change | ibm_netbox_plugins.change_powerstate |
| Delete | ibm_netbox_plugins.delete_powerstate |
Replace powerstate with osstate, runtimestate, or allocationstate for the other models. State catalog models (statetype, state, statetransitionrule) follow the same pattern.
Complete API Endpoint Reference
| Method | URL | Description |
|---|---|---|
| GET / POST | /api/plugins/ibm-netbox-plugins/power-states/ |
List / Create PowerState |
| GET / PATCH / PUT / DELETE | /api/plugins/ibm-netbox-plugins/power-states/{id}/ |
Retrieve / Update / Delete PowerState |
| POST | /plugins/ibm-netbox-plugins/power-states/{id}/transition/ |
UI transition (HTMX) |
| GET / POST | /api/plugins/ibm-netbox-plugins/os-states/ |
List / Create OSState |
| GET / PATCH / PUT / DELETE | /api/plugins/ibm-netbox-plugins/os-states/{id}/ |
Retrieve / Update / Delete OSState |
| GET / POST | /api/plugins/ibm-netbox-plugins/runtime-states/ |
List / Create RuntimeState |
| GET / PATCH / PUT / DELETE | /api/plugins/ibm-netbox-plugins/runtime-states/{id}/ |
Retrieve / Update / Delete RuntimeState |
| GET / POST | /api/plugins/ibm-netbox-plugins/allocation-states/ |
List / Create AllocationState |
| GET / PATCH / PUT / DELETE | /api/plugins/ibm-netbox-plugins/allocation-states/{id}/ |
Retrieve / Update / Delete AllocationState |
| GET / POST | /api/plugins/ibm-netbox-plugins/state-types/ |
List / Create StateType |
| GET / PATCH / PUT / DELETE | /api/plugins/ibm-netbox-plugins/state-types/{id}/ |
Retrieve / Update / Delete StateType |
| GET / POST | /api/plugins/ibm-netbox-plugins/states/ |
List / Create State |
| GET / PATCH / PUT / DELETE | /api/plugins/ibm-netbox-plugins/states/{id}/ |
Retrieve / Update / Delete State |
| GET / POST | /api/plugins/ibm-netbox-plugins/state-transition-rules/ |
List / Create StateTransitionRule |
| GET / PATCH / PUT / DELETE | /api/plugins/ibm-netbox-plugins/state-transition-rules/{id}/ |
Retrieve / Update / Delete StateTransitionRule |
All endpoints also support bulk operations via array request bodies.
Interactive documentation is available at: /api/docs/ → ibm-netbox-plugins section.
End-to-End Example: Full PowerState Lifecycle
This walk-through sets up the catalog and then toggles a device's power state.
1. Create the PowerState StateType
curl -sX POST http://<netbox>/api/plugins/ibm-netbox-plugins/state-types/ \
-H "Authorization: Token <token>" \
-H "Content-Type: application/json" \
-d '{"name": "power state"}'
# → id: 1
2. Create States
curl -sX POST http://<netbox>/api/plugins/ibm-netbox-plugins/states/ \
-H "Authorization: Token <token>" \
-H "Content-Type: application/json" \
-d '[
{"state_type": 1, "name": "off", "color": "9e9e9e", "is_initial": true},
{"state_type": 1, "name": "on", "color": "4caf50", "is_initial": false}
]'
# → ids: 1 (off), 2 (on)
3. Create Transition Rules
# off → on
curl -sX POST http://<netbox>/api/plugins/ibm-netbox-plugins/state-transition-rules/ \
-H "Authorization: Token <token>" -H "Content-Type: application/json" \
-d '{"state_type": 1, "from_state": 1, "to_states": [2]}'
# on → off
curl -sX POST http://<netbox>/api/plugins/ibm-netbox-plugins/state-transition-rules/ \
-H "Authorization: Token <token>" -H "Content-Type: application/json" \
-d '{"state_type": 1, "from_state": 2, "to_states": [1]}'
4. Create the PowerState for a device (starts at off)
curl -sX POST http://<netbox>/api/plugins/ibm-netbox-plugins/power-states/ \
-H "Authorization: Token <token>" -H "Content-Type: application/json" \
-d '{
"assigned_object_type": "dcim.device",
"assigned_object_id": 191688,
"current_state": "off",
"transition_source": "api"
}'
# → id: 42, current_state: "off", previous_state: null
5. Transition the power state
# off → on (name string)
curl -sX PATCH http://<netbox>/api/plugins/ibm-netbox-plugins/power-states/42/ \
-H "Authorization: Token <token>" -H "Content-Type: application/json" \
-d '{"current_state": "on"}'
# on → off (name string)
curl -sX PATCH http://<netbox>/api/plugins/ibm-netbox-plugins/power-states/42/ \
-H "Authorization: Token <token>" -H "Content-Type: application/json" \
-d '{"current_state": "off"}'
# off → off (BLOCKED — same state)
curl -sX PATCH http://<netbox>/api/plugins/ibm-netbox-plugins/power-states/42/ \
-H "Authorization: Token <token>" -H "Content-Type: application/json" \
-d '{"current_state": "off"}'
# → 400 {"current_state": ["Current state must differ from previous state."]}
6. Check the final state
curl -s http://<netbox>/api/plugins/ibm-netbox-plugins/power-states/42/ \
-H "Authorization: Token <token>"
{
"id": 42,
"current_state": "off",
"previous_state": "on",
"transitioned_by": "admin",
"transition_source": "api",
"transitioned_at": "2025-06-01T15:45:00.000Z"
}
End-to-End Example: Full OS State Lifecycle
This walk-through sets up the catalog and then drives a device through the full OS state lifecycle.
1. Create the StateType
curl -sX POST http://<netbox>/api/plugins/ibm-netbox-plugins/state-types/ \
-H "Authorization: Token <token>" \
-H "Content-Type: application/json" \
-d '{"name": "os state"}'
# → id: 2
2. Create States
curl -sX POST http://<netbox>/api/plugins/ibm-netbox-plugins/states/ \
-H "Authorization: Token <token>" \
-H "Content-Type: application/json" \
-d '[
{"state_type": 2, "name": "provisioning", "color": "ff9800", "is_initial": true},
{"state_type": 2, "name": "running", "color": "4caf50", "is_initial": false},
{"state_type": 2, "name": "stopped", "color": "f44336", "is_initial": false},
{"state_type": 2, "name": "decommissioned", "color": "9e9e9e", "is_initial": false}
]'
# → ids: 10, 11, 12, 13
3. Create Transition Rules
# provisioning → running
curl -sX POST http://<netbox>/api/plugins/ibm-netbox-plugins/state-transition-rules/ \
-H "Authorization: Token <token>" -H "Content-Type: application/json" \
-d '{"state_type": 2, "from_state": 10, "to_states": [11]}'
# running → stopped
curl -sX POST http://<netbox>/api/plugins/ibm-netbox-plugins/state-transition-rules/ \
-H "Authorization: Token <token>" -H "Content-Type: application/json" \
-d '{"state_type": 2, "from_state": 11, "to_states": [12]}'
# stopped → running or decommissioned
curl -sX POST http://<netbox>/api/plugins/ibm-netbox-plugins/state-transition-rules/ \
-H "Authorization: Token <token>" -H "Content-Type: application/json" \
-d '{"state_type": 2, "from_state": 12, "to_states": [11, 13]}'
# decommissioned → (terminal — no rule)
4. Create the OSState for a device (starts at provisioning)
curl -sX POST http://<netbox>/api/plugins/ibm-netbox-plugins/os-states/ \
-H "Authorization: Token <token>" -H "Content-Type: application/json" \
-d '{
"assigned_object_type": "dcim.device",
"assigned_object_id": 191688,
"current_state": "provisioning",
"transition_source": "api"
}'
# → id: 7, current_state: "provisioning", previous_state: null
5. Transition through the lifecycle
# provisioning → running
curl -sX PATCH http://<netbox>/api/plugins/ibm-netbox-plugins/os-states/7/ \
-H "Authorization: Token <token>" -H "Content-Type: application/json" \
-d '{"current_state": "running"}'
# running → stopped
curl -sX PATCH http://<netbox>/api/plugins/ibm-netbox-plugins/os-states/7/ \
-H "Authorization: Token <token>" -H "Content-Type: application/json" \
-d '{"current_state": "stopped"}'
# stopped → decommissioned
curl -sX PATCH http://<netbox>/api/plugins/ibm-netbox-plugins/os-states/7/ \
-H "Authorization: Token <token>" -H "Content-Type: application/json" \
-d '{"current_state": "decommissioned"}'
# decommissioned → running (BLOCKED — terminal state)
curl -sX PATCH http://<netbox>/api/plugins/ibm-netbox-plugins/os-states/7/ \
-H "Authorization: Token <token>" -H "Content-Type: application/json" \
-d '{"current_state": "running"}'
# → 400 {"current_state": ["Invalid transition from \"decommissioned\". Allowed: none."]}
6. Check the final state
curl -s http://<netbox>/api/plugins/ibm-netbox-plugins/os-states/7/ \
-H "Authorization: Token <token>"
{
"id": 7,
"current_state": "decommissioned",
"previous_state": "stopped",
"transitioned_by": "admin",
"transition_source": "api",
"transitioned_at": "2025-06-01T15:45:00.000Z"
}
Frequently Asked Questions
Q: How do I find a state record for a specific device without knowing its state ID?
Filter using ?device=<device_id> for AllocationState / OSState / RuntimeState, or ?device=<device_id> / ?assigned_object_id=<id> for PowerState. The response's id field is the state record ID to use in subsequent PATCH / DELETE requests.
Q: Can the same device have OSState, RuntimeState, AllocationState, AND PowerState all at the same time?
Yes. Each is an independent model. One device can hold one record of each type simultaneously.
Q: What happens if I try to create a PowerState but no power state StateType exists?
The transition check allows any state on creation (permissive fallback). However, you still need a valid State FK with current_state. If no matching StateType exists, transition enforcement is skipped. The safe approach is to always create the StateType, States, and TransitionRules first.
Q: Does PowerState still have fixed on/off values?
No. As of migration 0017/0018, current_state and previous_state are FK references to State catalog objects. You define the states yourself (name them anything, e.g. on, off, standby, maintenance). The recommended setup for simple power tracking is two states on and off with bidirectional transition rules.
Q: How were existing on/off PowerState records migrated?
Migration 0017 renamed the old current_state/previous_state CharFields to current_state_old/previous_state_old, added new FK columns, then back-filled the FK IDs by matching State.name (case-insensitive) within the power state StateType. Migration 0018 made current_state non-nullable and dropped the old columns. Rows that had no matching State remain with current_state_id = NULL until manually corrected.
Q: What is a terminal state?
A state that has no StateTransitionRule with it as the from_state. Once an object reaches a terminal state, no further transitions are possible via the API or UI. To make a terminal state non-terminal, create a StateTransitionRule for it.
Q: Can I delete a state that is currently in use by a device?
No. If a state is referenced by active PowerState / OSState / RuntimeState / AllocationState records as current_state or previous_state, the database will block deletion due to PROTECT foreign-key constraints.
Q: Is there a bulk-create endpoint for state records?
Yes. The NetBox REST API supports bulk POST via a JSON array on any list endpoint. For example:
curl -sX POST http://<netbox>/api/plugins/ibm-netbox-plugins/os-states/ \
-H "Authorization: Token <token>" -H "Content-Type: application/json" \
-d '[
{"assigned_object_type": "dcim.device", "assigned_object_id": 191688, "current_state": "provisioning"},
{"assigned_object_type": "dcim.device", "assigned_object_id": 191689, "current_state": "provisioning"}
]'
Q: Why does transition_source appear in the PowerState error reference as a blocked field in older docs?
In older versions, transition_source was read-only for PowerState. It is now writable for all four lifecycle models (PowerState, OSState, RuntimeState, AllocationState), allowing automation pipelines to identify themselves.
Filtering Devices and VMs by State
In addition to the dedicated state-model list endpoints, state filters are injected directly into the Device and Virtual Machine list endpoints so you can find objects by their current state without first querying the state records separately.
Where they appear
| Context | Endpoint / UI |
|---|---|
| Device list UI | Devices → Devices filter sidebar, under the States fieldset |
| VM list UI | Virtualization → Virtual Machines filter sidebar, under the States fieldset |
| Device list API | GET /api/dcim/devices/ |
| VM list API | GET /api/virtualization/virtual-machines/ |
Device state filter parameters
These parameters are available on /api/dcim/devices/:
| Parameter | Type | Description |
|---|---|---|
power_state |
integer (multi) | Filter devices whose current PowerState matches one of the given State IDs. Use GET /api/plugins/ibm-netbox-plugins/states/?state_type=power+state to look up IDs. Devices with no PowerState record are excluded. |
os_state_id |
integer (multi) | Filter devices whose current OSState matches one of the given State IDs. The UI dropdown is automatically scoped to states belonging to the os state StateType. |
runtime_state_id |
integer (multi) | Filter devices whose current RuntimeState matches one of the given State IDs. UI dropdown scoped to runtime state. |
allocation_state_id |
integer (multi) | Filter devices whose current AllocationState matches one of the given State IDs. UI dropdown scoped to allocation state. |
allocation_state_id is device-only
AllocationState is not supported on VMs, so allocation_state_id does not appear on the VM endpoint.
PowerState filter now accepts State IDs
The power_state filter on Device/VM list endpoints now accepts State record IDs (integers) rather than string literals on/off. Look up the IDs for your power state states using GET /api/plugins/ibm-netbox-plugins/states/?state_type=power+state.
Device API examples
Find all devices in the "on" power state (assuming State id=2 is "on"):
GET /api/dcim/devices/?power_state=2
Authorization: Token <token>
Find all devices in "off" power state at a specific site:
GET /api/dcim/devices/?power_state=1&site_id=3
Authorization: Token <token>
Find devices in a specific OS state (by State ID):
GET /api/dcim/devices/?os_state_id=11
Authorization: Token <token>
Find devices in multiple runtime states:
GET /api/dcim/devices/?runtime_state_id=21&runtime_state_id=22
Authorization: Token <token>
Find devices in a specific allocation state:
GET /api/dcim/devices/?allocation_state_id=32
Authorization: Token <token>
Combine state filters with other device filters:
GET /api/dcim/devices/?power_state=2&allocation_state_id=31&site_id=3
Authorization: Token <token>
Virtual Machine state filter parameters
These parameters are available on /api/virtualization/virtual-machines/:
| Parameter | Type | Description |
|---|---|---|
power_state |
integer (multi) | Filter VMs whose current PowerState matches one of the given State IDs. VMs with no PowerState record are excluded. |
os_state_id |
integer (multi) | Filter VMs whose current OSState matches one of the given State IDs. UI dropdown scoped to os state. |
runtime_state_id |
integer (multi) | Filter VMs whose current RuntimeState matches one of the given State IDs. UI dropdown scoped to runtime state. |
VM API examples
Find all powered-on VMs (assuming State id=2 is "on"):
GET /api/virtualization/virtual-machines/?power_state=2
Authorization: Token <token>
Find VMs in a specific runtime state:
GET /api/virtualization/virtual-machines/?runtime_state_id=21
Authorization: Token <token>
Combine with cluster filter:
GET /api/virtualization/virtual-machines/?power_state=2&cluster_id=5
Authorization: Token <token>
How the filters work
Each filter performs a reverse join through the state model to find matching object IDs:
power_state=2 (State id=2 = "on")
→ PowerState.objects.filter(assigned_object_type=<device or vm CT>, current_state_id=2)
→ collect assigned_object_ids
→ Device/VM.objects.filter(pk__in=<ids>)
This means:
- Objects with no state record are excluded when a state filter is applied. If a device has no
PowerStaterecord and you filter bypower_state=2, that device will not appear in the results. - Multiple values for the same parameter are OR'd —
?power_state=1&power_state=2returns all devices that have a PowerState record set to either state ID 1 or state ID 2. - Different state parameters are AND'd —
?power_state=2&allocation_state_id=31returns only devices that are both in power state 2 AND in allocation state 31. - All state ID parameters accept State record IDs (integers), not state names. Use
GET /api/plugins/ibm-netbox-plugins/states/?state_type=<type+name>to look up the IDs for the states you want to filter by.
Looking up State IDs for use in filters
# Get all power state IDs
curl -s "http://<netbox>/api/plugins/ibm-netbox-plugins/states/?state_type=power+state" \
-H "Authorization: Token <token>" | python3 -m json.tool
# Response excerpt:
# { "results": [
# { "id": 1, "name": "off", ... },
# { "id": 2, "name": "on", ... }
# ]}
# Get all OS state IDs
curl -s "http://<netbox>/api/plugins/ibm-netbox-plugins/states/?state_type=os+state" \
-H "Authorization: Token <token>" | python3 -m json.tool
# Then filter devices by os_state_id=11 (running)
curl -s "http://<netbox>/api/dcim/devices/?os_state_id=11" \
-H "Authorization: Token <token>"
UI — States fieldset
Both the Device and VM filter forms show a States fieldset in the filter sidebar panel:
Device filter sidebar → States:
| Field | Type | Description |
|---|---|---|
| Power State | dynamic dropdown | Shows all states under the power state StateType |
| OS State | dynamic dropdown | Shows all states under the os state StateType |
| Runtime State | dynamic dropdown | Shows all states under the runtime state StateType |
| Allocation State | dynamic dropdown | Shows all states under the allocation state StateType |
VM filter sidebar → States:
| Field | Type | Description |
|---|---|---|
| Power State | dynamic dropdown | Shows all states under the power state StateType |
| OS State | dynamic dropdown | Shows all states under the os state StateType |
| Runtime State | dynamic dropdown | Shows all states under the runtime state StateType |
The dynamic dropdowns are pre-filtered server-side to show only states belonging to the correct StateType, so the OS State dropdown will never show power or runtime states.