Reachability analysis with the full OVN substrate view.
curl --request POST \ --url https://stratum.example.com/v1/admin/network/ovn/reachability-analyses \ --header 'Authorization: Bearer <token>' \ --header 'Content-Type: application/json' \ --data '{ "source": { "kind": "instance-nic", "address": "example", "port": "example", "nicId": "example", "vpcId": "example", "id": "example" }, "destination": { "kind": "instance-nic", "address": "example", "port": "example", "nicId": "example", "vpcId": "example", "id": "example" }, "protocol": "tcp", "port": 1, "trace": false }'The operator’s reachability analysis: the same pipeline model as the project-scoped endpoint, projected with nothing removed — OVN object names, ACL and routing-policy priorities, verbatim match expressions, NAT rows, and the platform transit carves.
Separate from the tenant endpoint rather than the same one with a verbosity flag, because the difference is not verbosity. This surface can ask questions the tenant surface must never be able to express: from a platform port (IMDS, the DNS recursor, a subnet probe, any tenant’s load-balancer data plane), between projects, or by raw OVN port name.
Endpoints are therefore given as fabric coordinates — an address, and optionally the logical switch port that carries it — rather than as project resources. Nothing here consults the database.
Answers 200 for every verdict including blocked: a closed path is a
successful analysis. Install-scope admin (network:GetOvnTopology), the
same read-only gate as the topology and doctor endpoints.
Authorizations
Section titled “Authorizations”Request Bodyrequired
Section titled “Request Bodyrequired”object
A fabric coordinate. address is what the pipeline is evaluated for; port
names the OVN logical switch port directly when you already know it,
otherwise nicId resolves it, otherwise it is resolved from the address.
object
Advisory. Changes how the model treats a destination that legitimately leaves the fabric. Defaults to address.
OVN logical switch port name.
A fabric coordinate. address is what the pipeline is evaluated for; port
names the OVN logical switch port directly when you already know it,
otherwise nicId resolves it, otherwise it is resolved from the address.
object
Advisory. Changes how the model treats a destination that legitimately leaves the fabric. Defaults to address.
OVN logical switch port name.
Corroborate the verdict against the dataplane with ofproto/trace on the chassis hosting the ingress port. Shells out, so it is opt-in.
Responses
Section titled “Responses”The analysis, with the substrate view.
object
The resolved question, with each endpoint’s address and OVN port filled in.
object
undetermined is not a soft form of either other value. A path the
analyzer could not model, rendered as reachable, talks an operator out of
investigating a real outage; rendered as blocked, it sends them hunting
for a rule that does not exist.
The KIND of block, so a caller can branch without parsing prose. The specific offending object is on the blocking hop.
flow_log_capture is split from acl_deny because the repair differs: the
priority-501 capture ACL is the only band matching an explicit port name
rather than a port group, so on a port with no security group there is no
default-deny beneath it and the capture becomes that port’s entire policy. Reporting it as a generic ACL denial sends an operator looking
for a security-group rule on a port that has no security group at all.
no_return_route likewise: the forward path is open and the reply has
nowhere to go. Per routes are required in BOTH VPCs.
The one sentence. An operator who reads nothing else should be able to act on it — it names the object, the rule that acted, and the precondition that made it act.
object
undetermined is not a soft form of either other value. A path the
analyzer could not model, rendered as reachable, talks an operator out of
investigating a real outage; rendered as blocked, it sends them hunting
for a rule that does not exist.
The KIND of block, so a caller can branch without parsing prose. The specific offending object is on the blocking hop.
flow_log_capture is split from acl_deny because the repair differs: the
priority-501 capture ACL is the only band matching an explicit port name
rather than a port group, so on a port with no security group there is no
default-deny beneath it and the capture becomes that port’s entire policy. Reporting it as a generic ACL denial sends an operator looking
for a security-group rule on a port that has no security group at all.
no_return_route likewise: the forward path is open and the reply has
nowhere to go. Per routes are required in BOTH VPCs.
object
The pipeline stage, in the order OVN runs them. switch-load-balancer
runs BEFORE router-policy — a load balancer attached to a switch
rewrites the destination before the router sees the packet, which is
the ordering turned on.
The OVN object a hop passed through.
object
OVN kind — logical-switch-port, logical-router, acl, route-policy, static-route, nat, load-balancer, peering-link, localnet, external.
The OVN name, or the row uuid when the row is unnamed.
The Stratum resource behind an OVN row, recovered from its external_ids. Absent when the row carries no stratum-* stamp, which means it is operator-managed or pre-Stratum OVN content rather than something with an owner to name.
object
The rule’s priority for ACLs and routing policies, where it is the whole reason one rule won over another.
The OVN match expression, verbatim, so it can be pasted into ovn-nbctl.
One line explaining the hop, including why this rule won — a priority comparison, a longest-prefix match, or a precondition that did not hold.
Index into hops of the hop that dropped the packet.
The reply’s path, evaluated whenever the forward path completed. Not optional analysis: a forward path that is open and a reply that cannot get home is not a reachable path, and reporting it as one is the most dangerous wrong answer this endpoint could give.
object
undetermined is not a soft form of either other value. A path the
analyzer could not model, rendered as reachable, talks an operator out of
investigating a real outage; rendered as blocked, it sends them hunting
for a rule that does not exist.
The KIND of block, so a caller can branch without parsing prose. The specific offending object is on the blocking hop.
flow_log_capture is split from acl_deny because the repair differs: the
priority-501 capture ACL is the only band matching an explicit port name
rather than a port group, so on a port with no security group there is no
default-deny beneath it and the capture becomes that port’s entire policy. Reporting it as a generic ACL denial sends an operator looking
for a security-group rule on a port that has no security group at all.
no_return_route likewise: the forward path is open and the reply has
nowhere to go. Per routes are required in BOTH VPCs.
object
The pipeline stage, in the order OVN runs them. switch-load-balancer
runs BEFORE router-policy — a load balancer attached to a switch
rewrites the destination before the router sees the packet, which is
the ordering turned on.
The OVN object a hop passed through.
object
OVN kind — logical-switch-port, logical-router, acl, route-policy, static-route, nat, load-balancer, peering-link, localnet, external.
The OVN name, or the row uuid when the row is unnamed.
The Stratum resource behind an OVN row, recovered from its external_ids. Absent when the row carries no stratum-* stamp, which means it is operator-managed or pre-Stratum OVN content rather than something with an owner to name.
object
The rule’s priority for ACLs and routing policies, where it is the whole reason one rule won over another.
The OVN match expression, verbatim, so it can be pasted into ovn-nbctl.
One line explaining the hop, including why this rule won — a priority comparison, a longest-prefix match, or a precondition that did not hold.
Index into hops of the hop that dropped the packet.
The dataplane’s second opinion on a modeled path, from
ovs-appctl ofproto/trace on the chassis hosting the ingress port.
It never changes verdict. A configuration model and a flow table
disagreeing is information, not an error to be resolved by preferring one
source — and a disagreement is the finding, because it is the only signal
that catches a fabric whose configuration is right and whose dataplane is
not (’s chassis with zero geneve tunnels, ’s dead
ovn-controller).
object
tunneled means this chassis forwarded the packet into a geneve tunnel,
so only the SOURCE side is confirmed — the destination port’s policy is
evaluated on the chassis hosting that port and this trace cannot see it.
It is deliberately not delivered: reading it as delivery reports a
confident disagreement against a model that is right, on every cross-node
path whose destination policy drops the packet.
not-run carries the reason in detail — most often that another
chassis owns the ingress port, since a trace run anywhere else reads a
different flow table and returns something that looks exactly like an
answer. inconclusive means the trace ran and its output could not be
read; it is never collapsed into agreement.
The chassis owning the ingress port — the only one that can answer for it.
The chassis that served this request.
The chassis hosting the destination port, when it is a different one. On a tunneled outcome it names where the other half of the pipeline runs.
Present only when a trace produced a readable outcome. Absent means “not compared”, which is deliberately distinct from false.
The flow handed to ofproto/trace, so it can be rerun by hand.
The trace output, whole. Truncating it is how the manual sessions behind this epic reached wrong conclusions.
What could not be evaluated, and why. Each entry stands alone.
Example
{ "verdict": "reachable", "reason": "acl_deny", "summary": "Blocked: interface nic-abc at 192.0.2.11 -> load balancer lb-xyz over tcp/443 — dropped by flow-log capture ACL fl-yjgehorkyh (priority 501), because port lbdp-lb-xyz has no security group.", "forward": { "direction": "forward", "verdict": "reachable", "reason": "acl_deny", "hops": [ { "stage": "source-port", "component": { "resource": { "type": "security-group", "id": "sg-2hipbqkonx" } }, "action": "forward" } ] }, "return": { "direction": "forward", "verdict": "reachable", "reason": "acl_deny", "hops": [ { "stage": "source-port", "component": { "resource": { "type": "security-group", "id": "sg-2hipbqkonx" } }, "action": "forward" } ] }, "caveats": [ "dataplane_not_verified" ], "corroboration": { "outcome": "delivered" }}Bad request.
object
The findings of a rejected IAM policy document (custom and resource policies).
One finding of a rejected IAM policy document. A rejection lists every finding, so the author fixes the document in one round trip.
object
The finding, e.g. INVALID_DOCUMENT, UNKNOWN_ACTION, ACTION_NOT_APPLICABLE.
Index of the statement at fault; -1 for the document as a whole.
The offending action, where one is at fault (ACTION_NOT_APPLICABLE).
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.
One field’s problem, the stage-2 installer API’s shape.
field is the field’s path, message what is wrong with it.
object
The field’s path, e.g. links[0].interface, disks[1].confirm,
mgmt.address or role; empty for the whole request.
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
Example
{ "issues": [ { "code": "ACTION_NOT_APPLICABLE", "action": "compute:CreateVpc" } ]}Authenticated but not authorized.
object
The findings of a rejected IAM policy document (custom and resource policies).
One finding of a rejected IAM policy document. A rejection lists every finding, so the author fixes the document in one round trip.
object
The finding, e.g. INVALID_DOCUMENT, UNKNOWN_ACTION, ACTION_NOT_APPLICABLE.
Index of the statement at fault; -1 for the document as a whole.
The offending action, where one is at fault (ACTION_NOT_APPLICABLE).
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.
One field’s problem, the stage-2 installer API’s shape.
field is the field’s path, message what is wrong with it.
object
The field’s path, e.g. links[0].interface, disks[1].confirm,
mgmt.address or role; empty for the whole request.
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
Example
{ "issues": [ { "code": "ACTION_NOT_APPLICABLE", "action": "compute:CreateVpc" } ]}