Description
When generating patches for IOS-XR route-policy blocks that contain if/elseif/else structures, AsrFormatter.block_exit() adds spurious exit statements after elseif and else blocks, producing invalid patches.
Root Cause
In annet/vendors/tabparser.py, AsrFormatter.block_exit() only handles if...then (adds endif) and route-policy (adds end-policy). Lines starting with elseif or else fall through to super().block_exit() which
yields exit:
def block_exit(self, context: Optional[FormatterContext]) -> str:
current = context and context.row or ""
if current.startswith(("prefix-set", "as-path-set", "community-set")):
yield from block_wrapper("end-set")
elif current.startswith("if") and current.endswith("then"):
yield from block_wrapper("endif")
elif current.startswith("route-policy"):
yield from block_wrapper("end-policy")
else:
yield from super().block_exit(context) # yields "exit"
Actual Behavior
Patch output includes spurious exit and endif between branches:
route-policy TEST-IN
if destination in IPV4-DEFAULT or destination in IPV6-DEFAULT then
drop
endif <-- wrong
elseif destination in TEST-DENY then
drop
exit <-- wrong
elseif community in CUSTOMERS then
set local-preference 110
done
exit <-- wrong
else
set local-preference 110
exit <-- wrong
end-policy
Expected Behavior
elseif and else are branches of the parent if block. They should not produce any exit command — the endif from the if block covers the entire conditional:
route-policy TEST-IN
if destination in IPV4-DEFAULT or destination in IPV6-DEFAULT then
drop
elseif destination in TEST-DENY then
drop
elseif community in CUSTOMERS then
set local-preference 110
done
else
set local-preference 110
endif
end-policy
Note: annet diff produces correct output. Only annet patch is affected.
Suggested Fix
def _patched_block_exit(self, context):
from annet.vendors.tabparser import block_wrapper
current = context and context.row or ""
# if/elseif are part of a single conditional structure in route-policies.
# Only "else" (the last branch) should close the structure with "endif".
# if/elseif/else are branches of a single conditional in route-policies.
# We defer "endif" until the conditional structure is fully closed.
if current.startswith("if") and current.endswith("then"):
self._pending_endif = True
return
if current.startswith("elseif"):
# still inside the if-block, keep deferring
return
if current.startswith("else"):
# last branch — emit endif now
self._pending_endif = False
yield from block_wrapper("endif")
return
# If we were inside an if-block with no else (e.g. just if or if/elseif),
# emit the deferred endif before the parent block's exit.
if getattr(self, "_pending_endif", False):
self._pending_endif = False
yield from block_wrapper("endif")
yield from _original_block_exit(self, context)
Environment
- Platform: IOS-XR (NCS57 series)
Description
When generating patches for IOS-XR route-policy blocks that contain if/elseif/else structures, AsrFormatter.block_exit() adds spurious exit statements after elseif and else blocks, producing invalid patches.
Root Cause
In annet/vendors/tabparser.py, AsrFormatter.block_exit() only handles if...then (adds endif) and route-policy (adds end-policy). Lines starting with elseif or else fall through to super().block_exit() which
yields exit:
Actual Behavior
Patch output includes spurious exit and endif between branches:
Expected Behavior
elseif and else are branches of the parent if block. They should not produce any exit command — the endif from the if block covers the entire conditional:
Note: annet diff produces correct output. Only annet patch is affected.
Suggested Fix
Environment