Skip to content

Commit 5c46c79

Browse files
authored
Interface names and default (#615)
Also Fix breaking change in mypy 0.900 and install missing stub package for paramiko
1 parent 8be8a29 commit 5c46c79

File tree

3 files changed

+83
-5
lines changed

3 files changed

+83
-5
lines changed

test-requirements.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ mock
22
pytest-cov
33
pytest-xdist
44
paramiko
5+
types-paramiko
56
tornado<5
67
salt
78
pywinrm

test/test_modules.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -677,3 +677,26 @@ def test_addr_namespace(host):
677677
with pytest.raises(NotImplementedError):
678678
# nc is not available so an error is raised
679679
assert not namespace_lookup.port("443").is_reachable
680+
681+
682+
@pytest.mark.parametrize(
683+
"family",
684+
["inet", None],
685+
)
686+
def test_interface(host, family):
687+
# exist
688+
assert host.interface("eth0", family=family).exists
689+
assert not host.interface("does_not_exist", family=family).exists
690+
# adresses
691+
addresses = host.interface.default(family).addresses
692+
assert len(addresses) > 0
693+
for add in addresses:
694+
try:
695+
ip_address(add)
696+
except ValueError:
697+
pytest.fail(f"{add} is not a valid IP address")
698+
# names and default
699+
assert "eth0" in host.interface.names()
700+
default_itf = host.interface.default(family)
701+
assert default_itf.name == "eth0"
702+
assert default_itf.exists

testinfra/modules/interface.py

Lines changed: 59 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,20 @@
1515

1616

1717
class Interface(Module):
18-
"""Test network interfaces"""
18+
"""Test network interfaces
1919
20-
def __init__(self, name):
20+
>>> host.interface("eth0").exists
21+
True
22+
23+
Optionally, the protocol family to use can be enforced.
24+
25+
>>> host.interface("eth0", "inet6").adresses
26+
['fe80::e291:f5ff:fe98:6b8c']
27+
"""
28+
29+
def __init__(self, name, family=None):
2130
self.name = name
31+
self.family = family
2232
super().__init__()
2333

2434
@property
@@ -49,30 +59,74 @@ def get_module_class(cls, host):
4959
return BSDInterface
5060
raise NotImplementedError
5161

62+
@classmethod
63+
def names(cls):
64+
"""Return the names of all the interfaces.
65+
66+
>>> host.interface.names()
67+
['lo', 'tunl0', 'ip6tnl0', 'eth0']
68+
"""
69+
raise NotImplementedError
70+
71+
@classmethod
72+
def default(cls, family=None):
73+
"""Return the interface used for the default route.
74+
75+
>>> host.interface.default()
76+
<interface eth0>
77+
78+
Optionally, the protocol family to use can be enforced.
79+
80+
>>> host.interface.default("inet6")
81+
None
82+
"""
83+
raise NotImplementedError
84+
5285

5386
class LinuxInterface(Interface):
5487
@cached_property
5588
def _ip(self):
56-
return self.find_command("ip")
89+
ip_cmd = self.find_command("ip")
90+
if self.family is not None:
91+
ip_cmd = f"{ip_cmd} -f {self.family}"
92+
return ip_cmd
5793

5894
@property
5995
def exists(self):
60-
return self.run_test("%s link show %s", self._ip, self.name).rc == 0
96+
return self.run_test("{} link show %s".format(self._ip), self.name).rc == 0
6197

6298
@property
6399
def speed(self):
64100
return int(self.check_output("cat /sys/class/net/%s/speed", self.name))
65101

66102
@property
67103
def addresses(self):
68-
stdout = self.check_output("%s addr show %s", self._ip, self.name)
104+
stdout = self.check_output("{} addr show %s".format(self._ip), self.name)
69105
addrs = []
70106
for line in stdout.splitlines():
71107
splitted = [e.strip() for e in line.split(" ") if e]
72108
if splitted and splitted[0] in ("inet", "inet6"):
73109
addrs.append(splitted[1].split("/", 1)[0])
74110
return addrs
75111

112+
@classmethod
113+
def default(cls, family=None):
114+
_default = cls(None, family=family)
115+
out = cls.check_output("{} route ls".format(_default._ip))
116+
for line in out.splitlines():
117+
if "default" in line:
118+
_default.name = line.strip().rsplit(" ", 1)[-1]
119+
return _default
120+
121+
@classmethod
122+
def names(cls):
123+
# -o is to tell the ip command to return 1 line per interface
124+
out = cls.check_output("{} -o link show".format(cls(None)._ip))
125+
interfaces = []
126+
for line in out.splitlines():
127+
interfaces.append(line.strip().split(": ", 2)[1].split("@", 1)[0])
128+
return interfaces
129+
76130

77131
class BSDInterface(Interface):
78132
@property

0 commit comments

Comments
 (0)