brintos

brintos / linux-shallow public Read only

0
0
Text · 12.4 KiB · 2223337 Raw
417 lines · python
1#!/usr/bin/env python2# SPDX-License-Identifier: GPL-2.03 4import subprocess5import json as j6import random7 8 9class SkipTest(Exception):10    pass11 12 13class RandomValuePicker:14    """15    Class for storing shared buffer configuration. Can handle 3 different16    objects, pool, tcbind and portpool. Provide an interface to get random17    values for a specific object type as the follow:18      1. Pool:19         - random size20 21      2. TcBind:22         - random pool number23         - random threshold24 25      3. PortPool:26         - random threshold27    """28    def __init__(self, pools):29        self._pools = []30        for pool in pools:31            self._pools.append(pool)32 33    def _cell_size(self):34        return self._pools[0]["cell_size"]35 36    def _get_static_size(self, th):37        # For threshold of 16, this works out to be about 12MB on Spectrum-1,38        # and about 17MB on Spectrum-2.39        return th * 8000 * self._cell_size()40 41    def _get_size(self):42        return self._get_static_size(16)43 44    def _get_thtype(self):45        return "static"46 47    def _get_th(self, pool):48        # Threshold value could be any integer between 3 to 1649        th = random.randint(3, 16)50        if pool["thtype"] == "dynamic":51            return th52        else:53            return self._get_static_size(th)54 55    def _get_pool(self, direction):56        ing_pools = []57        egr_pools = []58        for pool in self._pools:59            if pool["type"] == "ingress":60                ing_pools.append(pool)61            else:62                egr_pools.append(pool)63        if direction == "ingress":64            arr = ing_pools65        else:66            arr = egr_pools67        return arr[random.randint(0, len(arr) - 1)]68 69    def get_value(self, objid):70        if isinstance(objid, Pool):71            if objid["pool"] in [4, 8, 9, 10]:72                # The threshold type of pools 4, 8, 9 and 10 cannot be changed73                raise SkipTest()74            else:75                return (self._get_size(), self._get_thtype())76        if isinstance(objid, TcBind):77            if objid["tc"] >= 8:78                # Multicast TCs cannot be changed79                raise SkipTest()80            else:81                pool = self._get_pool(objid["type"])82                th = self._get_th(pool)83                pool_n = pool["pool"]84                return (pool_n, th)85        if isinstance(objid, PortPool):86            pool_n = objid["pool"]87            pool = self._pools[pool_n]88            assert pool["pool"] == pool_n89            th = self._get_th(pool)90            return (th,)91 92 93class RecordValuePickerException(Exception):94    pass95 96 97class RecordValuePicker:98    """99    Class for storing shared buffer configuration. Can handle 2 different100    objects, pool and tcbind. Provide an interface to get the stored values per101    object type.102    """103    def __init__(self, objlist):104        self._recs = []105        for item in objlist:106            self._recs.append({"objid": item, "value": item.var_tuple()})107 108    def get_value(self, objid):109        if isinstance(objid, Pool) and objid["pool"] in [4, 8, 9, 10]:110            # The threshold type of pools 4, 8, 9 and 10 cannot be changed111            raise SkipTest()112        if isinstance(objid, TcBind) and objid["tc"] >= 8:113            # Multicast TCs cannot be changed114            raise SkipTest()115        for rec in self._recs:116            if rec["objid"].weak_eq(objid):117                return rec["value"]118        raise RecordValuePickerException()119 120 121def run_cmd(cmd, json=False):122    out = subprocess.check_output(cmd, shell=True)123    if json:124        return j.loads(out)125    return out126 127 128def run_json_cmd(cmd):129    return run_cmd(cmd, json=True)130 131 132def log_test(test_name, err_msg=None):133    if err_msg:134        print("\t%s" % err_msg)135        print("TEST: %-80s  [FAIL]" % test_name)136    else:137        print("TEST: %-80s  [ OK ]" % test_name)138 139 140class CommonItem(dict):141    varitems = []142 143    def var_tuple(self):144        ret = []145        self.varitems.sort()146        for key in self.varitems:147            ret.append(self[key])148        return tuple(ret)149 150    def weak_eq(self, other):151        for key in self:152            if key in self.varitems:153                continue154            if self[key] != other[key]:155                return False156        return True157 158 159class CommonList(list):160    def get_by(self, by_obj):161        for item in self:162            if item.weak_eq(by_obj):163                return item164        return None165 166    def del_by(self, by_obj):167        for item in self:168            if item.weak_eq(by_obj):169                self.remove(item)170 171 172class Pool(CommonItem):173    varitems = ["size", "thtype"]174 175    def dl_set(self, dlname, size, thtype):176        run_cmd("devlink sb pool set {} sb {} pool {} size {} thtype {}".format(dlname, self["sb"],177                                                                                self["pool"],178                                                                                size, thtype))179 180 181class PoolList(CommonList):182    pass183 184 185def get_pools(dlname, direction=None):186    d = run_json_cmd("devlink sb pool show -j")187    pools = PoolList()188    for pooldict in d["pool"][dlname]:189        if not direction or direction == pooldict["type"]:190            pools.append(Pool(pooldict))191    return pools192 193 194def do_check_pools(dlname, pools, vp):195    for pool in pools:196        pre_pools = get_pools(dlname)197        try:198            (size, thtype) = vp.get_value(pool)199        except SkipTest:200            continue201        pool.dl_set(dlname, size, thtype)202        post_pools = get_pools(dlname)203        pool = post_pools.get_by(pool)204 205        err_msg = None206        if pool["size"] != size:207            err_msg = "Incorrect pool size (got {}, expected {})".format(pool["size"], size)208        if pool["thtype"] != thtype:209            err_msg = "Incorrect pool threshold type (got {}, expected {})".format(pool["thtype"], thtype)210 211        pre_pools.del_by(pool)212        post_pools.del_by(pool)213        if pre_pools != post_pools:214            err_msg = "Other pool setup changed as well"215        log_test("pool {} of sb {} set verification".format(pool["pool"],216                                                            pool["sb"]), err_msg)217 218 219def check_pools(dlname, pools):220    # Save defaults221    record_vp = RecordValuePicker(pools)222 223    # For each pool, set random size and static threshold type224    do_check_pools(dlname, pools, RandomValuePicker(pools))225 226    # Restore defaults227    do_check_pools(dlname, pools, record_vp)228 229 230class TcBind(CommonItem):231    varitems = ["pool", "threshold"]232 233    def __init__(self, port, d):234        super(TcBind, self).__init__(d)235        self["dlportname"] = port.name236 237    def dl_set(self, pool, th):238        run_cmd("devlink sb tc bind set {} sb {} tc {} type {} pool {} th {}".format(self["dlportname"],239                                                                                     self["sb"],240                                                                                     self["tc"],241                                                                                     self["type"],242                                                                                     pool, th))243 244 245class TcBindList(CommonList):246    pass247 248 249def get_tcbinds(ports, verify_existence=False):250    d = run_json_cmd("devlink sb tc bind show -j -n")251    tcbinds = TcBindList()252    for port in ports:253        err_msg = None254        if port.name not in d["tc_bind"] or len(d["tc_bind"][port.name]) == 0:255            err_msg = "No tc bind for port"256        else:257            for tcbinddict in d["tc_bind"][port.name]:258                tcbinds.append(TcBind(port, tcbinddict))259        if verify_existence:260            log_test("tc bind existence for port {} verification".format(port.name), err_msg)261    return tcbinds262 263 264def do_check_tcbind(ports, tcbinds, vp):265    for tcbind in tcbinds:266        pre_tcbinds = get_tcbinds(ports)267        try:268            (pool, th) = vp.get_value(tcbind)269        except SkipTest:270            continue271        tcbind.dl_set(pool, th)272        post_tcbinds = get_tcbinds(ports)273        tcbind = post_tcbinds.get_by(tcbind)274 275        err_msg = None276        if tcbind["pool"] != pool:277            err_msg = "Incorrect pool (got {}, expected {})".format(tcbind["pool"], pool)278        if tcbind["threshold"] != th:279            err_msg = "Incorrect threshold (got {}, expected {})".format(tcbind["threshold"], th)280 281        pre_tcbinds.del_by(tcbind)282        post_tcbinds.del_by(tcbind)283        if pre_tcbinds != post_tcbinds:284            err_msg = "Other tc bind setup changed as well"285        log_test("tc bind {}-{} of sb {} set verification".format(tcbind["dlportname"],286                                                                  tcbind["tc"],287                                                                  tcbind["sb"]), err_msg)288 289 290def check_tcbind(dlname, ports, pools):291    tcbinds = get_tcbinds(ports, verify_existence=True)292 293    # Save defaults294    record_vp = RecordValuePicker(tcbinds)295 296    # Bind each port and unicast TC (TCs < 8) to a random pool and a random297    # threshold298    do_check_tcbind(ports, tcbinds, RandomValuePicker(pools))299 300    # Restore defaults301    do_check_tcbind(ports, tcbinds, record_vp)302 303 304class PortPool(CommonItem):305    varitems = ["threshold"]306 307    def __init__(self, port, d):308        super(PortPool, self).__init__(d)309        self["dlportname"] = port.name310 311    def dl_set(self, th):312        run_cmd("devlink sb port pool set {} sb {} pool {} th {}".format(self["dlportname"],313                                                                         self["sb"],314                                                                         self["pool"], th))315 316 317class PortPoolList(CommonList):318    pass319 320 321def get_portpools(ports, verify_existence=False):322    d = run_json_cmd("devlink sb port pool -j -n")323    portpools = PortPoolList()324    for port in ports:325        err_msg = None326        if port.name not in d["port_pool"] or len(d["port_pool"][port.name]) == 0:327            err_msg = "No port pool for port"328        else:329            for portpooldict in d["port_pool"][port.name]:330                portpools.append(PortPool(port, portpooldict))331        if verify_existence:332            log_test("port pool existence for port {} verification".format(port.name), err_msg)333    return portpools334 335 336def do_check_portpool(ports, portpools, vp):337    for portpool in portpools:338        pre_portpools = get_portpools(ports)339        (th,) = vp.get_value(portpool)340        portpool.dl_set(th)341        post_portpools = get_portpools(ports)342        portpool = post_portpools.get_by(portpool)343 344        err_msg = None345        if portpool["threshold"] != th:346            err_msg = "Incorrect threshold (got {}, expected {})".format(portpool["threshold"], th)347 348        pre_portpools.del_by(portpool)349        post_portpools.del_by(portpool)350        if pre_portpools != post_portpools:351            err_msg = "Other port pool setup changed as well"352        log_test("port pool {}-{} of sb {} set verification".format(portpool["dlportname"],353                                                                    portpool["pool"],354                                                                    portpool["sb"]), err_msg)355 356 357def check_portpool(dlname, ports, pools):358    portpools = get_portpools(ports, verify_existence=True)359 360    # Save defaults361    record_vp = RecordValuePicker(portpools)362 363    # For each port pool, set a random threshold364    do_check_portpool(ports, portpools, RandomValuePicker(pools))365 366    # Restore defaults367    do_check_portpool(ports, portpools, record_vp)368 369 370class Port:371    def __init__(self, name):372        self.name = name373 374 375class PortList(list):376    pass377 378 379def get_ports(dlname):380    d = run_json_cmd("devlink port show -j")381    ports = PortList()382    for name in d["port"]:383        if name.find(dlname) == 0 and d["port"][name]["flavour"] == "physical":384            ports.append(Port(name))385    return ports386 387 388def get_device():389    devices_info = run_json_cmd("devlink -j dev info")["info"]390    for d in devices_info:391        if "mlxsw_spectrum" in devices_info[d]["driver"]:392            return d393    return None394 395 396class UnavailableDevlinkNameException(Exception):397    pass398 399 400def test_sb_configuration():401    # Use static seed402    random.seed(0)403 404    dlname = get_device()405    if not dlname:406        raise UnavailableDevlinkNameException()407 408    ports = get_ports(dlname)409    pools = get_pools(dlname)410 411    check_pools(dlname, pools)412    check_tcbind(dlname, ports, pools)413    check_portpool(dlname, ports, pools)414 415 416test_sb_configuration()417