Skip to content

Flows

A flow is one procedure, written as a state machine in YAML:

  • each simulated UE runs the flow from its initial state;
  • each state names the events it waits for, what to do when one arrives, and where to go next;
  • the run passes when every UE ends in a final state the flow considers a success, and every check holds.

You write flows; you don't write Go. A library of ready-to-run flows is in the public d3x-templates repository.

A client flow

A gNB registers a UE with the AMF under test. This is shortened from gnb/registration.yaml in d3x-templates:

YAML

Top-level keys

Keys are checked when the flow loads, at every level: a key rk doesn't know, such as a misspelt mesage: in an action, stops the load with the file, the line and the key, and the nearest known key when there is one.

KeyRequiredWhat it is
kindyesflow
nameyesthe name you run it by (-flow registration)
typeyesclient: rk starts it; server: rk answers with it
protocolyesngap, sbi, diameter, rest, pfcp or gtpu
nf / nfsyesthe role or roles rk plays: gnb, amf, smf, udm, and so on; the first of nfs is the main one
initial_stateyeswhere each UE starts
final_statesyeswhere a UE can end
statesyesthe state machine
any_state_transitionstransitions tried in every state, after the state's own
final_checkschecks on the whole run's metrics
unsupported_statesfinal states that mean "the target doesn't support this"
hold_statewhere a UE waits during a held load run (client flows)
loada stepped load run (client flows); see Load runs
fuzzmutate messages (licence feature robustness.fuzz); see Fuzzing
dutthe role of the real NF under test, for the report
bindingfor a server flow: which environment NF it answers on, when several share its role
categoryfunctional, negative, robustness, stability, lifecycle, load or stress
description, detail, findingtext for people and reports

Rules for type and protocol:

  • A client flow needs a Start transition in its initial state.
  • A server flow must not have one, and needs at least one transition there.
  • gtpu is allowed only for a client gNB or a server UPF (the GTP-U reflector).

States and transitions

A state has transitions, and optionally on_timeout. A transition has:

  • event: what it waits for;
  • target: the next state. _self re-enters the same state, which also restarts its timeout;
  • actions: what to do, in order.

Events. An event is a message name, such as NASDownlinkTransport.AuthenticationRequest or sbi.Nudm_SDM_GetSubscriptionData. Combine events with {and: [...]} or {or: [...]}. rk also posts its own events:

EventWhen
Starta client UE begins
Errora transport or action failed
StateTimeoutthe state's timeout fired
UplaneCompleteuser-plane traffic finished
HoldReleasea held UE is released
ReflectorIdlea GTP-U reflector went idle

Order of matching:

  1. the state's own transitions; the first match wins;
  2. any_state_transitions;
  3. a few built-in defaults:

    • an NGAP ErrorIndication goes to failed, if the flow has that state;
    • a gNB answers UEContextReleaseCommand and acknowledged ConfigurationUpdateCommand by itself, and stays in its state.

A message nothing matches is counted in the report but doesn't move the UE.

Timeouts. on_timeout: {duration, target, actions} starts when the state is entered. When it fires, its actions run, then the UE goes to target. There's no separate wait action: a timeout with actions is how a flow waits.

When a UE fails. A UE that enters the final state named failed counts as failed; any other final state counts as a success. A failing check, extract or uplane_start also fails the UE at once.

Actions

Inside a transition, check, extract, cmd and uplane_start run in the order written. All send actions go out together at the end of the transition, so a check or extract can't come after a send.

typeFieldsWhat it does
sendmessage, message_body, params, peer, nf, protocolsends a message. message_body is JSON and may use templates; unknown keys are refused when the flow loads
parallel_sendmessage, count, concurrencysends one message count times, concurrency at a time (0 = all at once). It must be the only send in its transition. Results go into burst_total, burst_2xx, burst_4xx, burst_429, burst_5xx and burst_errors
checkfield, op, expectedchecks a field of the received message
extractfield or value, store; or extracts: [...]stores a value in the UE's params; a missing field fails the UE
uplane_startnf, paramsstarts user-plane traffic; see User plane
ngap_realloc, ngap_handover_swapnoneNGAP-specific steps of reallocation and handover flows
cmdargv, cwd, env, timeout, ignore_error, store_stdout, store_stderrruns a local program, without a shell. Off unless -allow-cmd-actions is given. cwd defaults to the flow file's directory, timeout to 10s

check operators: equals, not_equals, not_empty, greater_than, less_than, greater_or_equal, less_or_equal, contains, not_contains, exists, regex, in, oneof, length, ie_present.

final_checks compare a run metric with op:

  • the operator is one of equals, greater_than, less_than, greater_or_equal, less_or_equal;
  • the other side is a value, or another metric (value_metric, where value then acts as a multiplier).

Metrics include:

  • the report's metrics, such as flows_failed and p99_latency_ms;
  • final_state_counts.<state>;
  • msg_types.tx|rx.<protocol>.<Type>;
  • rx_events.<Event>.

A check on a metric the run didn't produce fails.

Templates: {{...}}

Message bodies, params, check expected values, extract values, cmd arguments and peer can use templates:

  • {{path}} looks a value up:

    • ue.*: the UE's state, such as ue.AmfUeNgapId, ue.PduAddress or ue.UplaneReport.<key>, and its params in ue.Params.<key> (also vars.<key>);
    • rest.* and sbi.*: the last HTTP message's status, headers.X and body.<path>;
    • pfcp.*, nas.*;
    • gnb.n3_addr;
    • a field of the last NGAP message, or a Diameter AVP by name.
  • {{$function args}} calls a function:

    • Time: now, now_ms, now_rfc3339, now_minus <dur>, now_plus <dur>, now_format <layout>.
    • Random: uuid, randint <lo> <hi>, random_bytes <n>, random_hex <n>, random_string <n>.
    • Encoding: hex, b64, b64decode, b64_u32, b64_ipv4, b64_ipv6, jwt_decode. jwt_decode doesn't verify the signature.
    • Other: padding <n>, inc <name> (a counter per UE), regex_replace, regex_extract, $n3_teid.

A template that is the whole value keeps its type, so a number stays a number. An unknown function is an error when the flow loads.

The verdict

A flow run passes unless one of these happened:

  • the run errored, or no UE started;
  • a UE didn't complete;
  • a UE failed or ended in a state that isn't declared final;
  • a check or a final check failed.

It's unsupported when every completed UE ended in one of its unsupported_states. A mix of unsupported and other endings fails.

Inspect flows

  • rk describe -flow <file> summarizes a flow (add -output json for JSON).
  • rk lint -flow <file> validates one, unknown keys included.
  • rk schema prints the JSON Schema of flows and suites.
  • rk vocab lists the message names, check operators, internal events and metric keys rk knows.