Common snappi constructs
Overview
Every object in snappi can be serialized to or deserialized from a JSON string which conforms to Open Traffic Generator API. This facilitates storing traffic configurations as JSON files and reusing them in API calls with or without further modifications.
import snappi
api = snappi.api()
config = api.config()
config.ports.port(name='p1', location='localhost:5555')
config.flows.flow(name='f1')
- Serialize to JSON (or python dictionary or YAML)
json_str = config.serialize()
# serialize child of config object to JSON string
json_str = config.ports.serialize()
yaml_str = config.serialize(encoding=config.YAML)
obj_dict = config.serialize(encoding=config.DICT)
- Deserialize from JSON (or python dictionary or YAML)
# whether the argument is JSON or YAML or dict is automatically determined
config.deserialize('{"ports": [{"name": "p2", "location": "localhost:5556"}]}')
# deserialize child of config object from JSON string
config.flows.deserialize('[{"name": "f1"}]')
config.deserialize({"ports": [{"name": "p1", "location": "localhost:5555"}]})
config.deserialize('ports:\n- name: p1\n location: localhost:5555\n')
- Pass either snappi object or equivalent JSON string as argument to API calls
config = api.config()
config.ports.port(name='p1', location='localhost:5555')
# config will be serialized to JSON string and sent on wire
api.set_config(config)
json_str = '{"ports": [{"name": "p1", "location": "localhost:5555"}]}'
# JSON string will be directly sent on wire
api.set_config(json_str)
Following sections discuss most commonly used constructs in snappi comparing each one of them with equivalent JSON snippet.
For brevity, snippet for config creation is not included (since it's the same across all).
Flows
This section deals with flow configuration and control.
Unidirectional Flow
A simple unidirectional flow for a **one-arm** test.
snappi
|
json
|
p1 = config.ports.port(name='p1', \
location='localhost:5555')[-1]
f1 = config.flows.flow(name='f1')[-1]
f1.tx_rx.port.tx_name = p1.name
|
{
"ports": [
{
"location": "localhost:5555",
"name": "p1"
}
],
"flows": [
{
"name": "f1",
"tx_rx": {
"port": {
"tx_name": "p1"
},
"choice": "port"
}
}
]
}
|
Bidirectional Flows
A bi-directional flow between two ports.
snappi | json |
---|
p1, p2 = ( \
config.ports \
.port(name='p1', location='localhost:5555') \
.port(name='p2', location='localhost:5556')
)
f1, f2 = config.flows.flow(name='flow p1->p2'). \
flow(name='flow p2->p1')
f1.tx_rx.port.tx_name = p1.name
f1.tx_rx.port.rx_name = p2.name
f2.tx_rx.port.tx_name = p2.name
f2.tx_rx.port.rx_name = p1.name
|
{
"ports": [
{
"location": "localhost:5555",
"name": "p1"
},
{
"location": "localhost:5556",
"name": "p2"
}
],
"flows": [
{
"name": "flow p1->p2",
"tx_rx": {
"port": {
"tx_name": "p1",
"rx_name": "p2"
},
"choice": "port"
}
},
{
"name": "flow p2->p1",
"tx_rx": {
"port": {
"tx_name": "p2",
"rx_name": "p1"
},
"choice": "port"
}
}
]
}
|
Meshed Flows
Fully meshed flows between four ports. Each port sends flows to all the ports (except itself). This example is for four ports, it can be easily extended to an arbitrary number of ports.
snappi | json |
---|
import itertools
for i in range(1, 4):
config.ports.port(name='p%d' % i, \
location='localhost:%d' % (5554 + i))
for tx, rx in \
itertools.permutations([p.name for \
p in config.ports], 2):
f = config.flows.flow(name='flow %s->%s' \
% (tx, rx))[-1]
f.tx_rx.port.tx_name = tx
f.tx_rx.port.rx_name = rx
|
{
"ports": [
{
"location": "localhost:5555",
"name": "p1"
},
{
"location": "localhost:5556",
"name": "p2"
},
{
"location": "localhost:5557",
"name": "p3"
}
],
"flows": [
{
"name": "flow p1->p2",
"tx_rx": {
"port": {
"tx_name": "p1",
"rx_name": "p2"
},
"choice": "port"
}
},
{
"name": "flow p1->p3",
"tx_rx": {
"port": {
"tx_name": "p1",
"rx_name": "p3"
},
"choice": "port"
}
},
{
"name": "flow p2->p1",
"tx_rx": {
"port": {
"tx_name": "p2",
"rx_name": "p1"
},
"choice": "port"
}
},
{
"name": "flow p2->p3",
"tx_rx": {
"port": {
"tx_name": "p2",
"rx_name": "p3"
},
"choice": "port"
}
},
{
"name": "flow p3->p1",
"tx_rx": {
"port": {
"tx_name": "p3",
"rx_name": "p1"
},
"choice": "port"
}
},
{
"name": "flow p3->p2",
"tx_rx": {
"port": {
"tx_name": "p3",
"rx_name": "p2"
},
"choice": "port"
}
}
]
}
|
Simple flow with Ethernet, IP and TCP protocol headers.
snappi | json |
---|
p1 = config.ports.port(name='p1', \
location='localhost:5555')[-1]
f1 = config.flows.flow(name='f1')[-1]
f1.tx_rx.port.tx_name = p1.name
eth, ip, tcp = f1.packet.ethernet().ipv4().tcp()
eth.dst.value = '00:00:00:00:00:AA'
ip.dst.value = '192.168.1.1'
tcp.dst_port.value = 5000
|
{
"ports": [
{
"location": "localhost:5555",
"name": "p1"
}
],
"flows": [
{
"name": "f1",
"tx_rx": {
"port": {
"tx_name": "p1"
},
"choice": "port"
},
"packet": [
{
"ethernet": {
"dst": {
"value": "00:00:00:00:00:AA",
"choice": "value"
}
},
"choice": "ethernet"
},
{
"ipv4": {
"dst": {
"value": "192.168.1.1",
"choice": "value"
}
},
"choice": "ipv4"
},
{
"tcp": {
"dst_port": {
"value": 5000,
"choice": "value"
}
},
"choice": "tcp"
}
]
}
]
}
|
Flow with Ethernet, IP and TCP headers. Ethernet destination MAC address, destination IP address and TCP destination port are varied using patterns.
snappi | json |
---|
p1 = config.ports.port(name='p1', \
location='localhost:5555')[-1]
f1 = config.flows.flow(name='f1')[-1]
f1.tx_rx.port.tx_name = p1.name
eth, ip, tcp = f1.packet.ethernet().ipv4().tcp()
eth.src.value = '00:00:00:00:00:AA'
eth.dst.values = ['00:00:00:00:00:AB', \
'00:00:00:00:00:AC']
ip.src.value = '192.168.1.1'
ip.dst.increment.start = '192.168.1.2'
ip.dst.increment.step = '0.0.0.1'
ip.dst.increment.count = 2
tcp.src_port.value = 5000
tcp.dst_port.decrement.start = 5002
tcp.dst_port.decrement.step = 1
tcp.dst_port.decrement.count = 2
tcp.seq_num.values = [1, 2]
|
{
"ports": [
{
"location": "localhost:5555",
"name": "p1"
}
],
"flows": [
{
"name": "f1",
"tx_rx": {
"port": {
"tx_name": "p1"
},
"choice": "port"
},
"packet": [
{
"ethernet": {
"src": {
"value": "00:00:00:00:00:AA",
"choice": "value"
},
"dst": {
"values": [
"00:00:00:00:00:AB",
"00:00:00:00:00:AC"
],
"choice": "values"
}
},
"choice": "ethernet"
},
{
"ipv4": {
"src": {
"value": "192.168.1.1",
"choice": "value"
},
"dst": {
"increment": {
"start": "192.168.1.2",
"step": "0.0.0.1",
"count": 2
},
"choice": "increment"
}
},
"choice": "ipv4"
},
{
"tcp": {
"src_port": {
"value": 5000,
"choice": "value"
},
"dst_port": {
"decrement": {
"start": 5002,
"step": 1,
"count": 2
},
"choice": "decrement"
},
"seq_num": {
"values": [
1,
2
],
"choice": "values"
}
},
"choice": "tcp"
}
]
}
]
}
|
Start Flow Transmit
Start transmit on a certain set of flows.
snappi | json |
---|
ts = api.control_state()
ts.traffic.flow_transmit.state = ts.traffic.flow_transmit.START # noqa
ts.traffic.flow_transmit.flow_names = ['f1', 'f2']
res = api.set_control_state(ts)
|
{
"choice": "traffic",
"traffic": {
"choice": "flow_transmit",
"flow_transmit": {
"flow_names": [
"f1",
"f2"
],
"state": "start"
}
}
}
|
Capture
Capture configuration and control
Capture Configuration
Configure capture prior to starting capture.
Start Capture
Start capture on a set of ports.
snappi | json |
---|
cs = api.control_state()
cs.port.capture.state = cs.port.capture.START
cs.port.capture.port_names = ['p1', 'p2']
res = api.set_control_state(cs)
|
{
"choice": "port",
"port": {
"capture": {
"port_names": [
"p1",
"p2"
],
"state": "start"
},
"choice": "capture"
}
}
|
Get Capture
Retrieve capture for a given port. Save capture to a .pcap file (python only).
snappi | json |
---|
req = api.capture_request()
req.port_name = 'p1'
with open('capture.pcap', 'w') as pcap:
pcap.write(api.get_capture(req).read())
|
|
Metrics
Port Metrics
Get port statistics for a given set of ports.
snappi | json |
---|
req = api.metrics_request()
req.port.port_names = ['tx', 'rx']
req.port.column_names = [req.port.FRAMES_TX, \
req.port.FRAMES_RX]
res = api.get_metrics(req)
assert res[0].frames_tx == res[1].frames_rx
|
{
"port": {
"port_names": [
"p1",
"p2"
],
"column_names": [
"frames_tx",
"frames_rx"
]
},
"choice": "port"
}
|
Flow Metrics
Get flow statistics.
Blah
TBD
- how to create a flow with certain protocol headers
- how to create a flow to test 5-tuple hashing
- how to create flows with changing flow sizes
- how to create stacked vlans
- creating bursty flows
- how to disable timestamps, signature