Skip to content

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.

  • Create a sample config
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.
snappijson
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.
snappijson
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"
      }
    }
  ]
}

Protocol Headers With Fixed Fields

Simple flow with Ethernet, IP and TCP protocol headers.
snappijson
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"
        }
      ]
    }
  ]
}

Protocol Headers With Varying Fields

Flow with Ethernet, IP and TCP headers. Ethernet destination MAC address, destination IP address and TCP destination port are varied using patterns.
snappijson
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.
snappijson
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.
snappijson
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).
snappijson
req = api.capture_request()
req.port_name = 'p1'

with open('capture.pcap', 'w') as pcap:
  pcap.write(api.get_capture(req).read())
{
  "port_name": "p1"
}

Metrics

Port Metrics

Get port statistics for a given set of ports.
snappijson
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