Repository navigation
Conversation
There was a problem hiding this comment.
Copilot review overview
🟡 Changes recommended
Prompt matching has several paths that miss standard login flows or produce false positives.
Review effort: Balanced
Findings: 4
Open (4)
What changed in this PR
Adds Serial over LAN login-screen recognition to prevent OS probes from being sent to interactive authentication prompts.
Changes:
- Adds login-prompt patterns and detection paths.
- Introduces a dedicated compatibility exception.
- Expands unit coverage for prompt detection and retries.
| File | Description |
|---|---|
mfd_connect/sol.py |
Implements prompt detection in SOL command and OS-discovery flows. |
mfd_connect/exceptions.py |
Adds SolLoginPromptDetected. |
tests/unit/test_mfd_connect/test_sol.py |
Tests prompt recognition and command retry behavior. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
74677da to
c714f5c
Compare
c714f5c to
8f668af
Compare
| """Raises when OS is not supported.""" | ||
|
|
||
|
|
||
| class SolLoginPromptDetected(OsNotSupported): |
There was a problem hiding this comment.
The Serial-over-LAN (SoL) is only a connection, by which the users gets to the text console of the SUT.
I would name this exception TextConsoleLoginPromptDetected because for graphic-mode log-in there is no output on SoL whatsoever...
| expected_return_codes: Optional[Iterable] = frozenset({0}), | ||
| shell: bool = True, | ||
| custom_exception: Type[CalledProcessError] = None, | ||
| detect_login: bool = False, |
There was a problem hiding this comment.
I am not sure if enhancing SolConnection.execute_command(...) to do two distinctively different things is a good idea. After all this method handles sending a text command over SoL, and returns the response without any interpretation.
In a test-framework you do need method like this. After all you never know, if the output would include strings like "log-in" or "password" by pure coincidence.
I would create a thin-wrapper for SolConnection.execute_command(...) method that would use the execute_command internally, but it would also raise exception if the output was anything but SUCCESS. A detection of the log-in prompt screen would be one of these possible exceptions.
E.g. SolConnection.execute_command_expect_success(...)
8f668af to
15b3644
Compare
7cc65b0 to
e38ba90
Compare
e38ba90 to
133c42d
Compare
133c42d to
2a704a7
Compare
| :raises ConnectionCalledProcessError: if program exits with an unexpected return code (shell=True only) | ||
| :raises TextConsoleLoginPromptDetected: if an interactive login prompt is detected in the response | ||
| """ | ||
| if discard_stdout: |
There was a problem hiding this comment.
The user may not be interested in the output of the command, despite the fact that the method processes it internally.
In this case, simply discard it instead of returning. There is no need for raising an exception.
There was a problem hiding this comment.
Without the output there is nothing to check the login prompt against - discard_stdout=True would make this method silently skip its whole purpose (login-prompt detection) without telling the caller. Raising an exception here prevents a false positive.
| raise custom_exception(returncode=returncode, cmd=command, output=output) | ||
| raise ConnectionCalledProcessError(returncode=returncode, cmd=command, output=output) | ||
|
|
||
| def execute_command_expect_success( |
There was a problem hiding this comment.
The name suggest that this method runs a command and expects it to be completed successfully. This is not the case, as SolConnection.execute_command(...) is also used for sending commands that are expected to fail.
I would rename it to SolConnection.execute_command_detect_login(...)
One more thing:
The reason for the execute_command(...) method to maintain the same interface (same parameters) despite being a method of various classes (e.g. SolConnection, SSHConnection or RShellConnection among others) is to allow the user to seamlessly replace the connection type (class) without the need for changing a single method call.
If you introduce SolConnecition.execute_command_detect_login(...), you break this symmetry anyway, as no other class has a counterpart for it, and there is no abstract method in Connection class either.
To fix this you would need to add an abstract method to the Connection base class to signal the need for implementing it for other types of connection as well.
There was a problem hiding this comment.
We could make this a private method so that we don't have to implement an abstract method in Connection/base.py
adrianlasota
left a comment
There was a problem hiding this comment.
The best should be adding support for login to that system. SerialConnection handles that, so it can be added here too.
| custom_exception: Type[CalledProcessError] = None, | ||
| ) -> "ConnectionCompletedProcess": | ||
| """ | ||
| Run program and wait for its completion, raising if the outcome is not a SUCCESS. |
There was a problem hiding this comment.
I don't get that method. before each execute command there is _raise_if_login_prompt means it won't go through the login prompt. What's the case where you are checking login again?
There was a problem hiding this comment.
_raise_if_login_prompt() only reads what is already in the buffer - it doesn't send anything. If the login prompt is not printed again, the buffer can be empty and the check passes, even if the prompt is there.
To detect it for sure, we need to send a command and check the response. That's why execute_command_detect_login() checks the output after sending the command - it's the only way to reliably catch the prompt.
We could make this a private method so that we don't have to implement an abstract method in Connection/base.py, as Tomasz suggested above.
Signed-off-by: Berk, Dawid <dawidx.berk@intel.com>
2a704a7 to
d128525
Compare
| raise custom_exception(returncode=returncode, cmd=command, output=output) | ||
| raise ConnectionCalledProcessError(returncode=returncode, cmd=command, output=output) | ||
|
|
||
| def execute_command_detect_login( |
There was a problem hiding this comment.
I suggest to do it always on the start of connection. I don't see the purpose to always "detect" login. If you need to do it should be done in your logic.
I don't see response for login feature request.
The flow should be:
connect, check if need to login, login or raise exception (your case?), execute command.
but it should be done in logic, here you should give just implementation of each part

Add Support Login Screen Recognition Over Serial over LAN