Skip to content

IOS-XR: exit/endif after elseif/else in route-policy patches #495

@kzaikov

Description

@kzaikov

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)

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions