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