tests: Use errno value rather than a specific int
[vpp.git] / test / vpp_qemu_utils.py
1 #!/usr/bin/env python
2
3 # Utility functions for QEMU tests ##
4
5 import subprocess
6 import sys
7 import os
8 import multiprocessing as mp
9
10
11 def can_create_namespaces(namespace="vpp_chk_4212"):
12     """Check if the environment allows creating the namespaces"""
13
14     try:
15         result = subprocess.run(["ip", "netns", "add", namespace], capture_output=True)
16         if result.returncode != 0:
17             return False
18         result = subprocess.run(["ip", "netns", "del", namespace], capture_output=True)
19         if result.returncode != 0:
20             return False
21         return True
22     except Exception:
23         return False
24
25
26 def create_namespace(ns):
27     """create one or more namespaces.
28
29     arguments:
30     ns -- a string value or an iterable of namespace names
31     """
32     if isinstance(ns, str):
33         namespaces = [ns]
34     else:
35         namespaces = ns
36     try:
37         for namespace in namespaces:
38             result = subprocess.run(["ip", "netns", "add", namespace])
39             if result.returncode != 0:
40                 raise Exception(f"Error while creating namespace {namespace}")
41     except subprocess.CalledProcessError as e:
42         raise Exception("Error creating namespace:", e.output)
43
44
45 def add_namespace_route(ns, prefix, gw_ip):
46     """Add a route to a namespace.
47
48     arguments:
49     ns -- namespace string value
50     prefix -- NETWORK/MASK or "default"
51     gw_ip -- Gateway IP
52     """
53     try:
54         subprocess.run(
55             ["ip", "netns", "exec", ns, "ip", "route", "add", prefix, "via", gw_ip],
56             capture_output=True,
57         )
58     except subprocess.CalledProcessError as e:
59         raise Exception("Error adding route to namespace:", e.output)
60
61
62 def delete_host_interfaces(*host_interface_names):
63     """Delete host interfaces.
64
65     arguments:
66     host_interface_names - sequence of host interface names to be deleted
67     """
68     for host_interface_name in host_interface_names:
69         try:
70             subprocess.run(
71                 ["ip", "link", "del", host_interface_name], capture_output=True
72             )
73         except subprocess.CalledProcessError as e:
74             raise Exception("Error deleting host interface:", e.output)
75
76
77 def create_host_interface(
78     host_interface_name, vpp_interface_name, host_namespace, *host_ip_prefixes
79 ):
80     """Create a host interface of type veth.
81
82     arguments:
83     host_interface_name -- name of the veth interface on the host side
84     vpp_interface_name -- name of the veth interface on the VPP side
85     host_namespace -- host namespace into which the host_interface needs to be set
86     host_ip_prefixes -- a sequence of ip/prefix-lengths to be set
87                         on the host_interface
88     """
89     try:
90         process = subprocess.run(
91             [
92                 "ip",
93                 "link",
94                 "add",
95                 "name",
96                 vpp_interface_name,
97                 "type",
98                 "veth",
99                 "peer",
100                 "name",
101                 host_interface_name,
102             ],
103             capture_output=True,
104         )
105         if process.returncode != 0:
106             print(f"Error creating host interface: {process.stderr}")
107             sys.exit(1)
108
109         process = subprocess.run(
110             ["ip", "link", "set", host_interface_name, "netns", host_namespace],
111             capture_output=True,
112         )
113         if process.returncode != 0:
114             print(f"Error setting host interface namespace: {process.stderr}")
115             sys.exit(1)
116
117         process = subprocess.run(
118             ["ip", "link", "set", "dev", vpp_interface_name, "up"], capture_output=True
119         )
120         if process.returncode != 0:
121             print(f"Error bringing up the host interface: {process.stderr}")
122             sys.exit(1)
123
124         process = subprocess.run(
125             [
126                 "ip",
127                 "netns",
128                 "exec",
129                 host_namespace,
130                 "ip",
131                 "link",
132                 "set",
133                 "dev",
134                 host_interface_name,
135                 "up",
136             ],
137             capture_output=True,
138         )
139         if process.returncode != 0:
140             print(
141                 f"Error bringing up the host interface in namespace: "
142                 f"{process.stderr}"
143             )
144             sys.exit(1)
145
146         for host_ip_prefix in host_ip_prefixes:
147             process = subprocess.run(
148                 [
149                     "ip",
150                     "netns",
151                     "exec",
152                     host_namespace,
153                     "ip",
154                     "addr",
155                     "add",
156                     host_ip_prefix,
157                     "dev",
158                     host_interface_name,
159                 ],
160                 capture_output=True,
161             )
162             if process.returncode != 0:
163                 print(
164                     f"Error setting ip prefix on the host interface: "
165                     f"{process.stderr}"
166                 )
167                 sys.exit(1)
168     except subprocess.CalledProcessError as e:
169         raise Exception("Error adding route to namespace:", e.output)
170
171
172 def set_interface_mtu(namespace, interface, mtu, logger):
173     """set an mtu number on a linux device interface."""
174     args = ["ip", "link", "set", "mtu", str(mtu), "dev", interface]
175     if namespace:
176         args = ["ip", "netns", "exec", namespace] + args
177     try:
178         logger.debug(
179             f"Setting mtu:{mtu} on linux interface:{interface} "
180             f"in namespace:{namespace}"
181         )
182         subprocess.run(args)
183     except subprocess.CalledProcessError as e:
184         raise Exception("Error updating mtu:", e.output)
185
186
187 def enable_interface_gso(namespace, interface):
188     """enable gso offload on a linux device interface."""
189     args = ["ethtool", "-K", interface, "rx", "on", "tx", "on"]
190     if namespace:
191         args = ["ip", "netns", "exec", namespace] + args
192     try:
193         process = subprocess.run(args, capture_output=True)
194         if process.returncode != 0:
195             print(
196                 f"Error enabling GSO offload on linux device interface: "
197                 f"{process.stderr}"
198             )
199             sys.exit(1)
200     except subprocess.CalledProcessError as e:
201         raise Exception("Error enabling gso:", e.output)
202
203
204 def disable_interface_gso(namespace, interface):
205     """disable gso offload on a linux device interface."""
206     args = ["ethtool", "-K", interface, "rx", "off", "tx", "off"]
207     if namespace:
208         args = ["ip", "netns", "exec", namespace] + args
209     try:
210         process = subprocess.run(args, capture_output=True)
211         if process.returncode != 0:
212             print(
213                 f"Error disabling GSO offload on linux device interface: "
214                 f"{process.stderr}"
215             )
216             sys.exit(1)
217     except subprocess.CalledProcessError as e:
218         raise Exception("Error disabling gso:", e.output)
219
220
221 def delete_namespace(ns):
222     """delete one or more namespaces.
223
224     arguments:
225     namespaces -- a list of namespace names
226     """
227     if isinstance(ns, str):
228         namespaces = [ns]
229     else:
230         namespaces = ns
231     try:
232         for namespace in namespaces:
233             result = subprocess.run(
234                 ["ip", "netns", "del", namespace], capture_output=True
235             )
236             if result.returncode != 0:
237                 raise Exception(f"Error while deleting namespace {namespace}")
238     except subprocess.CalledProcessError as e:
239         raise Exception("Error deleting namespace:", e.output)
240
241
242 def list_namespace(ns):
243     """List the IP address of a namespace"""
244     try:
245         subprocess.run(["ip", "netns", "exec", ns, "ip", "addr"])
246     except subprocess.CalledProcessError as e:
247         raise Exception("Error listing namespace IP:", e.output)
248
249
250 def libmemif_test_app(memif_sock_path, logger):
251     """Build & run the libmemif test_app for memif interface testing."""
252     test_dir = os.path.dirname(os.path.realpath(__file__))
253     ws_root = os.path.dirname(test_dir)
254     libmemif_app = os.path.join(
255         ws_root, "extras", "libmemif", "build", "examples", "test_app"
256     )
257
258     def build_libmemif_app():
259         if not os.path.exists(libmemif_app):
260             print(f"Building app:{libmemif_app} for memif interface testing")
261             libmemif_app_dir = os.path.join(ws_root, "extras", "libmemif", "build")
262             if not os.path.exists(libmemif_app_dir):
263                 os.makedirs(libmemif_app_dir)
264             os.chdir(libmemif_app_dir)
265             try:
266                 p = subprocess.run(["cmake", ".."], capture_output=True)
267                 logger.debug(p.stdout)
268                 if p.returncode != 0:
269                     print(f"libmemif app:{libmemif_app} cmake error:{p.stderr}")
270                     sys.exit(1)
271                 p = subprocess.run(["make"], capture_output=True)
272                 logger.debug(p.stdout)
273                 if p.returncode != 0:
274                     print(f"Error building libmemif app:{p.stderr}")
275                     sys.exit(1)
276             except subprocess.CalledProcessError as e:
277                 raise Exception("Error building libmemif_test_app:", e.output)
278
279     def start_libmemif_app():
280         """Restart once if the initial run fails."""
281         max_tries = 2
282         run = 0
283         if not os.path.exists(libmemif_app):
284             raise Exception(
285                 f"Error could not locate the libmemif test app:{libmemif_app}"
286             )
287         args = [libmemif_app, "-b", "9216", "-s", memif_sock_path]
288         while run < max_tries:
289             try:
290                 process = subprocess.run(args, capture_output=True)
291                 logger.debug(process.stdout)
292                 if process.returncode != 0:
293                     msg = f"Error starting libmemif app:{libmemif_app}"
294                     logger.error(msg)
295                     raise Exception(msg)
296             except Exception:
297                 msg = f"re-starting libmemif app:{libmemif_app}"
298                 logger.error(msg)
299                 continue
300             else:
301                 break
302             finally:
303                 run += 1
304
305     build_libmemif_app()
306     process = mp.Process(target=start_libmemif_app)
307     process.start()
308     return process