[PATCH 0/5] Add switchdev SPAN recipe
by Yotam Gigi
Add the SPAN port mirroring recipe for switchdev, and several lnst
features that were missing and were needed for that recipe.
Among the features added:
- The lnst slave now cleans the tc qdiscs and filters at the clean
procedure.
- The PacketAssert module supports setting promiscuos flag.
- The Switchdev TestLib IcmpPing module supports setting ping
parameters.
The SPAN recipe consist of two machines connected via a bridge in the
switch, and pinging each other, while ingress/egress port mirrors are
added and tested.
Yotam Gigi (5):
Controller: Task: Add documentation for one of the flags in run func.
switchdev: TestLib: Add controllable parameters to IcmpPing module.
PacketAssert: Add the promiscuous optional param.
Slave: Add basic support for tc.
recipes: switchdev: Add the SPAN recipe.
lnst/Controller/Task.py | 2 +
lnst/Slave/InterfaceManager.py | 28 +++++++++
recipes/switchdev/TestLib.py | 10 ++--
recipes/switchdev/l2-021-span.py | 119 ++++++++++++++++++++++++++++++++++++++
recipes/switchdev/l2-021-span.xml | 26 +++++++++
test_modules/PacketAssert.py | 6 +-
6 files changed, 185 insertions(+), 6 deletions(-)
create mode 100644 recipes/switchdev/l2-021-span.py
create mode 100644 recipes/switchdev/l2-021-span.xml
--
2.4.11
6 years, 10 months
[PATCH lnst 0/8] Layer 3 basic Switchdev recipes
by Elad Raz
This patchset introduce Layer 3 support in LNST. The pacthset add route APIs
and introduce route reporisoty for cleanup.
The last patchset add basic Layer 3 tests for router interface.
Elad Raz (8):
README: Adding prerequirement
contoller: Removing BridgeCtl class
InterfaceAPI: Adding slave IPv6 route support
InterfaceAPI: Adding nexthop route support
Machine: Adding route repository
InterfaceAPI: Export route commands
switchdev: Testlib adding UDP router support
recipes: Add L3 basic recipe
README.md | 23 +++++++++
lnst/Controller/Machine.py | 31 ++++++++++--
lnst/Controller/Task.py | 16 ++++++-
lnst/Controller/VirtUtils.py | 74 -----------------------------
lnst/Slave/InterfaceManager.py | 20 ++++++--
lnst/Slave/NetTestSlave.py | 24 ++++++++--
recipes/switchdev/TestLib.py | 1 +
recipes/switchdev/l3-000-minimal.py | 54 +++++++++++++++++++++
recipes/switchdev/l3-000-minimal.xml | 24 ++++++++++
recipes/switchdev/l3-001-router-port.py | 51 ++++++++++++++++++++
recipes/switchdev/l3-001-router-port.xml | 24 ++++++++++
recipes/switchdev/l3-002-vlan-interface.py | 54 +++++++++++++++++++++
recipes/switchdev/l3-002-vlan-interface.xml | 24 ++++++++++
recipes/switchdev/l3-003-bond-interface.py | 60 +++++++++++++++++++++++
recipes/switchdev/l3-003-bond-interface.xml | 26 ++++++++++
recipes/switchdev/l3-004-team-interface.py | 60 +++++++++++++++++++++++
recipes/switchdev/l3-004-team-interface.xml | 26 ++++++++++
17 files changed, 504 insertions(+), 88 deletions(-)
create mode 100644 recipes/switchdev/l3-000-minimal.py
create mode 100644 recipes/switchdev/l3-000-minimal.xml
create mode 100644 recipes/switchdev/l3-001-router-port.py
create mode 100644 recipes/switchdev/l3-001-router-port.xml
create mode 100644 recipes/switchdev/l3-002-vlan-interface.py
create mode 100644 recipes/switchdev/l3-002-vlan-interface.xml
create mode 100644 recipes/switchdev/l3-003-bond-interface.py
create mode 100644 recipes/switchdev/l3-003-bond-interface.xml
create mode 100644 recipes/switchdev/l3-004-team-interface.py
create mode 100644 recipes/switchdev/l3-004-team-interface.xml
--
2.4.3
7 years
[PATCH 1/2] recipes: switchdev: Remove IP address from LAG ports
by Ido Schimmel
From: Ido Schimmel <idosch(a)mellanox.com>
In kernel 4.8, L3 support was added to the mlxsw driver. Therefore,
non-bridged traffic is now directed to the switch itself only if the
packet's DIP matches the interface's IP.
However, due to several pending ordering problems in the switchdev
infrastructure, IP configuration is only offloaded to the device in
cases where it makes sense. Since IP configuration on a LAG port doesn't
make sense the operation is aborted and all the routes in the system are
flushed, thereby causing the machine to become unresponsive.
Until these issues are resolved in the kernel, remove the IP address
from the switch port so that when create_team() is called it won't be
configured upon port being up()-ed.
Signed-off-by: Ido Schimmel <idosch(a)mellanox.com>
---
recipes/switchdev/l2-006-bridge_team.py | 1 +
recipes/switchdev/l2-015-bridge_team_vlan1d.py | 1 +
2 files changed, 2 insertions(+)
diff --git a/recipes/switchdev/l2-006-bridge_team.py b/recipes/switchdev/l2-006-bridge_team.py
index 48b9752..5366b39 100644
--- a/recipes/switchdev/l2-006-bridge_team.py
+++ b/recipes/switchdev/l2-006-bridge_team.py
@@ -55,6 +55,7 @@ def do_task(ctl, hosts, ifaces, aliases):
tl.ping_simple(sw_if1, m1_if1)
# Repopulate the LAGs and make sure fastpath is OK.
+ sw_if1.set_addresses([]) # LAG port can't have IP address.
sw_lag3 = sw.create_team(slaves=[sw_if1, sw_if2],
config=team_config)
sw_br.slave_add(sw_lag3.get_id())
diff --git a/recipes/switchdev/l2-015-bridge_team_vlan1d.py b/recipes/switchdev/l2-015-bridge_team_vlan1d.py
index 723a04a..4078b94 100644
--- a/recipes/switchdev/l2-015-bridge_team_vlan1d.py
+++ b/recipes/switchdev/l2-015-bridge_team_vlan1d.py
@@ -76,6 +76,7 @@ def do_task(ctl, hosts, ifaces, aliases):
tl.ping_simple(sw_if1, m1_if1)
# Repopulate the LAGs and make sure fastpath is OK.
+ sw_if1.set_addresses([]) # LAG port can't have IP address.
sw_lag3 = sw.create_team(slaves=[sw_if1, sw_if2],
config=team_config)
sw_br1.slave_add(sw_lag3.get_id())
--
2.7.4
7 years
[PATCH] InterfaceManager: fix tc deconfiguration
by Ondrej Lichtner
From: Ondrej Lichtner <olichtne(a)redhat.com>
The recently added functionality of clearing tc configuration broke
recipes that already used netem functionality directly from the
configuration XML part. The problem was with trying to clear tc
configuration twice.
To fixed this I moved the new functions to be called after deconfigure()
takes care of it's own tc configuration. For this to work I also had to
add a check for interface existence since deconfigure() can remove the
device completely if NetworkManager is used.
Signed-off-by: Ondrej Lichtner <olichtne(a)redhat.com>
---
lnst/Slave/InterfaceManager.py | 18 ++++++++++++++++--
1 file changed, 16 insertions(+), 2 deletions(-)
diff --git a/lnst/Slave/InterfaceManager.py b/lnst/Slave/InterfaceManager.py
index 05bd71e..452bd89 100644
--- a/lnst/Slave/InterfaceManager.py
+++ b/lnst/Slave/InterfaceManager.py
@@ -520,6 +520,13 @@ class Device(object):
self._conf_dict = None
def _clear_tc_qdisc(self):
+ try:
+ #checks device existence as it might have been removed by
+ #calling self.deconfigure()
+ exec_cmd("ip l show %s" % self._name, log_outputs=False)
+ except:
+ return
+
exec_cmd("tc qdisc replace dev %s root pfifo" % self._name)
out, _ = exec_cmd("tc filter show dev %s" % self._name)
ingress_handles = re.findall("ingress (\\d+):", out)
@@ -532,6 +539,13 @@ class Device(object):
exec_cmd("tc qdisc del dev %s ingress" % self._name)
def _clear_tc_filters(self):
+ try:
+ #checks device existence as it might have been removed by
+ #calling self.deconfigure()
+ exec_cmd("ip l show %s" % self._name, log_outputs=False)
+ except:
+ return
+
out, _ = exec_cmd("tc filter show dev %s" % self._name)
egress_prefs = re.findall("pref (\\d+) .* handle", out)
@@ -552,10 +566,10 @@ class Device(object):
m_dev.clear_configuration()
if self._conf != None:
- self._clear_tc_qdisc()
- self._clear_tc_filters()
self.down()
self.deconfigure()
+ self._clear_tc_qdisc()
+ self._clear_tc_filters()
self.destroy()
self._conf = None
self._conf_dict = None
--
2.9.3
7 years
[PATCH] recipes: remove ip6 address from bond device in 3 vlans over
bond test
by Jan Tluka
The ip6 address was needed to configure 3 vlans over bond setup.
Originally this was a workaround for NM bug. With updated NetworkManager
the ip6 address causes that NM gets stuck on bond device configuration and
never activates it. The solution is to remove the address as the workaround
is no more needed.
Tested with NM: version 1.4
Signed-off-by: Jan Tluka <jtluka(a)redhat.com>
---
recipes/regression_tests/phase1/3_vlans_over_active_backup_bond.xml | 3 ---
1 file changed, 3 deletions(-)
diff --git a/recipes/regression_tests/phase1/3_vlans_over_active_backup_bond.xml b/recipes/regression_tests/phase1/3_vlans_over_active_backup_bond.xml
index f9b4922..26300b1 100644
--- a/recipes/regression_tests/phase1/3_vlans_over_active_backup_bond.xml
+++ b/recipes/regression_tests/phase1/3_vlans_over_active_backup_bond.xml
@@ -32,9 +32,6 @@
<slave id="eth1" />
<slave id="eth2" />
</slaves>
- <addresses>
- <address value="2002::1/64" />
- </addresses>
</bond>
<vlan id="vlan10">
<options>
--
2.4.11
7 years
[PATCH v5 0/3] changes in v5
by Kamil Jerabek
* fixed simple_netperf.py broken after rebase
* udp_size change in simple_netperf.py moved to proper commit
Kamil Jerabek (3):
Netperf: add option udp_size
regression_tests: add udp_size to netperf tests
regression_tests: skip unnecessary offload option for udp testing
recipes/regression_tests/phase1/3_vlans.py | 105 ++++++-----
.../regression_tests/phase1/3_vlans_over_bond.py | 105 ++++++-----
recipes/regression_tests/phase1/bonding_test.py | 97 ++++++----
recipes/regression_tests/phase1/simple_netperf.py | 101 +++++-----
.../phase1/virtual_bridge_2_vlans_over_bond.py | 17 +-
.../phase1/virtual_bridge_vlan_in_guest.py | 17 +-
.../phase1/virtual_bridge_vlan_in_host.py | 17 +-
.../regression_tests/phase2/3_vlans_over_team.py | 107 ++++++-----
recipes/regression_tests/phase2/team_test.py | 209 ++++++++++++---------
...l_ovs_bridge_2_vlans_over_active_backup_bond.py | 115 +++++++-----
.../phase2/virtual_ovs_bridge_vlan_in_guest.py | 115 +++++++-----
.../phase2/virtual_ovs_bridge_vlan_in_host.py | 115 +++++++-----
test_modules/Netperf.py | 11 ++
13 files changed, 662 insertions(+), 469 deletions(-)
--
2.5.5
7 years
[patch lnst] recipes: switchdev: Introduce shared buffer testing recipe
by Jiri Pirko
From: Jiri Pirko <jiri(a)mellanox.com>
This recipe utilizes devlink app to test setup of all shared buffer
configuration options.
Signed-off-by: Jiri Pirko <jiri(a)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(a)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
7 years
[patch lnst] NetTestCommand: fix requested json output check
by Jiri Pirko
From: Jiri Pirko <jiri(a)mellanox.com>
Fixes: bc9c6296e43 ("cmd: allow to parse JSON stdout into dictionary")
Signed-off-by: Jiri Pirko <jiri(a)mellanox.com>
---
lnst/Common/NetTestCommand.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/lnst/Common/NetTestCommand.py b/lnst/Common/NetTestCommand.py
index f91cc37..30aaabf 100644
--- a/lnst/Common/NetTestCommand.py
+++ b/lnst/Common/NetTestCommand.py
@@ -407,7 +407,7 @@ class NetTestCommandExec(NetTestCommandGeneric):
stdout, stderr = self.exec_from(self._command["from"],
self._command["command"])
else:
- json = True if "json" in self._command else False
+ json = True if "json" in self._command and self._command["json"] else False
stdout, stderr = self.exec_cmd(self._command["command"], json=json)
res_data = {"stdout": stdout, "stderr": stderr}
self.set_pass(res_data)
--
2.5.5
7 years, 1 month
[PATCH 1/2] InterfaceManager: Machine: ignore link stats when device not created
by Ondrej Lichtner
From: Ondrej Lichtner <olichtne(a)redhat.com>
When creation and configuration of a device fails, calling the
link_stats method would raise an Exception. Since this is always called
during deconfiguration, a configuration error would result in an
exception related crash of LNST and leave the Slave in an inconsistent
state - unable to run any more recipes unless manually restarted.
This commit fixes the issue by catching the exception and returning an
empty stats dictionary to indicate the failure. This is detected on the
Controller and the logging of device stats is skipped.
Signed-off-by: Ondrej Lichtner <olichtne(a)redhat.com>
---
lnst/Controller/Machine.py | 15 +++++++++------
lnst/Slave/InterfaceManager.py | 6 +++++-
2 files changed, 14 insertions(+), 7 deletions(-)
diff --git a/lnst/Controller/Machine.py b/lnst/Controller/Machine.py
index 3725de6..b2cff15 100644
--- a/lnst/Controller/Machine.py
+++ b/lnst/Controller/Machine.py
@@ -309,12 +309,15 @@ class Machine(object):
if isinstance(iface, UnusedInterface):
continue
stats = iface.link_stats()
- logging.debug("%s:%s:%s: RX:\t bytes: %d\t packets: %d\t dropped: %d" %
- (iface.get_netns(), iface.get_host(), iface.get_id(),
- stats["rx_bytes"], stats["rx_packets"], stats["rx_dropped"]))
- logging.debug("%s:%s:%s: TX:\t bytes: %d\t packets: %d\t dropped: %d" %
- (iface.get_netns(), iface.get_host(), iface.get_id(),
- stats["tx_bytes"], stats["tx_packets"], stats["tx_dropped"]))
+ if stats:
+ logging.debug("%s:%s:%s: RX:\t bytes: %d\t packets: %d\t dropped: %d" %
+ (iface.get_netns(), iface.get_host(),
+ iface.get_id(), stats["rx_bytes"],
+ stats["rx_packets"], stats["rx_dropped"]))
+ logging.debug("%s:%s:%s: TX:\t bytes: %d\t packets: %d\t dropped: %d" %
+ (iface.get_netns(), iface.get_host(),
+ iface.get_id(), stats["tx_bytes"],
+ stats["tx_packets"], stats["tx_dropped"]))
self._rpc_call("kill_cmds")
for netns in self._namespaces:
diff --git a/lnst/Slave/InterfaceManager.py b/lnst/Slave/InterfaceManager.py
index 51aa6cc..05bd71e 100644
--- a/lnst/Slave/InterfaceManager.py
+++ b/lnst/Slave/InterfaceManager.py
@@ -647,7 +647,11 @@ class Device(object):
def link_stats(self):
stats = {"devname": self._name,
"hwaddr": self._hwaddr}
- out, _ = exec_cmd("ip -s link show %s" % self._name)
+ try:
+ out, _ = exec_cmd("ip -s link show %s" % self._name)
+ except:
+ return {}
+
lines = iter(out.split("\n"))
for line in lines:
if (len(line.split()) == 0):
--
2.9.3
7 years, 1 month
[patch lnst] ExecCommand: leave report blank and avoid putting whole stdout there
by Jiri Pirko
From: Jiri Pirko <jiri(a)mellanox.com>
no need to put stdout there. Also this fixes json stdouwith keys
unusable as element names in xml.
Signed-off-by: Jiri Pirko <jiri(a)mellanox.com>
---
lnst/Common/NetTestCommand.py | 3 +++
1 file changed, 3 insertions(+)
diff --git a/lnst/Common/NetTestCommand.py b/lnst/Common/NetTestCommand.py
index 52f2a73..f91cc37 100644
--- a/lnst/Common/NetTestCommand.py
+++ b/lnst/Common/NetTestCommand.py
@@ -419,6 +419,9 @@ class NetTestCommandExec(NetTestCommandGeneric):
else:
self.set_fail(res_data)
+ def format_res_data(self, res_data, level=0):
+ return ""
+
def _format_cmd_res_header(self):
cmd_type = self._command["type"]
cmd_val = self._command["command"]
--
2.5.5
7 years, 1 month