Skip to content

Commit 322b2f7

Browse files
committed
modules/iproute2: Add filters in routes & addresses
1 parent f4def1b commit 322b2f7

File tree

2 files changed

+114
-7
lines changed

2 files changed

+114
-7
lines changed

test/test_modules.py

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -650,6 +650,23 @@ def test_interface(host, family):
650650
assert default_itf.exists
651651

652652

653+
def test_iproute2_addresses(host):
654+
assert host.iproute2.exists
655+
656+
addresses = host.iproute2.addresses()
657+
658+
assert len(addresses) > 0
659+
assert addresses[0].get("ifname") and addresses[0].get("ifindex")
660+
661+
filtered_addresses = host.iproute2.addresses(ifname="lo")
662+
assert filtered_addresses[0].get("ifname") == "lo" and len(filtered_addresses) == 1
663+
664+
filtered_addresses2 = host.iproute2.addresses(local="127.0.0.1")
665+
assert (
666+
filtered_addresses2[0].get("ifname") == "lo" and len(filtered_addresses2) == 1
667+
)
668+
669+
653670
def test_iproute2_links(host):
654671
assert host.iproute2.exists
655672

@@ -665,6 +682,9 @@ def test_iproute2_routes(host):
665682
routes = host.iproute2.routes()
666683
assert len(routes) > 0
667684

685+
filtered_routes = host.iproute2.routes(table="local", scope="host", src="127.0.0.1")
686+
assert filtered_routes[0].get("protocol") == "kernel" and len(filtered_routes) > 1
687+
668688

669689
def test_iproute2_rules(host):
670690
assert host.iproute2.exists
@@ -675,6 +695,13 @@ def test_iproute2_rules(host):
675695
assert rules[0].get("src") == "all"
676696
assert rules[0].get("table") == "local"
677697

698+
cmd = host.run("ip rule add from 1.2.3.4/32 table 123")
699+
assert cmd.exit_status == 0, f"{cmd.stdout}\n{cmd.stderr}"
700+
701+
rules_123 = host.iproute2.rules(src="1.2.3.4/32")
702+
assert len(rules_123) > 0
703+
assert rules_123[0].get("src") == "1.2.3.4"
704+
678705

679706
def test_iproute2_tunnels(host):
680707
assert host.iproute2.exists

testinfra/modules/iproute2.py

Lines changed: 87 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ def __repr__(self):
4848
def _ip(self):
4949
ip_cmd = self.find_command("ip")
5050
if self.namespace is not None:
51-
ip_cmd = f"{ip_cmd} netns exec {self.namespace} {ip_cmd}"
51+
ip_cmd = f"{ip_cmd} -n {self.namespace}"
5252
if self.family is not None:
5353
ip_cmd = f"{ip_cmd} -f {self.family}"
5454
return ip_cmd
@@ -57,27 +57,107 @@ def _ip(self):
5757
def exists(self):
5858
return self.run_test("{} -V".format(self._ip)).rc == 0
5959

60-
def addresses(self):
60+
def addresses(self, address=None, ifname=None, local=None):
6161
"""Return the addresses associated with interfaces"""
6262
cmd = f"{self._ip} --json address show"
6363
out = self.check_output(cmd)
64-
return json.loads(out)
64+
j = json.loads(out)
65+
o = []
66+
if address is None and ifname is None and local is None:
67+
# no filters, bail out early
68+
return j
69+
if address is not None:
70+
[o.append(x) for x in j if x["address"] == address]
71+
if ifname is not None:
72+
[o.append(x) for x in j if x["ifname"] == ifname]
73+
if local is not None:
74+
for x in j:
75+
for addr in x["addr_info"]: # multiple IPs in an interface
76+
if addr["local"] == local:
77+
o.append(x)
78+
return o
6579

6680
def links(self):
6781
"""Return links and their state"""
6882
cmd = f"{self._ip} --json link show"
6983
out = self.check_output(cmd)
7084
return json.loads(out)
7185

72-
def routes(self):
86+
def routes(
87+
self, table="all", device=None, scope=None, proto=None, src=None, metric=None
88+
):
7389
"""Return the routes installed"""
74-
cmd = f"{self._ip} --json route show table all"
90+
cmd = f"{self._ip} --json route show "
91+
options = []
92+
if table is not None:
93+
options += ["table", table]
94+
if device is not None:
95+
options += ["dev", device]
96+
if scope is not None:
97+
options += ["scope", scope]
98+
if proto is not None:
99+
options += ["proto", proto]
100+
if src is not None:
101+
options += ["src", src]
102+
if metric is not None:
103+
options += ["metric", metric]
104+
105+
cmd += " ".join(options)
75106
out = self.check_output(cmd)
76107
return json.loads(out)
77108

78-
def rules(self):
109+
def rules(
110+
self,
111+
src=None,
112+
to=None,
113+
tos=None,
114+
fwmark=None,
115+
iif=None,
116+
oif=None,
117+
pref=None,
118+
uidrange=None,
119+
ipproto=None,
120+
sport=None,
121+
dport=None,
122+
):
79123
"""Return the rules our routing policy consists of"""
80-
cmd = f"{self._ip} --json rule show"
124+
cmd = f"{self._ip} --json rule show "
125+
126+
options = []
127+
if src is not None:
128+
options += ["from", src]
129+
130+
if to is not None:
131+
options += ["to", to]
132+
133+
if tos is not None:
134+
options += ["tos", tos]
135+
136+
if fwmark is not None:
137+
options += ["fwmark", fwmark]
138+
139+
if iif is not None:
140+
options += ["iif", iif]
141+
142+
if oif is not None:
143+
options += ["oif", oif]
144+
145+
if pref is not None:
146+
options += ["pref", pref]
147+
148+
if uidrange is not None:
149+
options += ["uidrange", uidrange]
150+
151+
if ipproto is not None:
152+
options += ["ipproto", ipproto]
153+
154+
if sport is not None:
155+
options += ["sport", sport]
156+
157+
if dport is not None:
158+
options += ["dport", dport]
159+
160+
cmd += " ".join(options)
81161
out = self.check_output(cmd)
82162
return json.loads(out)
83163

0 commit comments

Comments
 (0)