|
11 | 11 | from typing import Union, Optional, Dict, Any, List |
12 | 12 | import os |
13 | 13 | import subprocess |
| 14 | +import sys |
| 15 | +import time |
14 | 16 | import textwrap |
15 | 17 | import uuid |
16 | 18 |
|
17 | 19 | # Import third party modules |
| 20 | +import pexpect # https://pexpect.readthedocs.io/en/stable/install.html |
18 | 21 | import psutil # Check for breaking changes https://github.com/giampaolo/psutil/blob/master/HISTORY.rst |
19 | 22 |
|
20 | | - |
21 | 23 | def get_pid_uptime(pid: int = 1) -> Optional[timedelta]: |
22 | 24 | """ |
23 | 25 | Get the uptime of a running process by its PID. |
@@ -222,6 +224,71 @@ def log_process_status( |
222 | 224 | log(ctx, status_message, log_level, structured_log_dict) |
223 | 225 |
|
224 | 226 |
|
| 227 | +def run_pexpect( |
| 228 | + ctx: Context, |
| 229 | + args: Union[str, List[str]], |
| 230 | + expect: Union[str, List[str]], |
| 231 | + response: str |
| 232 | + ) -> Dict: |
| 233 | + """ |
| 234 | + Run a command through the pexpect module |
| 235 | + Wait for expect to come from the command |
| 236 | + Then send the response into the command |
| 237 | + """ |
| 238 | + |
| 239 | + return_dict = {} |
| 240 | + |
| 241 | + # Convert args to a string |
| 242 | + if isinstance(args, List): |
| 243 | + args = " ".join(args) |
| 244 | + |
| 245 | + |
| 246 | + try: |
| 247 | + |
| 248 | + child = pexpect.spawn( |
| 249 | + command = args, |
| 250 | + encoding = 'utf-8', |
| 251 | + echo = False |
| 252 | + ) |
| 253 | + |
| 254 | + return_dict["pid"] = child.pid |
| 255 | + |
| 256 | + log(ctx, f"child.pid: {child.pid}") |
| 257 | + |
| 258 | + child.logfile_read = sys.stdout.buffer |
| 259 | + |
| 260 | + match = child.expect_exact( |
| 261 | + pattern = expect, |
| 262 | + timeout = 120 |
| 263 | + ) |
| 264 | + |
| 265 | + if match == 0: |
| 266 | + child.sendline(response) |
| 267 | + log(ctx, "match, sending response") |
| 268 | + else: |
| 269 | + log(ctx, "no match") |
| 270 | + |
| 271 | + |
| 272 | + time.sleep(1) |
| 273 | + |
| 274 | + child.close(force=False) |
| 275 | + |
| 276 | + return_dict["output"] = child.before + child.after |
| 277 | + return_dict["return_code"] = child.exitstatus |
| 278 | + return_dict["signal_status"] = child.signalstatus |
| 279 | + |
| 280 | + except pexpect.EOF: |
| 281 | + log(ctx, f"pexpect child exited: {str(child)}") |
| 282 | + |
| 283 | + except pexpect.TIMEOUT: |
| 284 | + log(ctx, f"pexpect hit a timeout: {str(child)}", "error") |
| 285 | + |
| 286 | + except: |
| 287 | + log(ctx, f"pexpect child threw an exception: {str(child)}", "error") |
| 288 | + |
| 289 | + return return_dict |
| 290 | + |
| 291 | + |
225 | 292 | def run_subprocess( |
226 | 293 | ctx: Context, |
227 | 294 | args: Union[str, List[str]], |
|
0 commit comments