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:
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.
| Key | Required | What it is |
|---|---|---|
kind | yes | flow |
name | yes | the name you run it by (-flow registration) |
type | yes | client: rk starts it; server: rk answers with it |
protocol | yes | ngap, sbi, diameter, rest, pfcp or gtpu |
nf / nfs | yes | the role or roles rk plays: gnb, amf, smf, udm, and so on; the first of nfs is the main one |
initial_state | yes | where each UE starts |
final_states | yes | where a UE can end |
states | yes | the state machine |
any_state_transitions | transitions tried in every state, after the state's own | |
final_checks | checks on the whole run's metrics | |
unsupported_states | final states that mean "the target doesn't support this" | |
hold_state | where a UE waits during a held load run (client flows) | |
load | a stepped load run (client flows); see Load runs | |
fuzz | mutate messages (licence feature robustness.fuzz); see Fuzzing | |
dut | the role of the real NF under test, for the report | |
binding | for a server flow: which environment NF it answers on, when several share its role | |
category | functional, negative, robustness, stability, lifecycle, load or stress | |
description, detail, finding | text for people and reports |
Rules for type and protocol:
- A client flow needs a
Starttransition in its initial state. - A server flow must not have one, and needs at least one transition there.
gtpuis 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._selfre-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:
| Event | When |
|---|---|
Start | a client UE begins |
Error | a transport or action failed |
StateTimeout | the state's timeout fired |
UplaneComplete | user-plane traffic finished |
HoldRelease | a held UE is released |
ReflectorIdle | a GTP-U reflector went idle |
Order of matching:
- the state's own transitions; the first match wins;
any_state_transitions;a few built-in defaults:
- an NGAP
ErrorIndicationgoes tofailed, if the flow has that state; - a gNB answers
UEContextReleaseCommandand acknowledgedConfigurationUpdateCommandby itself, and stays in its state.
- an NGAP
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.
type | Fields | What it does |
|---|---|---|
send | message, message_body, params, peer, nf, protocol | sends a message. message_body is JSON and may use templates; unknown keys are refused when the flow loads |
parallel_send | message, count, concurrency | sends 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 |
check | field, op, expected | checks a field of the received message |
extract | field or value, store; or extracts: [...] | stores a value in the UE's params; a missing field fails the UE |
uplane_start | nf, params | starts user-plane traffic; see User plane |
ngap_realloc, ngap_handover_swap | none | NGAP-specific steps of reallocation and handover flows |
cmd | argv, cwd, env, timeout, ignore_error, store_stdout, store_stderr | runs 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, wherevaluethen acts as a multiplier).
Metrics include:
- the report's metrics, such as
flows_failedandp99_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 asue.AmfUeNgapId,ue.PduAddressorue.UplaneReport.<key>, and its params inue.Params.<key>(alsovars.<key>);rest.*andsbi.*: the last HTTP message'sstatus,headers.Xandbody.<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_decodedoesn't verify the signature. - Other:
padding <n>,inc <name>(a counter per UE),regex_replace,regex_extract,$n3_teid.
- Time:
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 jsonfor JSON).rk lint -flow <file>validates one, unknown keys included.rk schemaprints the JSON Schema of flows and suites.rk vocablists the message names, check operators, internal events and metric keysrkknows.