tests: add enhanced packet counter verification

Add support for inline packet counter verification to send_and_* functions.

Diff dictionary is a dictionary of dictionaries of interesting stats:

    diff_dictionary =
    {
        "err" : { '/error/counter1' : 4, },
        sw_if_index1 : { '/stat/segment/counter1' : 5,
                         '/stat/segment/counter2' : 6,
                       },
        sw_if_index2 : { '/stat/segment/counter1' : 7,
                       },
    }

It describes a per sw-if-index diffset, where each key is stat segment
path and value is the expected change for that counter for sw-if-index.
Special case string "err" is used for error counters.

This then allows more precise packet counter verification by first
defining a "zero" dictionary, e.g. for ED NAT:

        cls.no_diff = StatsDiff({
            pg.sw_if_index: {
                '/nat44-ed/in2out/fastpath/tcp': 0,
                '/nat44-ed/in2out/fastpath/udp': 0,
                '/nat44-ed/in2out/fastpath/icmp': 0,
                '/nat44-ed/in2out/fastpath/drops': 0,
                '/nat44-ed/in2out/slowpath/tcp': 0,
                '/nat44-ed/in2out/slowpath/udp': 0,
                '/nat44-ed/in2out/slowpath/icmp': 0,
                '/nat44-ed/in2out/slowpath/drops': 0,
                '/nat44-ed/in2out/fastpath/tcp': 0,
                '/nat44-ed/in2out/fastpath/udp': 0,
                '/nat44-ed/in2out/fastpath/icmp': 0,
                '/nat44-ed/in2out/fastpath/drops': 0,
                '/nat44-ed/in2out/slowpath/tcp': 0,
                '/nat44-ed/in2out/slowpath/udp': 0,
                '/nat44-ed/in2out/slowpath/icmp': 0,
                '/nat44-ed/in2out/slowpath/drops': 0,
            }
            for pg in cls.pg_interfaces
        })

and then to specify only changed counters directly when calling
one of send_and_* functions:

        self.send_and_assert_no_replies(
            self.pg0, pkts, msg="i2o pkts",
            stats_diff=self.no_diff | {
                "err": {
                    '/err/nat44-ed-in2out-slowpath/out of ports': len(pkts),
                },
                self.pg0.sw_if_index: {
                    '/nat44-ed/in2out/slowpath/drops': len(pkts),
                },
            }
        )

operator | is overloaded by StatsDiff class to perform a deep merge operation,
so in above case, dictionaries for "err" and self.pg0.sw_if_index do not
overwrite whole sub-dictionaries, rather the contents are merged,
assuring that all the remaining counters are verified to be zero.

Type: improvement
Signed-off-by: Klement Sekera <klement.sekera@gmail.com>
Change-Id: I2b87f7bd58a7d4b34ee72344e2f871b2f372e2d9
This commit is contained in:
Klement Sekera
2022-02-18 10:34:35 +00:00
committed by Neale Ranns
parent 107ad73e1b
commit ad3187fe23
3 changed files with 174 additions and 39 deletions

View File

@ -1,12 +1,11 @@
""" test framework utilities """
import abc
import ipaddress
import logging
import socket
from socket import AF_INET6
import sys
import os.path
from copy import deepcopy
import scapy.compat
from scapy.layers.l2 import Ether
@ -452,3 +451,50 @@ def reassemble4_ether(listoffragments):
def reassemble4(listoffragments):
return reassemble4_core(listoffragments, True)
def recursive_dict_merge(dict_base, dict_update):
"""Recursively merge base dict with update dict, return merged dict"""
for key in dict_update:
if key in dict_base:
if type(dict_update[key]) is dict:
dict_base[key] = recursive_dict_merge(dict_base[key],
dict_update[key])
else:
dict_base[key] = dict_update[key]
else:
dict_base[key] = dict_update[key]
return dict_base
class StatsDiff:
"""
Diff dictionary is a dictionary of dictionaries of interesting stats:
diff_dictionary =
{
"err" : { '/error/counter1' : 4, },
sw_if_index1 : { '/stat/segment/counter1' : 5,
'/stat/segment/counter2' : 6,
},
sw_if_index2 : { '/stat/segment/counter1' : 7,
},
}
It describes a per sw-if-index diffset, where each key is stat segment
path and value is the expected change for that counter for sw-if-index.
Special case string "err" is used for error counters, which are not per
sw-if-index.
"""
def __init__(self, stats_diff={}):
self.stats_diff = stats_diff
def update(self, sw_if_index, key, value):
if sw_if_index in self.stats_diff:
self.stats_diff[sw_if_index][key] = value
else:
self.stats_diff[sw_if_index] = {key: value}
def __or__(self, other):
return recursive_dict_merge(deepcopy(self.stats_diff), other)