| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416 |
- #!/usr/bin/env python
- # SPDX-License-Identifier: GPL-2.0
- import subprocess
- import json as j
- import random
- class SkipTest(Exception):
- pass
- class RandomValuePicker:
- """
- Class for storing shared buffer configuration. Can handle 3 different
- objects, pool, tcbind and portpool. Provide an interface to get random
- values for a specific object type as the follow:
- 1. Pool:
- - random size
- 2. TcBind:
- - random pool number
- - random threshold
- 3. PortPool:
- - random threshold
- """
- def __init__(self, pools):
- self._pools = []
- for pool in pools:
- self._pools.append(pool)
- def _cell_size(self):
- return self._pools[0]["cell_size"]
- def _get_static_size(self, th):
- # For threshold of 16, this works out to be about 12MB on Spectrum-1,
- # and about 17MB on Spectrum-2.
- return th * 8000 * self._cell_size()
- def _get_size(self):
- return self._get_static_size(16)
- def _get_thtype(self):
- return "static"
- def _get_th(self, pool):
- # Threshold value could be any integer between 3 to 16
- th = random.randint(3, 16)
- if pool["thtype"] == "dynamic":
- return th
- else:
- return self._get_static_size(th)
- def _get_pool(self, direction):
- ing_pools = []
- egr_pools = []
- for pool in self._pools:
- if pool["type"] == "ingress":
- ing_pools.append(pool)
- else:
- egr_pools.append(pool)
- if direction == "ingress":
- arr = ing_pools
- else:
- arr = egr_pools
- return arr[random.randint(0, len(arr) - 1)]
- def get_value(self, objid):
- if isinstance(objid, Pool):
- if objid["pool"] in [4, 8, 9, 10]:
- # The threshold type of pools 4, 8, 9 and 10 cannot be changed
- raise SkipTest()
- else:
- return (self._get_size(), self._get_thtype())
- if isinstance(objid, TcBind):
- if objid["tc"] >= 8:
- # Multicast TCs cannot be changed
- raise SkipTest()
- else:
- pool = self._get_pool(objid["type"])
- th = self._get_th(pool)
- pool_n = pool["pool"]
- return (pool_n, th)
- if isinstance(objid, PortPool):
- pool_n = objid["pool"]
- pool = self._pools[pool_n]
- assert pool["pool"] == pool_n
- th = self._get_th(pool)
- return (th,)
- class RecordValuePickerException(Exception):
- pass
- class RecordValuePicker:
- """
- Class for storing shared buffer configuration. Can handle 2 different
- objects, pool and tcbind. Provide an interface to get the stored values per
- object type.
- """
- def __init__(self, objlist):
- self._recs = []
- for item in objlist:
- self._recs.append({"objid": item, "value": item.var_tuple()})
- def get_value(self, objid):
- if isinstance(objid, Pool) and objid["pool"] in [4, 8, 9, 10]:
- # The threshold type of pools 4, 8, 9 and 10 cannot be changed
- raise SkipTest()
- if isinstance(objid, TcBind) and objid["tc"] >= 8:
- # Multicast TCs cannot be changed
- raise SkipTest()
- for rec in self._recs:
- if rec["objid"].weak_eq(objid):
- return rec["value"]
- raise RecordValuePickerException()
- def run_cmd(cmd, json=False):
- out = subprocess.check_output(cmd, shell=True)
- if json:
- return j.loads(out)
- return out
- def run_json_cmd(cmd):
- return run_cmd(cmd, json=True)
- def log_test(test_name, err_msg=None):
- if err_msg:
- print("\t%s" % err_msg)
- print("TEST: %-80s [FAIL]" % test_name)
- else:
- print("TEST: %-80s [ OK ]" % test_name)
- class CommonItem(dict):
- varitems = []
- def var_tuple(self):
- ret = []
- self.varitems.sort()
- for key in self.varitems:
- ret.append(self[key])
- return tuple(ret)
- def weak_eq(self, other):
- for key in self:
- if key in self.varitems:
- continue
- if self[key] != other[key]:
- return False
- return True
- class CommonList(list):
- def get_by(self, by_obj):
- for item in self:
- if item.weak_eq(by_obj):
- return item
- return None
- def del_by(self, by_obj):
- for item in self:
- if item.weak_eq(by_obj):
- self.remove(item)
- class Pool(CommonItem):
- varitems = ["size", "thtype"]
- def dl_set(self, dlname, size, thtype):
- run_cmd("devlink sb pool set {} sb {} pool {} size {} thtype {}".format(dlname, self["sb"],
- self["pool"],
- size, thtype))
- class PoolList(CommonList):
- pass
- def get_pools(dlname, direction=None):
- d = run_json_cmd("devlink sb pool show -j")
- pools = PoolList()
- for pooldict in d["pool"][dlname]:
- if not direction or direction == pooldict["type"]:
- pools.append(Pool(pooldict))
- return pools
- def do_check_pools(dlname, pools, vp):
- for pool in pools:
- pre_pools = get_pools(dlname)
- try:
- (size, thtype) = vp.get_value(pool)
- except SkipTest:
- continue
- pool.dl_set(dlname, size, thtype)
- post_pools = get_pools(dlname)
- pool = post_pools.get_by(pool)
- err_msg = None
- if pool["size"] != size:
- err_msg = "Incorrect pool size (got {}, expected {})".format(pool["size"], size)
- if pool["thtype"] != thtype:
- err_msg = "Incorrect pool threshold type (got {}, expected {})".format(pool["thtype"], thtype)
- pre_pools.del_by(pool)
- post_pools.del_by(pool)
- if pre_pools != post_pools:
- err_msg = "Other pool setup changed as well"
- log_test("pool {} of sb {} set verification".format(pool["pool"],
- pool["sb"]), err_msg)
- def check_pools(dlname, pools):
- # Save defaults
- record_vp = RecordValuePicker(pools)
- # For each pool, set random size and static threshold type
- do_check_pools(dlname, pools, RandomValuePicker(pools))
- # Restore defaults
- do_check_pools(dlname, pools, record_vp)
- class TcBind(CommonItem):
- varitems = ["pool", "threshold"]
- def __init__(self, port, d):
- super(TcBind, self).__init__(d)
- self["dlportname"] = port.name
- def dl_set(self, pool, th):
- run_cmd("devlink sb tc bind set {} sb {} tc {} type {} pool {} th {}".format(self["dlportname"],
- self["sb"],
- self["tc"],
- self["type"],
- pool, th))
- class TcBindList(CommonList):
- pass
- def get_tcbinds(ports, verify_existence=False):
- d = run_json_cmd("devlink sb tc bind show -j -n")
- tcbinds = TcBindList()
- for port in ports:
- err_msg = None
- if port.name not in d["tc_bind"] or len(d["tc_bind"][port.name]) == 0:
- err_msg = "No tc bind for port"
- else:
- for tcbinddict in d["tc_bind"][port.name]:
- tcbinds.append(TcBind(port, tcbinddict))
- if verify_existence:
- log_test("tc bind existence for port {} verification".format(port.name), err_msg)
- return tcbinds
- def do_check_tcbind(ports, tcbinds, vp):
- for tcbind in tcbinds:
- pre_tcbinds = get_tcbinds(ports)
- try:
- (pool, th) = vp.get_value(tcbind)
- except SkipTest:
- continue
- tcbind.dl_set(pool, th)
- post_tcbinds = get_tcbinds(ports)
- tcbind = post_tcbinds.get_by(tcbind)
- err_msg = None
- if tcbind["pool"] != pool:
- err_msg = "Incorrect pool (got {}, expected {})".format(tcbind["pool"], pool)
- if tcbind["threshold"] != th:
- err_msg = "Incorrect threshold (got {}, expected {})".format(tcbind["threshold"], th)
- pre_tcbinds.del_by(tcbind)
- post_tcbinds.del_by(tcbind)
- if pre_tcbinds != post_tcbinds:
- err_msg = "Other tc bind setup changed as well"
- log_test("tc bind {}-{} of sb {} set verification".format(tcbind["dlportname"],
- tcbind["tc"],
- tcbind["sb"]), err_msg)
- def check_tcbind(dlname, ports, pools):
- tcbinds = get_tcbinds(ports, verify_existence=True)
- # Save defaults
- record_vp = RecordValuePicker(tcbinds)
- # Bind each port and unicast TC (TCs < 8) to a random pool and a random
- # threshold
- do_check_tcbind(ports, tcbinds, RandomValuePicker(pools))
- # Restore defaults
- do_check_tcbind(ports, tcbinds, record_vp)
- class PortPool(CommonItem):
- varitems = ["threshold"]
- def __init__(self, port, d):
- super(PortPool, self).__init__(d)
- self["dlportname"] = port.name
- def dl_set(self, th):
- run_cmd("devlink sb port pool set {} sb {} pool {} th {}".format(self["dlportname"],
- self["sb"],
- self["pool"], th))
- class PortPoolList(CommonList):
- pass
- def get_portpools(ports, verify_existence=False):
- d = run_json_cmd("devlink sb port pool -j -n")
- portpools = PortPoolList()
- for port in ports:
- err_msg = None
- if port.name not in d["port_pool"] or len(d["port_pool"][port.name]) == 0:
- err_msg = "No port pool for port"
- else:
- for portpooldict in d["port_pool"][port.name]:
- portpools.append(PortPool(port, portpooldict))
- if verify_existence:
- log_test("port pool existence for port {} verification".format(port.name), err_msg)
- return portpools
- def do_check_portpool(ports, portpools, vp):
- for portpool in portpools:
- pre_portpools = get_portpools(ports)
- (th,) = vp.get_value(portpool)
- portpool.dl_set(th)
- post_portpools = get_portpools(ports)
- portpool = post_portpools.get_by(portpool)
- err_msg = None
- if portpool["threshold"] != th:
- err_msg = "Incorrect threshold (got {}, expected {})".format(portpool["threshold"], th)
- pre_portpools.del_by(portpool)
- post_portpools.del_by(portpool)
- if pre_portpools != post_portpools:
- err_msg = "Other port pool setup changed as well"
- log_test("port pool {}-{} of sb {} set verification".format(portpool["dlportname"],
- portpool["pool"],
- portpool["sb"]), err_msg)
- def check_portpool(dlname, ports, pools):
- portpools = get_portpools(ports, verify_existence=True)
- # Save defaults
- record_vp = RecordValuePicker(portpools)
- # For each port pool, set a random threshold
- do_check_portpool(ports, portpools, RandomValuePicker(pools))
- # Restore defaults
- do_check_portpool(ports, portpools, record_vp)
- class Port:
- def __init__(self, name):
- self.name = name
- class PortList(list):
- pass
- def get_ports(dlname):
- d = run_json_cmd("devlink port show -j")
- ports = PortList()
- for name in d["port"]:
- if name.find(dlname) == 0 and d["port"][name]["flavour"] == "physical":
- ports.append(Port(name))
- return ports
- def get_device():
- devices_info = run_json_cmd("devlink -j dev info")["info"]
- for d in devices_info:
- if "mlxsw_spectrum" in devices_info[d]["driver"]:
- return d
- return None
- class UnavailableDevlinkNameException(Exception):
- pass
- def test_sb_configuration():
- # Use static seed
- random.seed(0)
- dlname = get_device()
- if not dlname:
- raise UnavailableDevlinkNameException()
- ports = get_ports(dlname)
- pools = get_pools(dlname)
- check_pools(dlname, pools)
- check_tcbind(dlname, ports, pools)
- check_portpool(dlname, ports, pools)
- test_sb_configuration()
|