fix: accept ArubaOS-S terminal prompts

Strip VT100 mode and control sequences emitted by WC firmware, add per-command timeout context, and allow longer interface and LLDP responses.
This commit is contained in:
2026-07-30 11:12:18 +02:00
parent 1b51784748
commit 8c268be778
+28 -25
View File
@@ -43,13 +43,17 @@ LLDP_CONTROL = {
}, },
} }
ANSI_ESCAPE = re.compile(r"\x1b(?:[@-Z\\-_]|\[[0-?]*[ -/]*[@-~])") ANSI_ESCAPE = re.compile(
r"\x1b(?:\][^\x07]*(?:\x07|\x1b\\)|\[[0-?]*[ -/]*[@-~]|[@-_]|[=>])"
)
TERMINAL_CONTROL = re.compile(r"[\x00-\x08\x0b\x0c\x0e-\x1f\x7f]")
MAC_PATTERN = re.compile(r"(?i)\b(?:[0-9a-f]{2}[:-]){5}[0-9a-f]{2}\b|\b[0-9a-f]{4}(?:-[0-9a-f]{4}){2}\b") MAC_PATTERN = re.compile(r"(?i)\b(?:[0-9a-f]{2}[:-]){5}[0-9a-f]{2}\b|\b[0-9a-f]{4}(?:-[0-9a-f]{4}){2}\b")
CLI_PROMPT = re.compile(r"(?:^|\n)[^\r\n]*[>#][ \t]*(?:\n)?\Z") CLI_PROMPT = re.compile(r"(?:^|\n)[^\r\n]*[>#][ \t]*(?:\n)?\Z")
def _clean_output(value): def _clean_output(value):
value = ANSI_ESCAPE.sub("", value).replace("\r", "") value = ANSI_ESCAPE.sub("", value).replace("\r", "")
value = TERMINAL_CONTROL.sub("", value)
value = re.sub(r"--\s*MORE\s*--|Press any key to continue.*", "", value, flags=re.I) value = re.sub(r"--\s*MORE\s*--|Press any key to continue.*", "", value, flags=re.I)
return value return value
@@ -74,6 +78,14 @@ def _read_available(channel, timeout=20):
return output return output
def _run_cli_command(channel, command, timeout=20):
channel.send(command + "\n")
try:
return _read_available(channel, timeout=timeout)
except TimeoutError as error:
raise TimeoutError(f"CLI-Befehl nicht vollständig abgeschlossen: {command}") from error
def run_switch_commands(config): def run_switch_commands(config):
platform = config["platform"] platform = config["platform"]
if platform not in COMMANDS: if platform not in COMMANDS:
@@ -93,49 +105,41 @@ def run_switch_commands(config):
banner_timeout=12, banner_timeout=12,
) )
channel = client.invoke_shell(width=240, height=1000) channel = client.invoke_shell(width=240, height=1000)
try:
_read_available(channel, timeout=30) _read_available(channel, timeout=30)
except TimeoutError as error:
raise TimeoutError("SSH-Anmeldung nicht mit einem vollständigen CLI-Prompt abgeschlossen") from error
pager_command, interface_command, neighbor_command = COMMANDS[platform] pager_command, interface_command, neighbor_command = COMMANDS[platform]
channel.send(pager_command + "\n") _run_cli_command(channel, pager_command)
_read_available(channel)
control = LLDP_CONTROL[platform] control = LLDP_CONTROL[platform]
channel.send(control["status"] + "\n") lldp_status = _run_cli_command(channel, control["status"])
lldp_status = _read_available(channel)
lldp_enabled_automatically = bool(re.search(control["disabled"], lldp_status, flags=re.I)) lldp_enabled_automatically = bool(re.search(control["disabled"], lldp_status, flags=re.I))
if lldp_enabled_automatically: if lldp_enabled_automatically:
enable_output = [] enable_output = []
for command in control["enable"]: for command in control["enable"]:
channel.send(command + "\n") enable_output.append(_run_cli_command(channel, command))
enable_output.append(_read_available(channel))
combined = "\n".join(enable_output) combined = "\n".join(enable_output)
if platform == "hpe_comware" and re.search(r"(?i)unrecognized|invalid|wrong parameter", combined): if platform == "hpe_comware" and re.search(r"(?i)unrecognized|invalid|wrong parameter", combined):
channel.send("system-view\n") _run_cli_command(channel, "system-view")
_read_available(channel) combined = _run_cli_command(channel, "lldp enable")
channel.send("lldp enable\n") combined += "\n" + _run_cli_command(channel, "return")
combined = _read_available(channel)
channel.send("return\n")
combined += "\n" + _read_available(channel)
if re.search(r"(?i)permission denied|authorization failed|access denied", combined): if re.search(r"(?i)permission denied|authorization failed|access denied", combined):
raise PermissionError("LLDP konnte mangels Konfigurationsrechten nicht aktiviert werden.") raise PermissionError("LLDP konnte mangels Konfigurationsrechten nicht aktiviert werden.")
if re.search(r"(?i)unrecognized|invalid input|unknown command|wrong parameter", combined): if re.search(r"(?i)unrecognized|invalid input|unknown command|wrong parameter", combined):
raise RuntimeError("Der LLDP-Aktivierungsbefehl wird von diesem Switch nicht unterstützt.") raise RuntimeError("Der LLDP-Aktivierungsbefehl wird von diesem Switch nicht unterstützt.")
time.sleep(2) time.sleep(2)
channel.send(interface_command + "\n") interfaces = _run_cli_command(channel, interface_command, timeout=45)
interfaces = _read_available(channel, timeout=30)
ip_outputs = [] ip_outputs = []
for command in IP_COMMANDS[platform]: for command in IP_COMMANDS[platform]:
channel.send(command + "\n") ip_outputs.append(_run_cli_command(channel, command, timeout=35))
ip_outputs.append(_read_available(channel, timeout=25)) neighbors = _run_cli_command(channel, neighbor_command, timeout=60)
channel.send(neighbor_command + "\n")
neighbors = _read_available(channel, timeout=35)
if platform == "aruba_cx" and re.search(r"(?i)invalid input|unknown command|unrecognized", neighbors): if platform == "aruba_cx" and re.search(r"(?i)invalid input|unknown command|unrecognized", neighbors):
channel.send("show lldp neighbor-info\n") neighbors = _run_cli_command(channel, "show lldp neighbor-info", timeout=60)
neighbors = _read_available(channel, timeout=35)
cdp_neighbors = "" cdp_neighbors = ""
if platform == "aruba_cx": if platform == "aruba_cx":
channel.send("show cdp neighbor-info\n") cdp_summary = _run_cli_command(channel, "show cdp neighbor-info", timeout=35)
cdp_summary = _read_available(channel, timeout=25)
cdp_ports = [] cdp_ports = []
for line in cdp_summary.splitlines(): for line in cdp_summary.splitlines():
match = re.match(r"^\s*(\d+/\d+/\d+)\s+\S+", line) match = re.match(r"^\s*(\d+/\d+/\d+)\s+\S+", line)
@@ -143,8 +147,7 @@ def run_switch_commands(config):
cdp_ports.append(match.group(1)) cdp_ports.append(match.group(1))
details = [] details = []
for port in cdp_ports: for port in cdp_ports:
channel.send(f"show cdp neighbor-info {port}\n") details.append(_run_cli_command(channel, f"show cdp neighbor-info {port}", timeout=30))
details.append(_read_available(channel, timeout=20))
cdp_neighbors = "\n".join([cdp_summary, *details]) cdp_neighbors = "\n".join([cdp_summary, *details])
return { return {
"interfaces": interfaces, "interfaces": interfaces,