Skip to content

Commit 678da29

Browse files
committed
Add '-w' argument to iptables
If multiple tests are executed in parallel (e.g. via pytest-xdist) and more than one test queries the iptables rules on the same host simultaneously, the following error will occur: E AssertionError: Unexpected exit code 4 for CommandResult(command='iptables -t filter -S', exit_status=4, stdout='', stderr='Another app is currently holding the xtables lock. Perhaps you want to use the -w option?') Iptables has an internal lock to prevent multiple simultaneous invocations. This change implements that suggestion and adds "-w 90" to the iptables command so that we will wait up to 90 seconds to obtain the lock. Unfortunatelly centos 6 is still maintained but doesn't have support for iptables with "-w" argument. So we have handle this by trying iptables with "-w 90", if it fail we retry without this option ("w argument support" is then cached at host level). Co-Authored-By James E. Blair <[email protected]>
1 parent dee08f2 commit 678da29

File tree

3 files changed

+46
-14
lines changed

3 files changed

+46
-14
lines changed

images/centos_6/Dockerfile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ FROM centos:6
22

33
RUN yum clean all && \
44
yum -y install \
5-
openssh-server && \
5+
openssh-server iptables && \
66
rm -f /etc/ssh/ssh_host_ecdsa_key /etc/ssh/ssh_host_rsa_key && \
77
ssh-keygen -q -N "" -t dsa -f /etc/ssh/ssh_host_ecdsa_key && \
88
ssh-keygen -q -N "" -t rsa -f /etc/ssh/ssh_host_rsa_key && \

test/test_modules.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -516,6 +516,13 @@ def test_iptables(host):
516516
assert ssh_rule_str in input_rules
517517
assert vip_redirect_rule_str in nat_rules
518518
assert vip_redirect_rule_str in nat_prerouting_rules
519+
assert host.iptables._has_w_argument is True
520+
521+
522+
@pytest.mark.testinfra_hosts('docker://centos_6')
523+
def test_iptables_centos6(host):
524+
host.iptables.rules()
525+
assert host.iptables._has_w_argument is False
519526

520527

521528
def test_ip6tables(host):

testinfra/modules/iptables.py

Lines changed: 38 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,39 @@
1616
class Iptables(InstanceModule):
1717
"""Test iptables rule exists"""
1818

19+
def __init__(self, *args, **kwargs):
20+
super().__init__(*args, **kwargs)
21+
# support for -w argument (since 1.6.0)
22+
# https://git.netfilter.org/iptables/commit/?id=aaa4ace72b
23+
# centos 6 has no support
24+
# centos 7 has 1.4 patched
25+
self._has_w_argument = None
26+
27+
def _iptables_command(self, version):
28+
if version == 4:
29+
iptables = "iptables"
30+
elif version == 6:
31+
iptables = "ip6tables"
32+
else:
33+
raise RuntimeError("Invalid version: %s" % version)
34+
if self._has_w_argument is False:
35+
return iptables
36+
else:
37+
return "{} -w 90".format(iptables)
38+
39+
def _run_iptables(self, version, cmd, *args):
40+
ipt_cmd = "{} {}".format(self._iptables_command(version), cmd)
41+
if self._has_w_argument is None:
42+
result = self.run_expect([0, 2], ipt_cmd, *args)
43+
if result.rc == 2:
44+
self._has_w_argument = False
45+
return self._run_iptables(version, cmd, *args)
46+
else:
47+
self._has_w_argument = True
48+
return result.stdout.rstrip('\r\n')
49+
else:
50+
return self.check_output(ipt_cmd, *args)
51+
1952
def rules(self, table='filter', chain=None, version=4):
2053
"""Returns list of iptables rules
2154
@@ -39,21 +72,13 @@ def rules(self, table='filter', chain=None, version=4):
3972
['-P PREROUTING ACCEPT']
4073
4174
"""
42-
43-
if version == 4:
44-
iptables = "iptables"
45-
elif version == 6:
46-
iptables = "ip6tables"
47-
else:
48-
raise RuntimeError("Invalid version: %s" % version)
49-
50-
rules = []
75+
cmd, args = "-t %s -S", [table]
5176
if chain:
52-
cmd = "{0} -t {1} -S {2}".format(iptables, table, chain)
53-
else:
54-
cmd = "{0} -t {1} -S".format(iptables, table)
77+
cmd += " %s"
78+
args += [chain]
5579

56-
for line in self.check_output(cmd).splitlines():
80+
rules = []
81+
for line in self._run_iptables(version, cmd, *args).splitlines():
5782
line = line.replace("\t", " ")
5883
rules.append(line)
5984
return rules

0 commit comments

Comments
 (0)