[prev in list] [next in list] [prev in thread] [next in thread]
List: lnst-developers
Subject: Re: [patch lnst] recipes: switchdev: Introduce shared buffer testing recipe
From: Ondrej Lichtner <olichtne () redhat ! com>
Date: 2016-08-25 11:41:26
Message-ID: 20160825114126.GA2677 () olichtne_rh ! localdomain
[Download RAW message or body]
On Wed, Aug 24, 2016 at 04:59:19PM +0200, Jiri Pirko wrote:
> From: Jiri Pirko <jiri@mellanox.com>
>
> This recipe utilizes devlink app to test setup of all shared buffer
> configuration options.
>
> Signed-off-by: Jiri Pirko <jiri@mellanox.com>
> ---
> recipes/switchdev/sharedbuffer.py | 305 +++++++++++++++++++++++++++++++++++++
> recipes/switchdev/sharedbuffer.xml | 22 +++
> 2 files changed, 327 insertions(+)
> create mode 100644 recipes/switchdev/sharedbuffer.py
> create mode 100644 recipes/switchdev/sharedbuffer.xml
>
> diff --git a/recipes/switchdev/sharedbuffer.py b/recipes/switchdev/sharedbuffer.py
> new file mode 100644
> index 0000000..28a6f44
> --- /dev/null
> +++ b/recipes/switchdev/sharedbuffer.py
> @@ -0,0 +1,305 @@
> +"""
> +Copyright 2016 Mellanox Technologies. All rights reserved.
> +Licensed under the GNU General Public License, version 2 as
> +published by the Free Software Foundation; see COPYING for details.
> +"""
> +
> +__author__ = """
> +jiri@mellanox.com (Jiri Pirko)
> +"""
> +
> +from lnst.Controller.Task import ctl
> +from TestLib import TestLib
> +from time import sleep
> +import random
> +
> +class RandomValuePicker:
> + def __init__(self, pools):
> + self._pools = {"ingress": [], "egress": []}
> + for pool in pools:
> + self._pools[pool["type"]].append(pool["pool"])
> +
> + def _get_size(self):
> + # support only this fixed size for now
> + return 12401088
> +
> + def _get_thtype(self):
> + # support static threshold only for now
> + return "static"
> +
> + def _get_th(self):
> + # support dynamic threshold only for now
> + return random.randint(3,16)
> +
> + def _get_pool(self, direction):
> + arr = self._pools[direction]
> + return arr[random.randint(0, len(arr) - 1)]
> +
> + def get_value(self, objid):
> + if isinstance(objid, Pool):
> + return (self._get_size(), self._get_thtype())
> + if isinstance(objid, TcBind):
> + pool = self._get_pool(objid["type"])
> + th = self._get_th()
> + return (pool, th)
> + if isinstance(objid, PortPool):
> + return (self._get_th(),)
> +
> +class RecordValuePickerException(Exception):
> + pass
> +
> +class RecordValuePicker:
> + def __init__(self, objlist):
> + self._recs = []
> + for item in objlist:
> + self._recs.append({"objid": item, "value": item.var_tuple()})
> +
> + def get_value(self, objid):
> + for rec in self._recs:
> + if rec["objid"].weak_eq(objid):
> + return rec["value"]
> + raise RecordValuePickerException()
> +
> +class RunCmdException(Exception):
> + pass
> +
> +def run_cmd(host, cmd, json=False):
> + cmd = host.run(cmd, json=json)
> + if not cmd.passed():
> + raise RunCmdException(cmd.get_result()["res_data"]["stderr"])
> + return cmd.out()
> +
> +def run_json_cmd(host, cmd):
> + return run_cmd(host, cmd, json=True)
> +
> +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, sw, dlname, size, thtype):
> + run_cmd(sw, "devlink sb pool set {} sb {} pool {} size {} thtype \
> {}".format(dlname, self["sb"], + \
> self["pool"], + \
> size, thtype)) +
> +class PoolList(CommonList):
> + pass
> +
> +def get_pools(sw, dlname, direction=None):
> + d = run_json_cmd(sw, "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(tl, sw, dlname, pools, vp):
> + for pool in pools:
> + pre_pools = get_pools(sw, dlname)
> + (size, thtype) = vp.get_value(pool)
> + pool.dl_set(sw, dlname, size, thtype)
> + post_pools = get_pools(sw, 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"
> + tl.custom(sw, "pool {} of sb {} set verification".format(pool["pool"],
> + pool["sb"]), \
> err_msg) +
> +def check_pools(tl, sw, dlname, pools):
> + record_vp = RecordValuePicker(pools)
> + do_check_pools(tl, sw, dlname, pools, RandomValuePicker(pools))
> + do_check_pools(tl, sw, 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, sw, pool, th):
> + run_cmd(sw, "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(tl, sw, ports, verify_existence=False):
> + d = run_json_cmd(sw, "devlink sb tc bind show -j -n")
> + tcbinds = TcBindList()
> + for port in ports:
> + err_msg = None
> + if not port.name 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:
> + tl.custom(sw, "tc bind existence for port {} \
> verification".format(port.name, err_msg)) + return tcbinds
> +
> +def do_check_tcbind(tl, sw, ports, tcbinds, vp):
> + for tcbind in tcbinds:
> + pre_tcbinds = get_tcbinds(tl, sw, ports)
> + (pool, th) = vp.get_value(tcbind)
> + tcbind.dl_set(sw, pool, th)
> + post_tcbinds = get_tcbinds(tl, sw, 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"
> + tl.custom(sw, "tc bind {}-{} of sb {} set \
> verification".format(tcbind["dlportname"], + \
> tcbind["tc"], + \
> tcbind["sb"]), err_msg) +
> +def check_tcbind(tl, sw, dlname, ports, pools):
> + tcbinds = get_tcbinds(tl, sw, ports, verify_existence=True)
> + record_vp = RecordValuePicker(tcbinds)
> + do_check_tcbind(tl, sw, ports, tcbinds, RandomValuePicker(pools))
> + do_check_tcbind(tl, sw, 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, sw, th):
> + run_cmd(sw, "devlink sb port pool set {} sb {} pool {} th \
> {}".format(self["dlportname"], + \
> self["sb"], + \
> self["pool"], th)) +
> +class PortPoolList(CommonList):
> + pass
> +
> +def get_portpools(tl, sw, ports, verify_existence=False):
> + d = run_json_cmd(sw, "devlink sb port pool -j -n")
> + portpools = PortPoolList()
> + for port in ports:
> + err_msg = None
> + if not port.name 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:
> + tl.custom(sw, "port pool existence for port {} \
> verification".format(port.name, err_msg)) + return portpools
> +
> +def do_check_portpool(tl, sw, ports, portpools, vp):
> + for portpool in portpools:
> + pre_portpools = get_portpools(tl, sw, ports)
> + (th,) = vp.get_value(portpool)
> + portpool.dl_set(sw, th)
> + post_portpools = get_portpools(tl, sw, 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"
> + tl.custom(sw, "port pool {}-{} of sb {} set \
> verification".format(portpool["dlportname"], + \
> portpool["pool"], + \
> portpool["sb"]), err_msg) +
> +def check_portpool(tl, sw, dlname, ports, pools):
> + portpools = get_portpools(tl, sw, ports, verify_existence=True)
> + record_vp = RecordValuePicker(portpools)
> + do_check_portpool(tl, sw, ports, portpools, RandomValuePicker(pools))
> + do_check_portpool(tl, sw, ports, portpools, record_vp)
> +
> +class Port:
> + def __init__(self, name):
> + self.name = name
> +
> +class PortList(list):
> + pass
> +
> +def get_ports(sw, dlname):
> + d = run_json_cmd(sw, "devlink port show -j")
> + ports = PortList()
> + for name in d["port"]:
> + if name.find(dlname) == 0:
> + ports.append(Port(name))
> + return ports
> +
> +class UnavailableDevlinkNameException(Exception):
> + pass
> +
> +def do_task(ctl, hosts, ifaces, aliases):
> + m1, m2, sw = hosts
> + m1_if1, m2_if1, sw_if1, sw_if2 = ifaces
> +
> + tl = TestLib(ctl, aliases)
> +
> + dlname = sw_if1.get_devlink_name()
> + if not dlname:
> + raise UnavailableDevlinkNameException()
> +
> + ports = get_ports(sw, dlname)
> + pools = get_pools(sw, dlname)
> + check_pools(tl, sw, dlname, pools)
> + check_tcbind(tl, sw, dlname, ports, pools)
> + check_portpool(tl, sw, dlname, ports, pools)
> +
> +do_task(ctl, [ctl.get_host("machine1"),
> + ctl.get_host("machine2"),
> + ctl.get_host("switch")],
> + [ctl.get_host("machine1").get_interface("if1"),
> + ctl.get_host("machine2").get_interface("if1"),
> + ctl.get_host("switch").get_interface("if1"),
> + ctl.get_host("switch").get_interface("if2")],
> + ctl.get_aliases())
> diff --git a/recipes/switchdev/sharedbuffer.xml \
> b/recipes/switchdev/sharedbuffer.xml new file mode 100644
> index 0000000..cfdb973
> --- /dev/null
> +++ b/recipes/switchdev/sharedbuffer.xml
> @@ -0,0 +1,22 @@
> +<lnstrecipe xmlns:xi="http://www.w3.org/2003/XInclude">
> + <xi:include href="default_aliases.xml" />
> + <network>
> + <host id="machine1">
> + <interfaces>
> + <eth id="if1" label="A" />
> + </interfaces>
> + </host>
> + <host id="machine2">
> + <interfaces>
> + <eth id="if1" label="B" />
> + </interfaces>
> + </host>
> + <host id="switch">
> + <interfaces>
> + <eth id="if1" label="A" />
> + <eth id="if2" label="B" />
> + </interfaces>
> + </host>
> + </network>
> + <task python="sharedbuffer.py" />
> +</lnstrecipe>
> --
> 2.5.5
>
ack
_______________________________________________
LNST-developers mailing list
lnst-developers@lists.fedorahosted.org
https://lists.fedorahosted.org/admin/lists/lnst-developers@lists.fedorahosted.org
[prev in list] [next in list] [prev in thread] [next in thread]
Configure |
About |
News |
Add a list |
Sponsored by KoreLogic