0001
0002
0003
0004 import subprocess
0005 import json as j
0006 import random
0007
0008
0009 class SkipTest(Exception):
0010 pass
0011
0012
0013 class RandomValuePicker:
0014 """
0015 Class for storing shared buffer configuration. Can handle 3 different
0016 objects, pool, tcbind and portpool. Provide an interface to get random
0017 values for a specific object type as the follow:
0018 1. Pool:
0019 - random size
0020
0021 2. TcBind:
0022 - random pool number
0023 - random threshold
0024
0025 3. PortPool:
0026 - random threshold
0027 """
0028 def __init__(self, pools):
0029 self._pools = []
0030 for pool in pools:
0031 self._pools.append(pool)
0032
0033 def _cell_size(self):
0034 return self._pools[0]["cell_size"]
0035
0036 def _get_static_size(self, th):
0037
0038
0039 return th * 8000 * self._cell_size()
0040
0041 def _get_size(self):
0042 return self._get_static_size(16)
0043
0044 def _get_thtype(self):
0045 return "static"
0046
0047 def _get_th(self, pool):
0048
0049 th = random.randint(3, 16)
0050 if pool["thtype"] == "dynamic":
0051 return th
0052 else:
0053 return self._get_static_size(th)
0054
0055 def _get_pool(self, direction):
0056 ing_pools = []
0057 egr_pools = []
0058 for pool in self._pools:
0059 if pool["type"] == "ingress":
0060 ing_pools.append(pool)
0061 else:
0062 egr_pools.append(pool)
0063 if direction == "ingress":
0064 arr = ing_pools
0065 else:
0066 arr = egr_pools
0067 return arr[random.randint(0, len(arr) - 1)]
0068
0069 def get_value(self, objid):
0070 if isinstance(objid, Pool):
0071 if objid["pool"] in [4, 8, 9, 10]:
0072
0073 raise SkipTest()
0074 else:
0075 return (self._get_size(), self._get_thtype())
0076 if isinstance(objid, TcBind):
0077 if objid["tc"] >= 8:
0078
0079 raise SkipTest()
0080 else:
0081 pool = self._get_pool(objid["type"])
0082 th = self._get_th(pool)
0083 pool_n = pool["pool"]
0084 return (pool_n, th)
0085 if isinstance(objid, PortPool):
0086 pool_n = objid["pool"]
0087 pool = self._pools[pool_n]
0088 assert pool["pool"] == pool_n
0089 th = self._get_th(pool)
0090 return (th,)
0091
0092
0093 class RecordValuePickerException(Exception):
0094 pass
0095
0096
0097 class RecordValuePicker:
0098 """
0099 Class for storing shared buffer configuration. Can handle 2 different
0100 objects, pool and tcbind. Provide an interface to get the stored values per
0101 object type.
0102 """
0103 def __init__(self, objlist):
0104 self._recs = []
0105 for item in objlist:
0106 self._recs.append({"objid": item, "value": item.var_tuple()})
0107
0108 def get_value(self, objid):
0109 if isinstance(objid, Pool) and objid["pool"] in [4, 8, 9, 10]:
0110
0111 raise SkipTest()
0112 if isinstance(objid, TcBind) and objid["tc"] >= 8:
0113
0114 raise SkipTest()
0115 for rec in self._recs:
0116 if rec["objid"].weak_eq(objid):
0117 return rec["value"]
0118 raise RecordValuePickerException()
0119
0120
0121 def run_cmd(cmd, json=False):
0122 out = subprocess.check_output(cmd, shell=True)
0123 if json:
0124 return j.loads(out)
0125 return out
0126
0127
0128 def run_json_cmd(cmd):
0129 return run_cmd(cmd, json=True)
0130
0131
0132 def log_test(test_name, err_msg=None):
0133 if err_msg:
0134 print("\t%s" % err_msg)
0135 print("TEST: %-80s [FAIL]" % test_name)
0136 else:
0137 print("TEST: %-80s [ OK ]" % test_name)
0138
0139
0140 class CommonItem(dict):
0141 varitems = []
0142
0143 def var_tuple(self):
0144 ret = []
0145 self.varitems.sort()
0146 for key in self.varitems:
0147 ret.append(self[key])
0148 return tuple(ret)
0149
0150 def weak_eq(self, other):
0151 for key in self:
0152 if key in self.varitems:
0153 continue
0154 if self[key] != other[key]:
0155 return False
0156 return True
0157
0158
0159 class CommonList(list):
0160 def get_by(self, by_obj):
0161 for item in self:
0162 if item.weak_eq(by_obj):
0163 return item
0164 return None
0165
0166 def del_by(self, by_obj):
0167 for item in self:
0168 if item.weak_eq(by_obj):
0169 self.remove(item)
0170
0171
0172 class Pool(CommonItem):
0173 varitems = ["size", "thtype"]
0174
0175 def dl_set(self, dlname, size, thtype):
0176 run_cmd("devlink sb pool set {} sb {} pool {} size {} thtype {}".format(dlname, self["sb"],
0177 self["pool"],
0178 size, thtype))
0179
0180
0181 class PoolList(CommonList):
0182 pass
0183
0184
0185 def get_pools(dlname, direction=None):
0186 d = run_json_cmd("devlink sb pool show -j")
0187 pools = PoolList()
0188 for pooldict in d["pool"][dlname]:
0189 if not direction or direction == pooldict["type"]:
0190 pools.append(Pool(pooldict))
0191 return pools
0192
0193
0194 def do_check_pools(dlname, pools, vp):
0195 for pool in pools:
0196 pre_pools = get_pools(dlname)
0197 try:
0198 (size, thtype) = vp.get_value(pool)
0199 except SkipTest:
0200 continue
0201 pool.dl_set(dlname, size, thtype)
0202 post_pools = get_pools(dlname)
0203 pool = post_pools.get_by(pool)
0204
0205 err_msg = None
0206 if pool["size"] != size:
0207 err_msg = "Incorrect pool size (got {}, expected {})".format(pool["size"], size)
0208 if pool["thtype"] != thtype:
0209 err_msg = "Incorrect pool threshold type (got {}, expected {})".format(pool["thtype"], thtype)
0210
0211 pre_pools.del_by(pool)
0212 post_pools.del_by(pool)
0213 if pre_pools != post_pools:
0214 err_msg = "Other pool setup changed as well"
0215 log_test("pool {} of sb {} set verification".format(pool["pool"],
0216 pool["sb"]), err_msg)
0217
0218
0219 def check_pools(dlname, pools):
0220
0221 record_vp = RecordValuePicker(pools)
0222
0223
0224 do_check_pools(dlname, pools, RandomValuePicker(pools))
0225
0226
0227 do_check_pools(dlname, pools, record_vp)
0228
0229
0230 class TcBind(CommonItem):
0231 varitems = ["pool", "threshold"]
0232
0233 def __init__(self, port, d):
0234 super(TcBind, self).__init__(d)
0235 self["dlportname"] = port.name
0236
0237 def dl_set(self, pool, th):
0238 run_cmd("devlink sb tc bind set {} sb {} tc {} type {} pool {} th {}".format(self["dlportname"],
0239 self["sb"],
0240 self["tc"],
0241 self["type"],
0242 pool, th))
0243
0244
0245 class TcBindList(CommonList):
0246 pass
0247
0248
0249 def get_tcbinds(ports, verify_existence=False):
0250 d = run_json_cmd("devlink sb tc bind show -j -n")
0251 tcbinds = TcBindList()
0252 for port in ports:
0253 err_msg = None
0254 if port.name not in d["tc_bind"] or len(d["tc_bind"][port.name]) == 0:
0255 err_msg = "No tc bind for port"
0256 else:
0257 for tcbinddict in d["tc_bind"][port.name]:
0258 tcbinds.append(TcBind(port, tcbinddict))
0259 if verify_existence:
0260 log_test("tc bind existence for port {} verification".format(port.name), err_msg)
0261 return tcbinds
0262
0263
0264 def do_check_tcbind(ports, tcbinds, vp):
0265 for tcbind in tcbinds:
0266 pre_tcbinds = get_tcbinds(ports)
0267 try:
0268 (pool, th) = vp.get_value(tcbind)
0269 except SkipTest:
0270 continue
0271 tcbind.dl_set(pool, th)
0272 post_tcbinds = get_tcbinds(ports)
0273 tcbind = post_tcbinds.get_by(tcbind)
0274
0275 err_msg = None
0276 if tcbind["pool"] != pool:
0277 err_msg = "Incorrect pool (got {}, expected {})".format(tcbind["pool"], pool)
0278 if tcbind["threshold"] != th:
0279 err_msg = "Incorrect threshold (got {}, expected {})".format(tcbind["threshold"], th)
0280
0281 pre_tcbinds.del_by(tcbind)
0282 post_tcbinds.del_by(tcbind)
0283 if pre_tcbinds != post_tcbinds:
0284 err_msg = "Other tc bind setup changed as well"
0285 log_test("tc bind {}-{} of sb {} set verification".format(tcbind["dlportname"],
0286 tcbind["tc"],
0287 tcbind["sb"]), err_msg)
0288
0289
0290 def check_tcbind(dlname, ports, pools):
0291 tcbinds = get_tcbinds(ports, verify_existence=True)
0292
0293
0294 record_vp = RecordValuePicker(tcbinds)
0295
0296
0297
0298 do_check_tcbind(ports, tcbinds, RandomValuePicker(pools))
0299
0300
0301 do_check_tcbind(ports, tcbinds, record_vp)
0302
0303
0304 class PortPool(CommonItem):
0305 varitems = ["threshold"]
0306
0307 def __init__(self, port, d):
0308 super(PortPool, self).__init__(d)
0309 self["dlportname"] = port.name
0310
0311 def dl_set(self, th):
0312 run_cmd("devlink sb port pool set {} sb {} pool {} th {}".format(self["dlportname"],
0313 self["sb"],
0314 self["pool"], th))
0315
0316
0317 class PortPoolList(CommonList):
0318 pass
0319
0320
0321 def get_portpools(ports, verify_existence=False):
0322 d = run_json_cmd("devlink sb port pool -j -n")
0323 portpools = PortPoolList()
0324 for port in ports:
0325 err_msg = None
0326 if port.name not in d["port_pool"] or len(d["port_pool"][port.name]) == 0:
0327 err_msg = "No port pool for port"
0328 else:
0329 for portpooldict in d["port_pool"][port.name]:
0330 portpools.append(PortPool(port, portpooldict))
0331 if verify_existence:
0332 log_test("port pool existence for port {} verification".format(port.name), err_msg)
0333 return portpools
0334
0335
0336 def do_check_portpool(ports, portpools, vp):
0337 for portpool in portpools:
0338 pre_portpools = get_portpools(ports)
0339 (th,) = vp.get_value(portpool)
0340 portpool.dl_set(th)
0341 post_portpools = get_portpools(ports)
0342 portpool = post_portpools.get_by(portpool)
0343
0344 err_msg = None
0345 if portpool["threshold"] != th:
0346 err_msg = "Incorrect threshold (got {}, expected {})".format(portpool["threshold"], th)
0347
0348 pre_portpools.del_by(portpool)
0349 post_portpools.del_by(portpool)
0350 if pre_portpools != post_portpools:
0351 err_msg = "Other port pool setup changed as well"
0352 log_test("port pool {}-{} of sb {} set verification".format(portpool["dlportname"],
0353 portpool["pool"],
0354 portpool["sb"]), err_msg)
0355
0356
0357 def check_portpool(dlname, ports, pools):
0358 portpools = get_portpools(ports, verify_existence=True)
0359
0360
0361 record_vp = RecordValuePicker(portpools)
0362
0363
0364 do_check_portpool(ports, portpools, RandomValuePicker(pools))
0365
0366
0367 do_check_portpool(ports, portpools, record_vp)
0368
0369
0370 class Port:
0371 def __init__(self, name):
0372 self.name = name
0373
0374
0375 class PortList(list):
0376 pass
0377
0378
0379 def get_ports(dlname):
0380 d = run_json_cmd("devlink port show -j")
0381 ports = PortList()
0382 for name in d["port"]:
0383 if name.find(dlname) == 0 and d["port"][name]["flavour"] == "physical":
0384 ports.append(Port(name))
0385 return ports
0386
0387
0388 def get_device():
0389 devices_info = run_json_cmd("devlink -j dev info")["info"]
0390 for d in devices_info:
0391 if "mlxsw_spectrum" in devices_info[d]["driver"]:
0392 return d
0393 return None
0394
0395
0396 class UnavailableDevlinkNameException(Exception):
0397 pass
0398
0399
0400 def test_sb_configuration():
0401
0402 random.seed(0)
0403
0404 dlname = get_device()
0405 if not dlname:
0406 raise UnavailableDevlinkNameException()
0407
0408 ports = get_ports(dlname)
0409 pools = get_pools(dlname)
0410
0411 check_pools(dlname, pools)
0412 check_tcbind(dlname, ports, pools)
0413 check_portpool(dlname, ports, pools)
0414
0415
0416 test_sb_configuration()