Skip to content

Commit 12179e7

Browse files
driver/rawnetworkinterfacedriver: add interface up/down/wait_state
Add methods to the RawNetworkInterfaceDriver to set interfaces up and down, as well as getting and waiting for the interface state. This allows the driver to take more control over the interface, preconfiguration is not needed anymore. Tests that expect the exporter interface to be down (such as ethernet selftests, cable tests) are now possible. Note that the RawNetworkInterfaceDriver now brings the bound interface up on activate and down on deactivate. Signed-off-by: Bastian Krause <[email protected]>
1 parent 47fdd37 commit 12179e7

File tree

2 files changed

+76
-2
lines changed

2 files changed

+76
-2
lines changed

helpers/labgrid-raw-interface

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ def main(program, options):
4545
if options.ifname in denylist:
4646
raise ValueError(f"Interface name '{options.ifname}' is denied in denylist.")
4747

48-
programs = ["tcpreplay", "tcpdump"]
48+
programs = ["tcpreplay", "tcpdump", "ip"]
4949
if program not in programs:
5050
raise ValueError(f"Invalid program {program} called with wrapper, valid programs are: {programs}")
5151

@@ -57,7 +57,7 @@ def main(program, options):
5757
args.append(f"--intf1={options.ifname}")
5858
args.append("-")
5959

60-
if program == "tcpdump":
60+
elif program == "tcpdump":
6161
args.append("-n")
6262
args.append(f"--interface={options.ifname}")
6363
# Write out each packet as it is received
@@ -79,6 +79,13 @@ def main(program, options):
7979
args.append("-W")
8080
args.append("1")
8181

82+
elif program == "ip":
83+
args.append("link")
84+
args.append("set")
85+
args.append("dev")
86+
args.append(options.ifname)
87+
args.append(options.action)
88+
8289
try:
8390
os.execvp(args[0], args)
8491
except FileNotFoundError as e:
@@ -102,6 +109,11 @@ if __name__ == "__main__":
102109
tcpreplay_parser = subparsers.add_parser("tcpreplay")
103110
tcpreplay_parser.add_argument("ifname", type=str, help="interface name")
104111

112+
# ip
113+
ip_parser = subparsers.add_parser("ip")
114+
ip_parser.add_argument("ifname", type=str, help="interface name")
115+
ip_parser.add_argument("action", type=str, choices=["up", "down"], help="action, one of {%(choices)s}")
116+
105117
args = parser.parse_args()
106118
try:
107119
main(args.program, args)

labgrid/driver/rawnetworkinterfacedriver.py

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
import contextlib
33
import json
44
import subprocess
5+
import time
56

67
import attr
78

@@ -10,6 +11,7 @@
1011
from ..step import step
1112
from ..util.helper import processwrapper
1213
from ..util.managedfile import ManagedFile
14+
from ..util.timeout import Timeout
1315
from ..resource.common import NetworkResource
1416

1517

@@ -25,6 +27,14 @@ def __attrs_post_init__(self):
2527
self._record_handle = None
2628
self._replay_handle = None
2729

30+
def on_activate(self):
31+
self._set_interface("up")
32+
self._wait_state("up")
33+
34+
def on_deactivate(self):
35+
self._set_interface("down")
36+
self._wait_state("down")
37+
2838
def _wrap_command(self, args):
2939
wrapper = ["sudo", "labgrid-raw-interface"]
3040

@@ -35,6 +45,58 @@ def _wrap_command(self, args):
3545
# keep wrapper and args as-is
3646
return wrapper + args
3747

48+
@step(args=["state"])
49+
def _set_interface(self, state):
50+
"""Set interface to given state."""
51+
cmd = ["ip", self.iface.ifname, state]
52+
cmd = self._wrap_command(cmd)
53+
subprocess.check_call(cmd)
54+
55+
@Driver.check_active
56+
def set_interface_up(self):
57+
"""Set bound interface up."""
58+
self._set_interface("up")
59+
60+
@Driver.check_active
61+
def set_interface_down(self):
62+
"""Set bound interface down."""
63+
self._set_interface("down")
64+
65+
def _get_state(self):
66+
"""Returns the bound interface's operstate."""
67+
if_state = self.iface.extra.get("state")
68+
if if_state:
69+
return if_state
70+
71+
cmd = self.iface.command_prefix + ["cat", f"/sys/class/net/{self.iface.ifname}/operstate"]
72+
output = processwrapper.check_output(cmd).decode("ascii")
73+
if_state = output.strip()
74+
return if_state
75+
76+
@Driver.check_active
77+
def get_state(self):
78+
"""Returns the bound interface's operstate."""
79+
return self._get_state()
80+
81+
@step(title="wait_state", args=["expected_state", "timeout"])
82+
def _wait_state(self, expected_state, timeout=60):
83+
"""Wait until the expected state is reached or the timeout expires."""
84+
timeout = Timeout(float(timeout))
85+
86+
while True:
87+
if self._get_state() == expected_state:
88+
return
89+
if timeout.expired:
90+
raise TimeoutError(
91+
f"exported interface {self.iface.ifname} did not go {expected_state} within {timeout.timeout} seconds"
92+
)
93+
time.sleep(1)
94+
95+
@Driver.check_active
96+
def wait_state(self, expected_state, timeout=60):
97+
"""Wait until the expected state is reached or the timeout expires."""
98+
self._wait_state(expected_state, timeout=timeout)
99+
38100
def _stop(self, proc, *, timeout=None):
39101
assert proc is not None
40102

0 commit comments

Comments
 (0)