X-Git-Url: https://gerrit.fd.io/r/gitweb?a=blobdiff_plain;f=test%2Futil.py;h=2c24571c3504918abb34b24f1ea8e13cb6a44aa2;hb=16ce09db9e3d7cf588037c80138438095a0ac200;hp=c86d602d79250762b5006bc0cff3480042d70999;hpb=2f1563129ad8d34d365f5ef8620ff76ff7b08e70;p=vpp.git diff --git a/test/util.py b/test/util.py index c86d602d792..2c24571c350 100644 --- a/test/util.py +++ b/test/util.py @@ -1,12 +1,12 @@ """ test framework utilities """ -import abc import ipaddress +import logging import socket from socket import AF_INET6 -import six -import sys import os.path +from copy import deepcopy +from collections import UserDict import scapy.compat from scapy.layers.l2 import Ether @@ -20,9 +20,17 @@ from scapy.utils6 import in6_mactoifaceid from io import BytesIO from vpp_papi import mac_pton +# Set up an empty logger for the testcase that can be overridden as necessary +null_logger = logging.getLogger('VppTestCase.util') +null_logger.addHandler(logging.NullHandler()) + + +def pr(packet): + return packet.__repr__() + def ppp(headline, packet): - """ Return string containing the output of scapy packet.show() call. """ + """ Return string containing headline and output of scapy packet.show() """ return '%s\n%s\n\n%s\n' % (headline, hexdump(packet, dump=True), packet.show(dump=True)) @@ -108,7 +116,7 @@ def check_core_path(logger, core_path): " current core pattern is: %s" % corefmt) -class NumericConstant(object): +class NumericConstant: desc_dict = {} @@ -127,7 +135,7 @@ class NumericConstant(object): return "" -class Host(object): +class Host: """ Generic test host "connected" to VPPs interface. """ @property @@ -199,19 +207,6 @@ class Host(object): self._ip6_ll = ip6_ll -class ForeignAddressFactory(object): - count = 0 - prefix_len = 24 - net_template = '10.10.10.{}' - net = net_template.format(0) + '/' + str(prefix_len) - - def get_ip4(self): - if self.count > 255: - raise Exception("Network host address exhaustion") - self.count += 1 - return self.net_template.format(self.count) - - class L4_Conn(): """ L4 'connection' tied to two VPP interfaces """ @@ -272,20 +267,7 @@ class L4_CONN_SIDE: L4_CONN_SIDE_ONE = 1 -class LoggerWrapper(object): - def __init__(self, logger=None): - self._logger = logger - - def debug(self, *args, **kwargs): - if self._logger: - self._logger.debug(*args, **kwargs) - - def error(self, *args, **kwargs): - if self._logger: - self._logger.error(*args, **kwargs) - - -def fragment_rfc791(packet, fragsize, _logger=None): +def fragment_rfc791(packet, fragsize, logger=null_logger): """ Fragment an IPv4 packet per RFC 791 :param packet: packet to fragment @@ -293,7 +275,6 @@ def fragment_rfc791(packet, fragsize, _logger=None): :note: IP options are not supported :returns: list of fragments """ - logger = LoggerWrapper(_logger) logger.debug(ppp("Fragmenting packet:", packet)) packet = packet.__class__(scapy.compat.raw(packet)) # recalc. all values if len(packet[IP].options) > 0: @@ -325,13 +306,13 @@ def fragment_rfc791(packet, fragsize, _logger=None): p[IP].frag = fo + nfb del p[IP].chksum - more_fragments = fragment_rfc791(p, fragsize, _logger) + more_fragments = fragment_rfc791(p, fragsize, logger) pkts.extend(more_fragments) return pkts -def fragment_rfc8200(packet, identification, fragsize, _logger=None): +def fragment_rfc8200(packet, identification, fragsize, logger=null_logger): """ Fragment an IPv6 packet per RFC 8200 :param packet: packet to fragment @@ -339,7 +320,6 @@ def fragment_rfc8200(packet, identification, fragsize, _logger=None): :note: IP options are not supported :returns: list of fragments """ - logger = LoggerWrapper(_logger) packet = packet.__class__(scapy.compat.raw(packet)) # recalc. all values if len(packet) <= fragsize: return [packet] @@ -476,3 +456,55 @@ def reassemble4_ether(listoffragments): def reassemble4(listoffragments): return reassemble4_core(listoffragments, True) + + +class UnexpectedPacketError(Exception): + def __init__(self, packet, msg=""): + self.packet = packet + self.msg = msg + + def __str__(self): + return f"\nUnexpected packet:\n{pr(self.packet)}{self.msg}" + + +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(UserDict): + """ + 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. + """ + + __slots__ = () # prevent setting properties to act like a dictionary + + def __init__(self, data): + super().__init__(data) + + def __or__(self, other): + return recursive_dict_merge(deepcopy(self.data), other)