Consolidation of Genie parsing logic to Connection Broker - #835
Conversation
|
Thanks for the fix, @seojumper ! Can we also fix this without having to adjust our already generated test cases? |
|
Thanks for digging into this, Jeremy. I now reviewed the underlying issue, and I'd prefer a lighter fix that addresses the root cause directly rather than restructuring the parse flow into the broker. Root cause: Some genie parsers call Proposed fix: Patch Sketch: from functools import partial
# In _async_setup(), after broker connection is established:
if self.testbed_device:
self._patch_device_execute_for_broker()
def _patch_device_execute_for_broker(self):
"""Patch testbed_device.execute so Genie's internal calls route through the broker."""
broker_client = self.broker_client
hostname = self.hostname
loop = get_or_create_event_loop()
def broker_execute(cmd, *args, **kwargs):
"""Sync execute that routes through connection broker.
Must be called from a worker thread (via run_in_executor),
not from the event loop thread, to avoid deadlock.
"""
future = asyncio.run_coroutine_threadsafe(
broker_client.execute_command(hostname, cmd), loop
)
return future.result(timeout=120)
self.testbed_device.execute = broker_execute
async def parse_output(
self, command: str, output: str | None = None
) -> dict[str, Any] | None:
"""Parse command output using Genie parser.
Runs in a worker thread so Genie's supplementary device.execute()
calls can safely route through the broker without deadlocking.
"""
if not self.testbed_device:
return None
try:
loop = get_or_create_event_loop()
if output is not None:
result = await loop.run_in_executor(
None, partial(self.testbed_device.parse, command, output=output)
)
else:
result = await loop.run_in_executor(
None, partial(self.testbed_device.parse, command)
)
return dict(result) if result is not None else None
except Exception as e:
self.logger.warning(f"Genie parser failed for '{command}': {e}")
return NoneImpact on test templates — one keyword added: # Before:
parsed_output = self.parse_output(command, output=output)
# After:
parsed_output = await self.parse_output(command, output=output)Why I prefer this:
Happy to discuss further — the key insight is that Genie just needs a working can you please try out this approach, @seojumper ? |
|
I pushed the alternative approach to https://github.com/netascode/nac-test/compare/663-genie-device-execute-support, could you test this in your environment? I also added a test which reproduces the bug, so we can also catch regressions here in our pipeline. @aitestino @seojumper |
yeah i can try that out tonight/tomorrow, thanks. |
|
Hey @seojumper, thank you for the PR — the intent here is correct, and moving the Genie parsing logic into a more centralized path is definitely the right direction for solving #663. Per our internal conversation but recapping here for business continuity, it looks like Oliver (@oboehmer) has created his own branch ( Why Oliver's approach fits the codebase better:
We also discussed some concerns surrounding monkey patching annnnd I hear you, and I share the general instinct that monkey patching creates behavior that's harder to reason about. It's a legitimate tradeoff. In this specific case though, it's the only approach that transparently intercepts Genie's internal Oliver's patching is well-isolated to one method ( @oboehmer — Looking at the work in your branch, here are my findings/thoughts: Things that likely need adjustment:
Rest looks good to me. Once those items are addressed, I think this is ready to go. Would you mind opening a PR for the branch so we can track it properly? What do you both think? P.S. — This comment was drafted using voice-to-text via Claude Code. If the tone comes across as overly direct or terse, please know that's just how it tends to phrase things. No offense or criticism is intended — this is purely an objective technical review of the PR. Thanks for understanding! 🙂 |
|
Thanks for your comprehensive review of the situation and the two approaches, @aitestino ! I share the same concern about putting more logic into the broker (and we couldn't stop here if people would leverage other genie applications like To your concern about patching I will raise a PR soon, taking your comments into account. |
|
I raised PR #863 |
Description
PR consolidates genir parsing logic to ConnectionBroker with storage of parsed output retained in CommandCache.
Related Issue(s)
This is related to #663 where previously parsing of previously fetched command output that already existed in cache was not working correctly based on discovered logic in Genie Parsers.
Type of Change
Test Framework Affected
Network as Code (NaC) Architecture Affected
Platform Tested
Key Changes
Testing Done
pytest/pre-commit run -a)Test Commands Used
Checklist
pre-commit run -apasses)Additional Notes
This change is currently causing tests to directly call 'parse_output' instead of 'execute_command' > 'parse_output' which does impact visual of test results as raw command output is no longer present. Default genie api's do not return command output from a 'parse()' call. This could be changed with providing the 'return_raw=True' optional arg to the 'parse_output' fn.