Skip to content

One node's underlay connections + observed status (bootstrap-admin).

GET
/v1/admin/network/underlay/nodes/{nodeId}
Code sample: Shell / cURL
curl --request GET \
--url https://stratum.example.com/v1/admin/network/underlay/nodes/example \
--header 'Authorization: Bearer <token>'

One node’s view from listUnderlayNodes: declared connections, the observed interfaces with their declared and observed roles, the per-connection apply state, and fabric — the signals the roles were derived from, each observed, unset or unobserved. A node that has neither declared nor reported anything returns an empty view, so a fresh node can still be configured. Install-scope; gates on network:GetUnderlayNode.

nodeId
required
string

Node underlay state.

Media typeapplication/json
object
nodeId
required
string
connections
required
Array<object>

declared host underlay connection (a NIC, VLAN or bond + addressing).

object
nodeId
required
string
name
required
string
/^[a-zA-Z0-9][a-zA-Z0-9_-]{0,62}$/
kind
required
string
Allowed values: ethernet vlan bond
interface
required

Resulting host interface name (<= 15 chars). e.g. enp2s0f0np0 or vlan40.

string
parent

Parent device for a vlan connection.

string
vlanId
integer
<= 4094
addresses
required

Static CIDRs, e.g. [“192.0.2.111/24”].

Array<string>
mtu
integer
trafficClass

traffic class this connection serves.

string
Allowed values: "" mgmt overlay storage provider
createdAt
string format: date-time
updatedAt
string format: date-time
createdBy
string
zone

Firewalld zone. A cluster-internal link left in NetworkManager’s default public zone comes up and silently drops the traffic it exists to carry, so the fabric declares trusted explicitly.

string
controller

The bond this connection is a member of. A member carries no addresses of its own — the bond’s VLANs own the addressing, and an addressed member is how a link ends up half in the aggregate.

string
bond

802.3ad (or active-backup) aggregation for a bond connection.

xmitHashPolicy is required for 802.3ad and is not a tuning preference. Geneve puts every tunnel between a single MAC/IP pair, so the kernel default (layer2) hashes ALL overlay traffic onto one member: the bond reports UP, both links report UP, and half the fabric is unreachable.

object
mode
required
string
Allowed values: 802.3ad active-backup
xmitHashPolicy

Required for 802.3ad. Use layer3+4 so overlay traffic spreads across members.

string
miimonMs

Link-monitor interval in milliseconds, e.g. 100.

integer
lacpRate
string
Allowed values: slow fast
neverDefault

Keep this connection’s route out of the default path. A storage or overlay VLAN taking the default route would pull cluster egress onto the fabric.

boolean
purposes

What the link carries: the network half of the node’s hardware plan. trafficClass is the primary one (the first of mgmt, storage, overlay, provider); send either, or both agreeing. oob is the stage-1 lifeline and is never an underlay connection.

Array<string>
Allowed values: oob mgmt storage overlay provider
interfaces
required
Array<object>

An observed interface on a node: a physical NIC, a bond, or a VLAN.

this used to be physical NICs only, which on a bonded node hid the layer the traffic actually rides: the bond and the storage/overlay VLANs did not appear at all, while the members carrying every Ceph and Geneve byte showed as undeclared with no addresses.

object
name
required
string
mac
string
state

UP | DOWN | UNKNOWN

string
speedMb

Link speed in Mb/s (0 if unknown).

integer
mtu
integer
kind

bridge is only ever the Stratum LB dataplane management bridge (stratum-lbmgmt,): it holds a host address no other row explains. Other bridges (podman, libvirt, OVS’s own) are not reported.

string
Allowed values: physical bond vlan bridge
controller

The bond a physical member is enslaved to.

string
parent

A VLAN’s underlying device (often the bond).

string
bond

A bond’s observed LACP state.

aggregated is the field that matters. Under 802.3ad a member whose LACP negotiation failed keeps its own aggregator id and runs solo: the bond reports UP, every link reports UP at full speed, and the cluster silently has half the bandwidth it believes it has. Everything else here is the evidence behind that verdict.

object
mode
string
xmitHashPolicy
string
lacpRate
string
partnerMac

The switch the aggregate negotiated with. Absent means nothing answered LACP.

string
aggregatorId

The active aggregator; every member should share it.

integer
aggregated
required

True only when there is an LACP partner and every UP member sits in the active aggregator. Deliberately strict — “up” is what the failure already reports.

boolean
members
Array<object>

One link in a bond, with the fields that reveal a member running outside the aggregate.

object
name
required
string
miiStatus

Up | down

string
speedMb
integer
aggregatorId

The member’s LACP aggregator. A member whose negotiation failed keeps its OWN id and runs solo while still reporting up at full speed — comparing this against the bond’s aggregatorId is what exposes that.

integer
linkFailures
integer
addresses

The CIDRs actually on the link. The inventory previously reported the shape of a node’s network and nothing about what rides it.

Array<string>
declaredRoles

What the cluster topology (cluster.yaml, resolved) says this interface is for. A class declared with an interface lands on that interface; one declared by address only (mgmt_ip, the data_ip alias, or a class collapsing to mgmt_ip) lands on whichever interface holds the address.

Array<string>
Allowed values: mgmt storage overlay
observedRoles

What this node’s own running configuration shows the link is actually carrying, derived from the Ceph public/cluster networks, the OVN Geneve endpoint, the provider bridge mappings, the declared service VIPs and the LB dataplane management bridge.

Reported alongside declaredRoles rather than instead of them, because the declared value is the one that goes stale. Both ceph-public and ceph-cluster on one link is normal for a hyperconverged install and means replication shares the link with client I/O; with no Ceph cluster_network set, replication rides the public network, so its link carries both.

An empty list is only “carries nothing” when the signals were read: see roleStatus, unverifiedRoles and the node’s fabric.signals.

Array<string>
Allowed values: mgmt mgmt-vip ceph-public ceph-cluster ovn-encap ovn-provider lb-mgmt
roleMismatch

The topology and the running configuration disagree about this interface: it is declared for a role the running configuration puts elsewhere, or it carries a role the topology declares elsewhere. This is the field worth alarming on: was a node whose OVN Geneve endpoint had collapsed to its management address while the topology declared the overlay, and every other surface reported the node healthy while all cross-chassis tenant traffic was dropped. Both rows are flagged — the link that lost the role and the link that has it.

Never raised from a signal that could not be read (that is unverifiedRoles), so a node without Ceph or OVN does not report every interface as mismatched.

boolean
roleMismatchReasons

What disagrees, with both values, e.g. “declared overlay 192.0.2.112 on vlan91, but OVN’s Geneve endpoint (ovn-encap-ip) is 192.0.2.12 on enp89s0”. Present only when roleMismatch is true.

Array<string>
unverifiedRoles

Declared roles this node could not check: the signal that would confirm or contradict them could not be read (no OVS database, no Ceph client), or the subsystem has nothing configured to check against. These are UNKNOWN — neither a match nor a mismatch.

Array<string>
Allowed values: mgmt storage overlay
pendingRoles

Declared roles not in use yet: storage before Ceph is set up on the node (its Ceph binds no public network). Neutral: neither unknown nor a mismatch, and no warning. Once Ceph is set up the role is checked against Ceph’s public and cluster networks.

Array<string>
Allowed values: storage
roleStatus

The verdict for this interface. match: every declared role was checked and agrees. mismatch: see roleMismatchReasons. unverified: nothing disagrees, but a declared role (or the topology itself) could not be read. pending-ceph: the storage role is not in use yet because Ceph is not set up (“Not in use yet (Ceph not set up)”); neutral, not a warning. undeclared: the topology declares nothing for this link. Absent from a node whose host-helper predates it.

string
Allowed values: match mismatch unverified pending-ceph undeclared
purposes

What the node’s hardware plan says this interface carries (its own link’s purposes, plus those of everything stacked on it), in the vocabulary. The plan’s counterpart of declaredRoles.

Array<string>
Allowed values: oob mgmt storage overlay provider
observedPurposes

observedRoles mapped onto purposes: mgmt / mgmt-vip -> mgmt, ceph-public / ceph-cluster -> storage, ovn-encap -> overlay, ovn-provider -> provider. a VLAN sub-interface the node made itself on a planned link (OVN’s bridged provider <trunk>.<vlan>), which no connection names, rolls up to that link: what it runs of the trunk’s planned purposes is observed on the trunk too, and is the sub-interface’s own purposes.

Array<string>
Allowed values: oob mgmt storage overlay provider
purposeStatus

The plan against the running configuration. match: every planned observable purpose runs on this interface or one stacked on it. mismatch: see purposeMismatchReasons. unverified: no signal for a planned purpose (oob is never observable). pending-ceph: the planned storage purpose is not in use yet because Ceph is not set up, and nothing else is unknown; neutral. undeclared: the plan gives it none.

string
Allowed values: match mismatch unverified pending-ceph undeclared
purposeMismatchReasons
Array<string>
connectionStatus
required
Array<object>

Observed apply-state of one declared connection.

object
name
required
string
interface
required
string
present

The interface exists on the host.

boolean
up
boolean
addresses
Array<string>
lastError
string
rolledBack
boolean
lastApplied
string
reportedAt
string format: date-time
fabric

The node-level half of the interface role attribution, as the node itself reported it (never a peer’s view): what its topology declares and every signal the observed roles were derived from, each with whether it could be read. An interface with no observed roles only means “carries nothing” when the signals it would have come from were observed.

Absent when the node’s host-helper predates it.

object
observedAt
required

When the node’s host-helper read the signals.

string format: date-time
declared

This node’s traffic-class bindings as cluster.yaml resolves them (an explicit networks entry, else the deprecated data_ip alias, else collapse to mgmt_ip). Empty when the topology is not observed.

Array<object>

One traffic class’s declared binding on a node.

object
role
required
string
Allowed values: mgmt storage overlay
ip

The address the class uses on this node.

string
interface

The declared link, when cluster.yaml names one. Absent means the role sits on whichever interface holds ip.

string
source

Where the address comes from: an explicit networks.<class> entry, the deprecated data_ip alias, or mgmt_ip (the mgmt class itself, or a class with no binding of its own collapsing to it, -TOPO-2).

string
Allowed values: networks data_ip mgmt_ip
signals
required
Array<object>

One input to a node’s interface role attribution and whether it could be read.

object
name
required

cluster-topology is this node’s entry in cluster.yaml; the others are the node’s own OVS external_ids and Ceph config options.

string
Allowed values: cluster-topology ovn-encap-ip ovn-bridge-mappings ceph-public-network ceph-cluster-network
state
required

observed: read, with a value. unset: read, and nothing is configured (OVS has no such key, Ceph has no such option, the node has no Ceph cluster at all because its ceph.conf names none, the node is not in cluster.yaml). unobserved: could not be read — UNKNOWN, so nothing is claimed from it, neither a role nor a mismatch.

string
Allowed values: observed unset unobserved
value

The value read, when observed.

string
detail

Why a signal is unset or unobserved (the probe’s own first line of output), e.g. “ovs-vsctl get external_ids:ovn-encap-ip: ovs-vsctl: unix:/var/run/openvswitch/db.sock: database connection failed”.

string

Example

{
"connections": [
{
"kind": "ethernet",
"trafficClass": "",
"bond": {
"mode": "802.3ad",
"lacpRate": "slow"
},
"purposes": [
"oob"
]
}
],
"interfaces": [
{
"kind": "physical",
"declaredRoles": [
"mgmt"
],
"observedRoles": [
"mgmt"
],
"unverifiedRoles": [
"mgmt"
],
"pendingRoles": [
"storage"
],
"roleStatus": "match",
"purposes": [
"oob"
],
"observedPurposes": [
"oob"
],
"purposeStatus": "match"
}
],
"fabric": {
"declared": [
{
"role": "mgmt",
"ip": "192.0.2.111",
"interface": "vlan91",
"source": "networks"
}
],
"signals": [
{
"name": "cluster-topology",
"state": "observed",
"value": "192.0.2.111"
}
]
}
}

Authenticated but not authorized.

Media typeapplication/problem+json
object
type
required
string format: uri
title
required
string
status
required
integer
>= 400 <= 599
detail
string
instance
string format: uri-reference
requestId
required
string
code
required
string
issues

The findings of a rejected IAM policy document (custom and resource policies).

Array<object>

One finding of a rejected IAM policy document. A rejection lists every finding, so the author fixes the document in one round trip.

object
code
required

The finding, e.g. INVALID_DOCUMENT, UNKNOWN_ACTION, ACTION_NOT_APPLICABLE.

string
statement
required

Index of the statement at fault; -1 for the document as a whole.

integer
>= -1
detail
required
string
action

The offending action, where one is at fault (ACTION_NOT_APPLICABLE).

string
errors

Field errors, for codes that carry them: each problem at its field (see FieldError). Documented on the responses that set it, e.g. approveNodeJoin’s INVALID_INPUT and JOIN_APPROVAL_REFUSED. Absent otherwise.

Array<object>

One field’s problem, the stage-2 installer API’s shape. field is the field’s path, message what is wrong with it.

object
field
required

The field’s path, e.g. links[0].interface, disks[1].confirm, mgmt.address or role; empty for the whole request.

string
message
required
string
details

Structured facts about the refusal, for codes that carry them. The shape is fixed per code and documented on the response that answers it, e.g. IMAGE_POOL_MISMATCH (see ImagePoolMismatchDetails) and IMAGE_IN_USE. Absent for codes that carry none.

object
key
additional properties
any

Example

{
"issues": [
{
"code": "ACTION_NOT_APPLICABLE",
"action": "compute:CreateVpc"
}
]
}