1*c25ce589SFinn Behrens#!/usr/bin/env python 26697b51eSShalom Toledo# SPDX-License-Identifier: GPL-2.0 36697b51eSShalom Toledo 46697b51eSShalom Toledoimport subprocess 56697b51eSShalom Toledoimport json as j 66697b51eSShalom Toledoimport random 76697b51eSShalom Toledo 86697b51eSShalom Toledo 96697b51eSShalom Toledoclass SkipTest(Exception): 106697b51eSShalom Toledo pass 116697b51eSShalom Toledo 126697b51eSShalom Toledo 136697b51eSShalom Toledoclass RandomValuePicker: 146697b51eSShalom Toledo """ 156697b51eSShalom Toledo Class for storing shared buffer configuration. Can handle 3 different 166697b51eSShalom Toledo objects, pool, tcbind and portpool. Provide an interface to get random 176697b51eSShalom Toledo values for a specific object type as the follow: 186697b51eSShalom Toledo 1. Pool: 196697b51eSShalom Toledo - random size 206697b51eSShalom Toledo 216697b51eSShalom Toledo 2. TcBind: 226697b51eSShalom Toledo - random pool number 236697b51eSShalom Toledo - random threshold 246697b51eSShalom Toledo 256697b51eSShalom Toledo 3. PortPool: 266697b51eSShalom Toledo - random threshold 276697b51eSShalom Toledo """ 286697b51eSShalom Toledo def __init__(self, pools): 296697b51eSShalom Toledo self._pools = [] 306697b51eSShalom Toledo for pool in pools: 316697b51eSShalom Toledo self._pools.append(pool) 326697b51eSShalom Toledo 336697b51eSShalom Toledo def _cell_size(self): 346697b51eSShalom Toledo return self._pools[0]["cell_size"] 356697b51eSShalom Toledo 366697b51eSShalom Toledo def _get_static_size(self, th): 376697b51eSShalom Toledo # For threshold of 16, this works out to be about 12MB on Spectrum-1, 386697b51eSShalom Toledo # and about 17MB on Spectrum-2. 396697b51eSShalom Toledo return th * 8000 * self._cell_size() 406697b51eSShalom Toledo 416697b51eSShalom Toledo def _get_size(self): 426697b51eSShalom Toledo return self._get_static_size(16) 436697b51eSShalom Toledo 446697b51eSShalom Toledo def _get_thtype(self): 456697b51eSShalom Toledo return "static" 466697b51eSShalom Toledo 476697b51eSShalom Toledo def _get_th(self, pool): 486697b51eSShalom Toledo # Threshold value could be any integer between 3 to 16 496697b51eSShalom Toledo th = random.randint(3, 16) 506697b51eSShalom Toledo if pool["thtype"] == "dynamic": 516697b51eSShalom Toledo return th 526697b51eSShalom Toledo else: 536697b51eSShalom Toledo return self._get_static_size(th) 546697b51eSShalom Toledo 556697b51eSShalom Toledo def _get_pool(self, direction): 566697b51eSShalom Toledo ing_pools = [] 576697b51eSShalom Toledo egr_pools = [] 586697b51eSShalom Toledo for pool in self._pools: 596697b51eSShalom Toledo if pool["type"] == "ingress": 606697b51eSShalom Toledo ing_pools.append(pool) 616697b51eSShalom Toledo else: 626697b51eSShalom Toledo egr_pools.append(pool) 636697b51eSShalom Toledo if direction == "ingress": 646697b51eSShalom Toledo arr = ing_pools 656697b51eSShalom Toledo else: 666697b51eSShalom Toledo arr = egr_pools 676697b51eSShalom Toledo return arr[random.randint(0, len(arr) - 1)] 686697b51eSShalom Toledo 696697b51eSShalom Toledo def get_value(self, objid): 706697b51eSShalom Toledo if isinstance(objid, Pool): 716697b51eSShalom Toledo if objid["pool"] in [4, 8, 9, 10]: 726697b51eSShalom Toledo # The threshold type of pools 4, 8, 9 and 10 cannot be changed 736697b51eSShalom Toledo raise SkipTest() 746697b51eSShalom Toledo else: 756697b51eSShalom Toledo return (self._get_size(), self._get_thtype()) 766697b51eSShalom Toledo if isinstance(objid, TcBind): 776697b51eSShalom Toledo if objid["tc"] >= 8: 786697b51eSShalom Toledo # Multicast TCs cannot be changed 796697b51eSShalom Toledo raise SkipTest() 806697b51eSShalom Toledo else: 816697b51eSShalom Toledo pool = self._get_pool(objid["type"]) 826697b51eSShalom Toledo th = self._get_th(pool) 836697b51eSShalom Toledo pool_n = pool["pool"] 846697b51eSShalom Toledo return (pool_n, th) 856697b51eSShalom Toledo if isinstance(objid, PortPool): 866697b51eSShalom Toledo pool_n = objid["pool"] 876697b51eSShalom Toledo pool = self._pools[pool_n] 886697b51eSShalom Toledo assert pool["pool"] == pool_n 896697b51eSShalom Toledo th = self._get_th(pool) 906697b51eSShalom Toledo return (th,) 916697b51eSShalom Toledo 926697b51eSShalom Toledo 936697b51eSShalom Toledoclass RecordValuePickerException(Exception): 946697b51eSShalom Toledo pass 956697b51eSShalom Toledo 966697b51eSShalom Toledo 976697b51eSShalom Toledoclass RecordValuePicker: 986697b51eSShalom Toledo """ 996697b51eSShalom Toledo Class for storing shared buffer configuration. Can handle 2 different 1006697b51eSShalom Toledo objects, pool and tcbind. Provide an interface to get the stored values per 1016697b51eSShalom Toledo object type. 1026697b51eSShalom Toledo """ 1036697b51eSShalom Toledo def __init__(self, objlist): 1046697b51eSShalom Toledo self._recs = [] 1056697b51eSShalom Toledo for item in objlist: 1066697b51eSShalom Toledo self._recs.append({"objid": item, "value": item.var_tuple()}) 1076697b51eSShalom Toledo 1086697b51eSShalom Toledo def get_value(self, objid): 1096697b51eSShalom Toledo if isinstance(objid, Pool) and objid["pool"] in [4, 8, 9, 10]: 1106697b51eSShalom Toledo # The threshold type of pools 4, 8, 9 and 10 cannot be changed 1116697b51eSShalom Toledo raise SkipTest() 1126697b51eSShalom Toledo if isinstance(objid, TcBind) and objid["tc"] >= 8: 1136697b51eSShalom Toledo # Multicast TCs cannot be changed 1146697b51eSShalom Toledo raise SkipTest() 1156697b51eSShalom Toledo for rec in self._recs: 1166697b51eSShalom Toledo if rec["objid"].weak_eq(objid): 1176697b51eSShalom Toledo return rec["value"] 1186697b51eSShalom Toledo raise RecordValuePickerException() 1196697b51eSShalom Toledo 1206697b51eSShalom Toledo 1216697b51eSShalom Toledodef run_cmd(cmd, json=False): 1226697b51eSShalom Toledo out = subprocess.check_output(cmd, shell=True) 1236697b51eSShalom Toledo if json: 1246697b51eSShalom Toledo return j.loads(out) 1256697b51eSShalom Toledo return out 1266697b51eSShalom Toledo 1276697b51eSShalom Toledo 1286697b51eSShalom Toledodef run_json_cmd(cmd): 1296697b51eSShalom Toledo return run_cmd(cmd, json=True) 1306697b51eSShalom Toledo 1316697b51eSShalom Toledo 1326697b51eSShalom Toledodef log_test(test_name, err_msg=None): 1336697b51eSShalom Toledo if err_msg: 1346697b51eSShalom Toledo print("\t%s" % err_msg) 1356697b51eSShalom Toledo print("TEST: %-80s [FAIL]" % test_name) 1366697b51eSShalom Toledo else: 1376697b51eSShalom Toledo print("TEST: %-80s [ OK ]" % test_name) 1386697b51eSShalom Toledo 1396697b51eSShalom Toledo 1406697b51eSShalom Toledoclass CommonItem(dict): 1416697b51eSShalom Toledo varitems = [] 1426697b51eSShalom Toledo 1436697b51eSShalom Toledo def var_tuple(self): 1446697b51eSShalom Toledo ret = [] 1456697b51eSShalom Toledo self.varitems.sort() 1466697b51eSShalom Toledo for key in self.varitems: 1476697b51eSShalom Toledo ret.append(self[key]) 1486697b51eSShalom Toledo return tuple(ret) 1496697b51eSShalom Toledo 1506697b51eSShalom Toledo def weak_eq(self, other): 1516697b51eSShalom Toledo for key in self: 1526697b51eSShalom Toledo if key in self.varitems: 1536697b51eSShalom Toledo continue 1546697b51eSShalom Toledo if self[key] != other[key]: 1556697b51eSShalom Toledo return False 1566697b51eSShalom Toledo return True 1576697b51eSShalom Toledo 1586697b51eSShalom Toledo 1596697b51eSShalom Toledoclass CommonList(list): 1606697b51eSShalom Toledo def get_by(self, by_obj): 1616697b51eSShalom Toledo for item in self: 1626697b51eSShalom Toledo if item.weak_eq(by_obj): 1636697b51eSShalom Toledo return item 1646697b51eSShalom Toledo return None 1656697b51eSShalom Toledo 1666697b51eSShalom Toledo def del_by(self, by_obj): 1676697b51eSShalom Toledo for item in self: 1686697b51eSShalom Toledo if item.weak_eq(by_obj): 1696697b51eSShalom Toledo self.remove(item) 1706697b51eSShalom Toledo 1716697b51eSShalom Toledo 1726697b51eSShalom Toledoclass Pool(CommonItem): 1736697b51eSShalom Toledo varitems = ["size", "thtype"] 1746697b51eSShalom Toledo 1756697b51eSShalom Toledo def dl_set(self, dlname, size, thtype): 1766697b51eSShalom Toledo run_cmd("devlink sb pool set {} sb {} pool {} size {} thtype {}".format(dlname, self["sb"], 1776697b51eSShalom Toledo self["pool"], 1786697b51eSShalom Toledo size, thtype)) 1796697b51eSShalom Toledo 1806697b51eSShalom Toledo 1816697b51eSShalom Toledoclass PoolList(CommonList): 1826697b51eSShalom Toledo pass 1836697b51eSShalom Toledo 1846697b51eSShalom Toledo 1856697b51eSShalom Toledodef get_pools(dlname, direction=None): 1866697b51eSShalom Toledo d = run_json_cmd("devlink sb pool show -j") 1876697b51eSShalom Toledo pools = PoolList() 1886697b51eSShalom Toledo for pooldict in d["pool"][dlname]: 1896697b51eSShalom Toledo if not direction or direction == pooldict["type"]: 1906697b51eSShalom Toledo pools.append(Pool(pooldict)) 1916697b51eSShalom Toledo return pools 1926697b51eSShalom Toledo 1936697b51eSShalom Toledo 1946697b51eSShalom Toledodef do_check_pools(dlname, pools, vp): 1956697b51eSShalom Toledo for pool in pools: 1966697b51eSShalom Toledo pre_pools = get_pools(dlname) 1976697b51eSShalom Toledo try: 1986697b51eSShalom Toledo (size, thtype) = vp.get_value(pool) 1996697b51eSShalom Toledo except SkipTest: 2006697b51eSShalom Toledo continue 2016697b51eSShalom Toledo pool.dl_set(dlname, size, thtype) 2026697b51eSShalom Toledo post_pools = get_pools(dlname) 2036697b51eSShalom Toledo pool = post_pools.get_by(pool) 2046697b51eSShalom Toledo 2056697b51eSShalom Toledo err_msg = None 2066697b51eSShalom Toledo if pool["size"] != size: 2076697b51eSShalom Toledo err_msg = "Incorrect pool size (got {}, expected {})".format(pool["size"], size) 2086697b51eSShalom Toledo if pool["thtype"] != thtype: 2096697b51eSShalom Toledo err_msg = "Incorrect pool threshold type (got {}, expected {})".format(pool["thtype"], thtype) 2106697b51eSShalom Toledo 2116697b51eSShalom Toledo pre_pools.del_by(pool) 2126697b51eSShalom Toledo post_pools.del_by(pool) 2136697b51eSShalom Toledo if pre_pools != post_pools: 2146697b51eSShalom Toledo err_msg = "Other pool setup changed as well" 2156697b51eSShalom Toledo log_test("pool {} of sb {} set verification".format(pool["pool"], 2166697b51eSShalom Toledo pool["sb"]), err_msg) 2176697b51eSShalom Toledo 2186697b51eSShalom Toledo 2196697b51eSShalom Toledodef check_pools(dlname, pools): 2206697b51eSShalom Toledo # Save defaults 2216697b51eSShalom Toledo record_vp = RecordValuePicker(pools) 2226697b51eSShalom Toledo 2236697b51eSShalom Toledo # For each pool, set random size and static threshold type 2246697b51eSShalom Toledo do_check_pools(dlname, pools, RandomValuePicker(pools)) 2256697b51eSShalom Toledo 2266697b51eSShalom Toledo # Restore defaults 2276697b51eSShalom Toledo do_check_pools(dlname, pools, record_vp) 2286697b51eSShalom Toledo 2296697b51eSShalom Toledo 2306697b51eSShalom Toledoclass TcBind(CommonItem): 2316697b51eSShalom Toledo varitems = ["pool", "threshold"] 2326697b51eSShalom Toledo 2336697b51eSShalom Toledo def __init__(self, port, d): 2346697b51eSShalom Toledo super(TcBind, self).__init__(d) 2356697b51eSShalom Toledo self["dlportname"] = port.name 2366697b51eSShalom Toledo 2376697b51eSShalom Toledo def dl_set(self, pool, th): 2386697b51eSShalom Toledo run_cmd("devlink sb tc bind set {} sb {} tc {} type {} pool {} th {}".format(self["dlportname"], 2396697b51eSShalom Toledo self["sb"], 2406697b51eSShalom Toledo self["tc"], 2416697b51eSShalom Toledo self["type"], 2426697b51eSShalom Toledo pool, th)) 2436697b51eSShalom Toledo 2446697b51eSShalom Toledo 2456697b51eSShalom Toledoclass TcBindList(CommonList): 2466697b51eSShalom Toledo pass 2476697b51eSShalom Toledo 2486697b51eSShalom Toledo 2496697b51eSShalom Toledodef get_tcbinds(ports, verify_existence=False): 2506697b51eSShalom Toledo d = run_json_cmd("devlink sb tc bind show -j -n") 2516697b51eSShalom Toledo tcbinds = TcBindList() 2526697b51eSShalom Toledo for port in ports: 2536697b51eSShalom Toledo err_msg = None 2546697b51eSShalom Toledo if port.name not in d["tc_bind"] or len(d["tc_bind"][port.name]) == 0: 2556697b51eSShalom Toledo err_msg = "No tc bind for port" 2566697b51eSShalom Toledo else: 2576697b51eSShalom Toledo for tcbinddict in d["tc_bind"][port.name]: 2586697b51eSShalom Toledo tcbinds.append(TcBind(port, tcbinddict)) 2596697b51eSShalom Toledo if verify_existence: 2606697b51eSShalom Toledo log_test("tc bind existence for port {} verification".format(port.name), err_msg) 2616697b51eSShalom Toledo return tcbinds 2626697b51eSShalom Toledo 2636697b51eSShalom Toledo 2646697b51eSShalom Toledodef do_check_tcbind(ports, tcbinds, vp): 2656697b51eSShalom Toledo for tcbind in tcbinds: 2666697b51eSShalom Toledo pre_tcbinds = get_tcbinds(ports) 2676697b51eSShalom Toledo try: 2686697b51eSShalom Toledo (pool, th) = vp.get_value(tcbind) 2696697b51eSShalom Toledo except SkipTest: 2706697b51eSShalom Toledo continue 2716697b51eSShalom Toledo tcbind.dl_set(pool, th) 2726697b51eSShalom Toledo post_tcbinds = get_tcbinds(ports) 2736697b51eSShalom Toledo tcbind = post_tcbinds.get_by(tcbind) 2746697b51eSShalom Toledo 2756697b51eSShalom Toledo err_msg = None 2766697b51eSShalom Toledo if tcbind["pool"] != pool: 2776697b51eSShalom Toledo err_msg = "Incorrect pool (got {}, expected {})".format(tcbind["pool"], pool) 2786697b51eSShalom Toledo if tcbind["threshold"] != th: 2796697b51eSShalom Toledo err_msg = "Incorrect threshold (got {}, expected {})".format(tcbind["threshold"], th) 2806697b51eSShalom Toledo 2816697b51eSShalom Toledo pre_tcbinds.del_by(tcbind) 2826697b51eSShalom Toledo post_tcbinds.del_by(tcbind) 2836697b51eSShalom Toledo if pre_tcbinds != post_tcbinds: 2846697b51eSShalom Toledo err_msg = "Other tc bind setup changed as well" 2856697b51eSShalom Toledo log_test("tc bind {}-{} of sb {} set verification".format(tcbind["dlportname"], 2866697b51eSShalom Toledo tcbind["tc"], 2876697b51eSShalom Toledo tcbind["sb"]), err_msg) 2886697b51eSShalom Toledo 2896697b51eSShalom Toledo 2906697b51eSShalom Toledodef check_tcbind(dlname, ports, pools): 2916697b51eSShalom Toledo tcbinds = get_tcbinds(ports, verify_existence=True) 2926697b51eSShalom Toledo 2936697b51eSShalom Toledo # Save defaults 2946697b51eSShalom Toledo record_vp = RecordValuePicker(tcbinds) 2956697b51eSShalom Toledo 2966697b51eSShalom Toledo # Bind each port and unicast TC (TCs < 8) to a random pool and a random 2976697b51eSShalom Toledo # threshold 2986697b51eSShalom Toledo do_check_tcbind(ports, tcbinds, RandomValuePicker(pools)) 2996697b51eSShalom Toledo 3006697b51eSShalom Toledo # Restore defaults 3016697b51eSShalom Toledo do_check_tcbind(ports, tcbinds, record_vp) 3026697b51eSShalom Toledo 3036697b51eSShalom Toledo 3046697b51eSShalom Toledoclass PortPool(CommonItem): 3056697b51eSShalom Toledo varitems = ["threshold"] 3066697b51eSShalom Toledo 3076697b51eSShalom Toledo def __init__(self, port, d): 3086697b51eSShalom Toledo super(PortPool, self).__init__(d) 3096697b51eSShalom Toledo self["dlportname"] = port.name 3106697b51eSShalom Toledo 3116697b51eSShalom Toledo def dl_set(self, th): 3126697b51eSShalom Toledo run_cmd("devlink sb port pool set {} sb {} pool {} th {}".format(self["dlportname"], 3136697b51eSShalom Toledo self["sb"], 3146697b51eSShalom Toledo self["pool"], th)) 3156697b51eSShalom Toledo 3166697b51eSShalom Toledo 3176697b51eSShalom Toledoclass PortPoolList(CommonList): 3186697b51eSShalom Toledo pass 3196697b51eSShalom Toledo 3206697b51eSShalom Toledo 3216697b51eSShalom Toledodef get_portpools(ports, verify_existence=False): 3226697b51eSShalom Toledo d = run_json_cmd("devlink sb port pool -j -n") 3236697b51eSShalom Toledo portpools = PortPoolList() 3246697b51eSShalom Toledo for port in ports: 3256697b51eSShalom Toledo err_msg = None 3266697b51eSShalom Toledo if port.name not in d["port_pool"] or len(d["port_pool"][port.name]) == 0: 3276697b51eSShalom Toledo err_msg = "No port pool for port" 3286697b51eSShalom Toledo else: 3296697b51eSShalom Toledo for portpooldict in d["port_pool"][port.name]: 3306697b51eSShalom Toledo portpools.append(PortPool(port, portpooldict)) 3316697b51eSShalom Toledo if verify_existence: 3326697b51eSShalom Toledo log_test("port pool existence for port {} verification".format(port.name), err_msg) 3336697b51eSShalom Toledo return portpools 3346697b51eSShalom Toledo 3356697b51eSShalom Toledo 3366697b51eSShalom Toledodef do_check_portpool(ports, portpools, vp): 3376697b51eSShalom Toledo for portpool in portpools: 3386697b51eSShalom Toledo pre_portpools = get_portpools(ports) 3396697b51eSShalom Toledo (th,) = vp.get_value(portpool) 3406697b51eSShalom Toledo portpool.dl_set(th) 3416697b51eSShalom Toledo post_portpools = get_portpools(ports) 3426697b51eSShalom Toledo portpool = post_portpools.get_by(portpool) 3436697b51eSShalom Toledo 3446697b51eSShalom Toledo err_msg = None 3456697b51eSShalom Toledo if portpool["threshold"] != th: 3466697b51eSShalom Toledo err_msg = "Incorrect threshold (got {}, expected {})".format(portpool["threshold"], th) 3476697b51eSShalom Toledo 3486697b51eSShalom Toledo pre_portpools.del_by(portpool) 3496697b51eSShalom Toledo post_portpools.del_by(portpool) 3506697b51eSShalom Toledo if pre_portpools != post_portpools: 3516697b51eSShalom Toledo err_msg = "Other port pool setup changed as well" 3526697b51eSShalom Toledo log_test("port pool {}-{} of sb {} set verification".format(portpool["dlportname"], 3536697b51eSShalom Toledo portpool["pool"], 3546697b51eSShalom Toledo portpool["sb"]), err_msg) 3556697b51eSShalom Toledo 3566697b51eSShalom Toledo 3576697b51eSShalom Toledodef check_portpool(dlname, ports, pools): 3586697b51eSShalom Toledo portpools = get_portpools(ports, verify_existence=True) 3596697b51eSShalom Toledo 3606697b51eSShalom Toledo # Save defaults 3616697b51eSShalom Toledo record_vp = RecordValuePicker(portpools) 3626697b51eSShalom Toledo 3636697b51eSShalom Toledo # For each port pool, set a random threshold 3646697b51eSShalom Toledo do_check_portpool(ports, portpools, RandomValuePicker(pools)) 3656697b51eSShalom Toledo 3666697b51eSShalom Toledo # Restore defaults 3676697b51eSShalom Toledo do_check_portpool(ports, portpools, record_vp) 3686697b51eSShalom Toledo 3696697b51eSShalom Toledo 3706697b51eSShalom Toledoclass Port: 3716697b51eSShalom Toledo def __init__(self, name): 3726697b51eSShalom Toledo self.name = name 3736697b51eSShalom Toledo 3746697b51eSShalom Toledo 3756697b51eSShalom Toledoclass PortList(list): 3766697b51eSShalom Toledo pass 3776697b51eSShalom Toledo 3786697b51eSShalom Toledo 3796697b51eSShalom Toledodef get_ports(dlname): 3806697b51eSShalom Toledo d = run_json_cmd("devlink port show -j") 3816697b51eSShalom Toledo ports = PortList() 3826697b51eSShalom Toledo for name in d["port"]: 3836697b51eSShalom Toledo if name.find(dlname) == 0 and d["port"][name]["flavour"] == "physical": 3846697b51eSShalom Toledo ports.append(Port(name)) 3856697b51eSShalom Toledo return ports 3866697b51eSShalom Toledo 3876697b51eSShalom Toledo 3886697b51eSShalom Toledodef get_device(): 3896697b51eSShalom Toledo devices_info = run_json_cmd("devlink -j dev info")["info"] 3906697b51eSShalom Toledo for d in devices_info: 3916697b51eSShalom Toledo if "mlxsw_spectrum" in devices_info[d]["driver"]: 3926697b51eSShalom Toledo return d 3936697b51eSShalom Toledo return None 3946697b51eSShalom Toledo 3956697b51eSShalom Toledo 3966697b51eSShalom Toledoclass UnavailableDevlinkNameException(Exception): 3976697b51eSShalom Toledo pass 3986697b51eSShalom Toledo 3996697b51eSShalom Toledo 4006697b51eSShalom Toledodef test_sb_configuration(): 4016697b51eSShalom Toledo # Use static seed 4026697b51eSShalom Toledo random.seed(0) 4036697b51eSShalom Toledo 4046697b51eSShalom Toledo dlname = get_device() 4056697b51eSShalom Toledo if not dlname: 4066697b51eSShalom Toledo raise UnavailableDevlinkNameException() 4076697b51eSShalom Toledo 4086697b51eSShalom Toledo ports = get_ports(dlname) 4096697b51eSShalom Toledo pools = get_pools(dlname) 4106697b51eSShalom Toledo 4116697b51eSShalom Toledo check_pools(dlname, pools) 4126697b51eSShalom Toledo check_tcbind(dlname, ports, pools) 4136697b51eSShalom Toledo check_portpool(dlname, ports, pools) 4146697b51eSShalom Toledo 4156697b51eSShalom Toledo 4166697b51eSShalom Toledotest_sb_configuration() 417