Fix serial login detection when IOM is already logged in
Adds _at_shell_prompt() helper that detects a shell prompt by checking whether any line ends with '#' or '# ', rather than using a bare '#' in response check guarded by 'login' not in output. The old guard caused a false negative when the IOM echoed 'Last login: ...' text alongside the prompt, sending an unnecessary login attempt to an active session. Also broadens _ANSI_RE to catch all CSI escape sequences and two-character ESC sequences, not just the original five terminal codes. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -26,8 +26,24 @@ from ui import (
|
||||
prompt, prompt_ip, prompt_yn, prompt_password,
|
||||
)
|
||||
|
||||
# Strip ANSI escape sequences from serial terminal output
|
||||
_ANSI_RE = re.compile(r'\x1b\[[0-9;]*[mGKHF]')
|
||||
# Strip ANSI escape sequences from serial terminal output.
|
||||
# Catches CSI sequences (ESC[...X) and other two-character ESC sequences.
|
||||
_ANSI_RE = re.compile(r'\x1b(?:\[[0-9;]*[A-Za-z]|[^[])')
|
||||
|
||||
|
||||
def _at_shell_prompt(text: str) -> bool:
|
||||
"""
|
||||
Return True if any line in text looks like a root shell prompt.
|
||||
Checks whether a non-empty line ends with '#' or '# ' — the pattern
|
||||
produced by prompts like 'root@hostname:~#' — without being tripped
|
||||
up by the word 'login' appearing elsewhere in the output (e.g. in
|
||||
'Last login: ...' messages shown after a session is resumed).
|
||||
"""
|
||||
for line in text.splitlines():
|
||||
stripped = line.rstrip()
|
||||
if stripped.endswith("#") or stripped.endswith("# "):
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
# ── Serial Redfish transport ───────────────────────────────────────────────────
|
||||
@@ -35,7 +51,8 @@ def _login_serial_console(ser: SerialPort, password: str) -> bool:
|
||||
"""
|
||||
Perform the root login sequence on the IOM serial console.
|
||||
Handles both the case where a login prompt is showing and where
|
||||
a shell session is already active.
|
||||
a shell session is already active (i.e. was never logged out after
|
||||
a prior run — the IOM stays logged in until power-cycled).
|
||||
Returns True if a shell prompt is reached, False on failure.
|
||||
"""
|
||||
info("Logging in to IOM serial console...")
|
||||
@@ -43,13 +60,16 @@ def _login_serial_console(ser: SerialPort, password: str) -> bool:
|
||||
# Send a newline to get the current state of the console
|
||||
ser.send_line("", delay=0.5)
|
||||
response = _ANSI_RE.sub("", ser.read_until_quiet(quiet_period=0.5))
|
||||
low = response.lower()
|
||||
|
||||
# Already at a shell prompt — no login needed
|
||||
if ("#" in response or "$" in response) and "login" not in low:
|
||||
# Already at a shell prompt — no login needed.
|
||||
# Use _at_shell_prompt() rather than a bare '#' check so that
|
||||
# 'Last login: ...' text in the response does not cause a false negative.
|
||||
if _at_shell_prompt(response):
|
||||
ok("Already at shell prompt.")
|
||||
return True
|
||||
|
||||
low = response.lower()
|
||||
|
||||
# Send username if we see a login prompt (or an empty/unknown response)
|
||||
if "login" in low or not response.strip():
|
||||
ser.send_line("root", delay=0.5)
|
||||
@@ -60,7 +80,7 @@ def _login_serial_console(ser: SerialPort, password: str) -> bool:
|
||||
ser.send_line(password, delay=0.5)
|
||||
response = _ANSI_RE.sub("", ser.read_until_quiet(quiet_period=2.0))
|
||||
|
||||
if "#" in response or "$" in response:
|
||||
if _at_shell_prompt(response):
|
||||
ok("Logged in to IOM console.")
|
||||
return True
|
||||
|
||||
|
||||
Reference in New Issue
Block a user