Ask whether a source can reach a destination, and if not, which hop drops it.
curl --request POST \ --url https://stratum.example.com/v1/projects/example/network/reachability-analyses \ --header 'Authorization: Bearer <token>' \ --header 'Content-Type: application/json' \ --data '{ "source": { "kind": "instance-nic", "id": "example", "address": "example" }, "destination": { "kind": "instance-nic", "id": "example", "address": "example" }, "protocol": "tcp", "port": 1 }'Evaluates a packet through the modelled OVN pipeline and returns the
hop-by-hop path with a verdict: reachable with the path, or blocked
naming the exact rule — the ACL and its priority, the routing policy, a
missing route, an absent NAT, an unbound port.
The pipeline is modelled in the order OVN runs it, because that ordering is
where the bugs behind this endpoint lived: a load balancer attached to a
switch rewrites the destination before the router stage evaluates any
routing policy, so a policy keyed on the pre-rewrite address is looking at
an address that no longer exists. Routing-policy priorities are
1000 + prefix length, so a peering route to a /16 sits at 1016 and does
outrank the 0.0.0.0/0 catch-all at 1000 — “the catch-all outranked it”
is a tempting wrong answer.
The return path is evaluated too, whenever the forward path completed.
A forward path that is open and a reply that cannot get home is not a
reachable path peering routes are required in BOTH
VPCs, and reporting one-way wiring as reachable is the most dangerous
wrong answer this endpoint could give.
What it does not do
This is configuration analysis, not a packet. It reads the OVN northbound
database; it is blind to a dataplane that does not match its
configuration — a chassis with zero geneve tunnels, a dead
ovn-controller, a stale flow table. Every non-blocked verdict
therefore carries the dataplane_not_verified caveat, and
GET /v1/admin/network/ovn/doctor is the surface that checks the
substrate’s own health.
Connection tracking is not modelled either, so allow-related return
traffic and a load balancer’s reverse translation are assumed rather than
proven; both are stated as caveats rather than left implicit.
A path the analyzer cannot model answers undetermined with the reason,
never a guess. Answers 200 for every verdict, including blocked: a
closed path is a successful analysis, and using 4xx for it would stop a
caller telling “the path is shut” from “the request was wrong”.
Vocabulary
The answer is in the resources you configure — security group, route
table, internet gateway, floating IP — and never in the substrate that
implements them. A block also carries fault: configuration means it is
yours to fix, platform means Stratum’s own configuration caused it and
changing yours will not help.
The operator view of the same analysis, with the OVN objects, rule
priorities and match expressions, is
POST /v1/admin/network/ovn/reachability-analyses. One pipeline model
backs both, so they cannot disagree about a path.
Nothing is stored. The analysis is computed inline from current state, like the IAM policy simulator.
Authorizations
Section titled “Authorizations”Parameters
Section titled “Parameters”Path Parameters
Section titled “Path Parameters”Request Bodyrequired
Section titled “Request Bodyrequired”object
Names a source or destination before resolution. id is required for
every kind that names a Stratum resource; address is required for
address and optional elsewhere, where it selects which of a NIC’s
addresses the question is about.
object
The Stratum resource id (nic-…, lb-…, fip-…, pcx-…, cvpn-…, vpce-…).
Names a source or destination before resolution. id is required for
every kind that names a Stratum resource; address is required for
address and optional elsewhere, where it selects which of a NIC’s
addresses the question is about.
object
The Stratum resource id (nic-…, lb-…, fip-…, pcx-…, cvpn-…, vpce-…).
Destination port. Required for tcp and udp, ignored for icmp. Security-group rules are per-port, so a tcp verdict without a port would be a verdict about a packet nobody sends.
Responses
Section titled “Responses”The analysis.
A reachability analysis in tenant vocabulary. The same pipeline model backs
this and the operator view at /v1/admin/network/ovn/reachability-analyses;
only the projection differs, so the two can never disagree about a path.
object
The resolved question.
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.
Whose problem the block is.
platform means Stratum’s own configuration caused it and changing yours
will not help — the case this exists for is, where a platform
capture rule black-holed a load-balancer port and any tenant told “dropped
by an ACL” would have audited security groups that were perfectly correct.
The one sentence. Names the resource, the rule that acted, and — when the cause is the platform — says so outright.
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.
object
A Stratum resource, never an OVN row. platform is used for a step that has
no tenant-facing construct (a platform service rewrite, an internal guard),
so the path has no unexplained gaps.
object
One line, in the vocabulary you configure in.
Index into hops of the hop that dropped the packet.
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.
object
A Stratum resource, never an OVN row. platform is used for a step that has
no tenant-facing construct (a platform service rewrite, an internal guard),
so the path has no unexplained gaps.
object
One line, in the vocabulary you configure in.
Index into hops of the hop that dropped the packet.
Example
{ "verdict": "reachable", "reason": "acl_deny", "fault": "configuration", "summary": "Blocked: network interface nic-abc cannot reach network interface nic-def over tcp/3306 — security group sg-web has no inbound rule admitting this traffic.", "forward": { "direction": "forward", "verdict": "reachable", "hops": [ { "component": { "type": "network-interface" }, "action": "forward" } ] }, "return": { "direction": "forward", "verdict": "reachable", "hops": [ { "component": { "type": "network-interface" }, "action": "forward" } ] }, "caveats": [ "dataplane_not_verified" ]}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" } ]}Resource not found.
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" } ]}