blob: f31a1327b4a0fec0c6a2a11a1fac12cf25fffb0b [file] [log] [blame]
Naveen Joyc872cec2022-08-30 13:59:03 -07001#!/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
8from glob import glob
9import os
10import sys
11import subprocess
12from config import config
13
14
15def 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.run_vpp = RunningVPP.run_vpp
30 cls.quit_vpp = RunningVPP.quit_vpp
31 cls.vpp = RunningVPP
32 cls.running_vpp = True
33 return cls
34
35
36class RunningVPP:
Naveen Joyc872cec2022-08-30 13:59:03 -070037 api_sock = "" # api_sock file path
38 stats_sock = "" # stats sock_file path
39 socket_dir = "" # running VPP's socket directory
40 pid = None # running VPP's pid
41 returncode = None # indicates to the framework that VPP is running
42
43 @classmethod
44 def get_stats_sock_path(cls):
45 return cls.stats_sock
46
47 @classmethod
48 def get_api_sock_path(cls):
49 return cls.api_sock
50
51 @classmethod
52 def run_vpp(cls):
53 """VPP is already running -- skip this action."""
54 pass
55
56 @classmethod
57 def quit_vpp(cls):
58 """Indicate quitting to framework by setting returncode=1."""
59 cls.returncode = 1
60
61 @classmethod
62 def terminate(cls):
63 """Indicate termination to framework by setting returncode=1."""
64 cls.returncode = 1
65
66 @classmethod
67 def get_default_socket_dir(cls):
68 """Return running VPP's default socket directory.
69
70 Default socket dir is:
71 /var/run/user/${UID}/vpp (or)
72 /var/run/vpp, if VPP is started as a root user
73 """
74 if cls.is_running_vpp():
75 vpp_user_id = (
76 subprocess.check_output(["ps", "-o", "uid=", "-p", str(cls.pid)])
77 .decode("utf-8")
78 .strip()
79 )
80 if vpp_user_id == "0":
81 return "/var/run/vpp"
82 else:
83 return f"/var/run/user/{vpp_user_id}/vpp"
84 else:
85 print(
86 "Error: getting default socket dir, as "
87 "a running VPP process could not be found"
88 )
89 sys.exit(1)
90
91 @classmethod
92 def get_set_vpp_sock_files(cls):
93 """Look for *.sock files in the socket_dir and set cls attributes.
94
95 Returns a tuple: (api_sock_file, stats_sock_file)
96 Sets cls.api_sock and cls.stats_sock attributes
97 """
98 # Return if the sock files are already set
99 if cls.api_sock and cls.stats_sock:
100 return (cls.api_sock, cls.stats_sock)
101 # Find running VPP's sock files in the socket dir
102 if os.path.isdir(cls.socket_dir):
103 if not cls.is_running_vpp():
104 print(
105 "Error: The socket dir for a running VPP directory is, "
106 "set but a running VPP process could not be found"
107 )
108 sys.exit(1)
109 sock_files = glob(os.path.join(cls.socket_dir + "/" + "*.sock"))
110 for sock_file in sock_files:
111 if "api.sock" in sock_file:
112 cls.api_sock = os.path.abspath(sock_file)
113 elif "stats.sock" in sock_file:
114 cls.stats_sock = os.path.abspath(sock_file)
115 if not cls.api_sock:
116 print(
117 f"Error: Could not find a valid api.sock file "
118 f"in running VPP's socket directory {cls.socket_dir}"
119 )
120 sys.exit(1)
121 if not cls.stats_sock:
122 print(
123 f"Error: Could not find a valid stats.sock file "
124 f"in running VPP's socket directory {cls.socket_dir}"
125 )
126 sys.exit(1)
127 return (cls.api_sock, cls.stats_sock)
128 else:
129 print("Error: The socket dir for a running VPP directory is unset")
130 sys.exit(1)
131
132 @classmethod
133 def is_running_vpp(cls):
134 """Return True if VPP's pid is visible else False."""
135 vpp_pid = subprocess.Popen(
136 ["pgrep", "-d,", "-x", "vpp_main"],
137 stdout=subprocess.PIPE,
138 stderr=subprocess.PIPE,
139 universal_newlines=True,
140 )
141 stdout, stderr = vpp_pid.communicate()
142 cls.pid = int(stdout.split(",")[0]) if stdout else None
143 return bool(cls.pid)
144
145 @classmethod
146 def poll(cls):
147 """Return None to indicate that the process hasn't terminated."""
148 return cls.returncode
149
150
151if __name__ == "__main__":
152 RunningVPP.socket_dir = RunningVPP.get_default_socket_dir()
153 RunningVPP.get_set_vpp_sock_files()
154 print(f"Running VPP's sock files")
155 print(f"api_sock_file {RunningVPP.api_sock}")
156 print(f"stats_sock_file {RunningVPP.stats_sock}")