tests: Use errno value rather than a specific int
[vpp.git] / test / vpp_running.py
1 #!/usr/bin/env python3
2
3 # Supporting module for running tests against a running VPP.
4 # This module is used by the test framework. Do not invoke this module
5 # directly for running tests against a running vpp. Use run.py for
6 # running all unit tests.
7
8 from glob import glob
9 import os
10 import sys
11 import subprocess
12 from config import config
13
14
15 def use_running(cls):
16     """Update VPPTestCase to use running VPP's sock files & methods.
17
18     Arguments:
19     cls -- VPPTestCase Class
20     """
21     if config.running_vpp:
22         if os.path.isdir(config.socket_dir):
23             RunningVPP.socket_dir = config.socket_dir
24         else:
25             RunningVPP.socket_dir = RunningVPP.get_default_socket_dir()
26         RunningVPP.get_set_vpp_sock_files()
27         cls.get_stats_sock_path = RunningVPP.get_stats_sock_path
28         cls.get_api_sock_path = RunningVPP.get_api_sock_path
29         cls.get_memif_sock_path = RunningVPP.get_memif_sock_path
30         cls.run_vpp = RunningVPP.run_vpp
31         cls.quit_vpp = RunningVPP.quit_vpp
32         cls.vpp = RunningVPP
33         cls.running_vpp = True
34     return cls
35
36
37 class RunningVPP:
38     api_sock = ""  # api_sock file path
39     stats_sock = ""  # stats sock_file path
40     memif_sock = ""  # memif sock path
41     socket_dir = ""  # running VPP's socket directory
42     pid = None  # running VPP's pid
43     returncode = None  # indicates to the framework that VPP is running
44
45     @classmethod
46     def get_stats_sock_path(cls):
47         return cls.stats_sock
48
49     @classmethod
50     def get_api_sock_path(cls):
51         return cls.api_sock
52
53     @classmethod
54     def get_memif_sock_path(cls):
55         return cls.memif_sock
56
57     @classmethod
58     def run_vpp(cls):
59         """VPP is already running -- skip this action."""
60         pass
61
62     @classmethod
63     def quit_vpp(cls):
64         """Indicate quitting to framework by setting returncode=1."""
65         cls.returncode = 1
66
67     @classmethod
68     def terminate(cls):
69         """Indicate termination to framework by setting returncode=1."""
70         cls.returncode = 1
71
72     @classmethod
73     def get_default_socket_dir(cls):
74         """Return running VPP's default socket directory.
75
76         Default socket dir is:
77            /var/run/user/${UID}/vpp  (or)
78            /var/run/vpp, if VPP is started as a root user
79         """
80         if cls.is_running_vpp():
81             vpp_user_id = (
82                 subprocess.check_output(["ps", "-o", "uid=", "-p", str(cls.pid)])
83                 .decode("utf-8")
84                 .strip()
85             )
86             if vpp_user_id == "0":
87                 return "/var/run/vpp"
88             else:
89                 return f"/var/run/user/{vpp_user_id}/vpp"
90         else:
91             print(
92                 "Error: getting default socket dir, as "
93                 "a running VPP process could not be found"
94             )
95             sys.exit(1)
96
97     @classmethod
98     def get_set_vpp_sock_files(cls):
99         """Look for *.sock files in the socket_dir and set cls attributes.
100
101         Returns a tuple: (api_sock_file, stats_sock_file)
102         Sets cls.api_sock and cls.stats_sock attributes
103         """
104         # Return if the sock files are already set
105         if cls.api_sock and cls.stats_sock:
106             return (cls.api_sock, cls.stats_sock)
107         # Find running VPP's sock files in the socket dir
108         if os.path.isdir(cls.socket_dir):
109             if not cls.is_running_vpp():
110                 print(
111                     "Error: The socket dir for a running VPP directory is, "
112                     "set but a running VPP process could not be found"
113                 )
114                 sys.exit(1)
115             sock_files = glob(os.path.join(cls.socket_dir + "/" + "*.sock"))
116             for sock_file in sock_files:
117                 if "api.sock" in sock_file:
118                     cls.api_sock = os.path.abspath(sock_file)
119                 elif "stats.sock" in sock_file:
120                     cls.stats_sock = os.path.abspath(sock_file)
121                 elif "memif.sock" in sock_file:
122                     cls.memif_sock = os.path.abspath(sock_file)
123             if not cls.api_sock:
124                 print(
125                     f"Error: Could not find a valid api.sock file "
126                     f"in running VPP's socket directory {cls.socket_dir}"
127                 )
128                 sys.exit(1)
129             if not cls.stats_sock:
130                 print(
131                     f"Error: Could not find a valid stats.sock file "
132                     f"in running VPP's socket directory {cls.socket_dir}"
133                 )
134                 sys.exit(1)
135             return (cls.api_sock, cls.stats_sock)
136         else:
137             print("Error: The socket dir for a running VPP directory is unset")
138             sys.exit(1)
139
140     @classmethod
141     def is_running_vpp(cls):
142         """Return True if VPP's pid is visible else False."""
143         vpp_pid = subprocess.Popen(
144             ["pgrep", "-d,", "-x", "vpp_main"],
145             stdout=subprocess.PIPE,
146             stderr=subprocess.PIPE,
147             universal_newlines=True,
148         )
149         stdout, stderr = vpp_pid.communicate()
150         cls.pid = int(stdout.split(",")[0]) if stdout else None
151         return bool(cls.pid)
152
153     @classmethod
154     def poll(cls):
155         """Return None to indicate that the process hasn't terminated."""
156         return cls.returncode
157
158
159 if __name__ == "__main__":
160     RunningVPP.socket_dir = RunningVPP.get_default_socket_dir()
161     RunningVPP.get_set_vpp_sock_files()
162     print(f"Running VPP's sock files")
163     print(f"api_sock_file {RunningVPP.api_sock}")
164     print(f"stats_sock_file {RunningVPP.stats_sock}")