Skip to content

User-Extensible Remediation Plugin System #181

@jtdub

Description

@jtdub

Is your feature request related to a problem? Please describe.

While the driver-based remediation transform callbacks (see related issue) solve the problem for platform-specific transforms built into hier_config, there's currently no way for end users to extend remediation behavior without modifying the hier_config source code or forking drivers.

Users in specialized environments often need custom remediation logic that:

  • Is specific to their organization's network policies
  • Shouldn't be included in the core hier_config distribution
  • Needs to be maintained separately from the main codebase
  • Should be shareable as independent packages within their organization

For example:

  • A financial services company might require multi-step certificate rotation with specific validation steps
  • A service provider might need custom BGP graceful shutdown sequences for their specific topology
  • A university might have unique VLAN provisioning workflows tied to their identity management system

Currently, users must either:

  1. Fork and modify driver code (hard to maintain across upgrades)
  2. Write complex post-processing scripts (breaks the cohesive driver design)
  3. Request their custom logic be added to hier_config core (inappropriate for org-specific needs)

Describe the solution you'd like

Create a plugin framework that allows users to register custom remediation transforms without modifying hier_config source code.

Proposed Architecture:

  1. Plugin Base Class (plugins.py):
from abc import ABC, abstractmethod
from hier_config import HConfig
from hier_config.models import MatchRule
 
class RemediationPlugin(ABC):
    """Base class for user-defined remediation plugins."""
 
    @property
    @abstractmethod
    def name(self) -> str:
        """Unique identifier for this plugin."""
        pass
 
    @property
    @abstractmethod
    def description(self) -> str:
        """Human-readable description of what this plugin does."""
        pass
 
    @property
    def applies_to(self) -> tuple[MatchRule, ...]:
        """Optional: MatchRules to filter which remediation sections this plugin processes.
        If not specified, plugin receives the entire remediation tree."""
        return ()
 
    @property
    def priority(self) -> int:
        """Execution order (lower numbers run first). Default: 100."""
        return 100
 
    @abstractmethod
    def transform(self, remediation: HConfig) -> None:
        """Apply the transformation to the remediation config.
 
        Args:
            remediation: The remediation HConfig tree (mutable)
        """
        pass
  1. Plugin Registration (extend HConfigDriverBase):
class HConfigDriverBase(ABC):
    def __init__(self) -> None:
        self.rules = self._instantiate_rules()
        self._user_plugins: list[RemediationPlugin] = []
 
    def register_plugin(self, plugin: RemediationPlugin) -> None:
        """Register a user-defined remediation plugin."""
        self._user_plugins.append(plugin)
        # Sort by priority
        self._user_plugins.sort(key=lambda p: p.priority)
 
    def unregister_plugin(self, plugin_name: str) -> None:
        """Remove a plugin by name."""
        self._user_plugins = [p for p in self._user_plugins if p.name != plugin_name]
  1. Integration with WorkflowRemediation (workflows.py):
@property
def remediation_config(self) -> HConfig:
    if self._remediation_config:
        return self._remediation_config
 
    remediation_config = self.running_config.config_to_get_to(
        self.generated_config
    ).set_order_weight()
 
    # Apply driver-native remediation transforms
    for callback in remediation_config.driver.rules.remediation_transform_callbacks:
        callback(remediation_config)
 
    # Apply user-registered plugins (sorted by priority)
    for plugin in remediation_config.driver._user_plugins:
        if plugin.applies_to:
            # Plugin targets specific sections
            for child in remediation_config.get_children_deep(plugin.applies_to):
                plugin.transform(child)
        else:
            # Plugin processes entire remediation tree
            plugin.transform(remediation_config)
 
    self._remediation_config = remediation_config
    return self._remediation_config
  1. User Implementation Example:
# my_company/network_plugins.py
 
from hier_config.plugins import RemediationPlugin
from hier_config import HConfig
from hier_config.models import MatchRule
 
class CompanyBGPGracefulShutdown(RemediationPlugin):
    """Adds graceful shutdown sequence for BGP peer changes."""
 
    name = "company_bgp_graceful_shutdown"
    description = "Wraps BGP peer config changes with graceful shutdown"
    priority = 50  # Run before other transforms
 
    applies_to = (
        MatchRule(startswith="router bgp"),
        MatchRule(contains="neighbor"),
    )
 
    def transform(self, remediation: HConfig) -> None:
        """Add shutdown before peer changes, no shutdown after."""
        for bgp_section in remediation.get_children(startswith="router bgp"):
            has_peer_changes = any(
                "neighbor" in child.text
                for child in bgp_section.children
            )
 
            if has_peer_changes:
                # Add graceful shutdown at the start
                shutdown = bgp_section.add_child("  graceful-shutdown activate")
                shutdown.order_weight = -2000
 
                # Remove graceful shutdown at the end
                reactivate = bgp_section.add_child("  no graceful-shutdown activate")
                reactivate.order_weight = 2000
 
# Usage in user's code:
from hier_config import get_hconfig_driver, Platform
from my_company.network_plugins import CompanyBGPGracefulShutdown
 
driver = get_hconfig_driver(Platform.CISCO_IOS)
driver.register_plugin(CompanyBGPGracefulShutdown())
 
# Now all workflows using this driver will apply the plugin
workflow = WorkflowRemediation(running, generated)
remediation = workflow.remediation_config  # Plugin automatically applied

Benefits:

  • ✅ Users can extend ohne modifying hier_config source
  • ✅ Plugins can be packaged and distributed separately (pip install my-company-network-plugins)
  • ✅ Multiple plugins can be composed (priority-based execution order)
  • ✅ Plugins are reusable across multiple workflows
  • ✅ Full access to HConfig API
  • ✅ Type-safe with abstract base class
  • ✅ Platform-agnostic (plugins work with any driver)
  • ✅ Optional filtering via applies_to MatchRules
  • ✅ Clean separation between core and user-specific logic

Describe alternatives you've considered

  1. Driver Subclassing (Current Workaround)

    • Users subclass driver and override methods
    • Cons: Requires forking, hard to maintain, can't easily share
  2. Global Registry

    • Plugins register globally instead of per-driver
    • Pros: Simpler registration
    • Cons: Unexpected behavior across different drivers, harder to scope
  3. Declarative Plugin Configuration

    • Define plugins via YAML/JSON configuration files
    • Pros: No code required for simple transforms
    • Cons: Limited flexibility, harder to debug
  4. Monkey Patching (Anti-pattern)

    • Users monkey-patch WorkflowRemediation
    • Cons: Fragile, breaks across versions, impossible to support

The plugin system provides the right balance of flexibility, maintainability, and hier_config compatibility.

Additional context

Relationship to Remediation Transform Callbacks:

This plugin system complements (not replaces) the driver-native remediation transform callbacks:

Feature Driver Callbacks Plugin System
Who defines it? hier_config maintainers End users
Where does it live? In driver source code External packages
Scope Platform-specific, universal Org-specific, customized
Examples Safe ACL remediation Company-specific BGP workflows
Execution Always runs (part of driver) Opt-in (user registers)

Execution Flow:

remediation_config property called
  ↓
1. Compute diff (config_to_get_to)
  ↓
2. Set order weights
  ↓
3. Apply driver-native callbacks (post_load_callbacks, remediation_transform_callbacks)
  ↓
4. Apply user-registered plugins (sorted by priority)
  ↓
5. Cache and return

Real-World Plugin Examples:

  1. Certificate Rotation with Validation
class SecureCertRotationPlugin(RemediationPlugin):
    """Multi-step certificate rotation with validation."""
    def transform(self, remediation):
        # 1. Install new cert
        # 2. Verify new cert is valid
        # 3. Switch active cert
        # 4. Keep old cert as backup for N days
  1. Compliance Audit Trail
class ComplianceAuditPlugin(RemediationPlugin):
    """Injects audit comments into remediation."""
    def transform(self, remediation):
        for change in remediation.all_children():
            change.add_child(f"! Audit: {ticket_number} - {timestamp}")
  1. QoS Safe Transition
class QoSSafeTransitionPlugin(RemediationPlugin):
    """Ensures QoS policy changes don't drop traffic."""
    def transform(self, remediation):
        # Create new policy → apply to interface → delete old policy

Implementation Phases:

  • Phase 1: Core plugin infrastructure (base class, registration, execution)
  • Phase 2: Plugin discovery (automatic loading from installed packages)
  • Phase 3: Plugin configuration (enable/disable, configure parameters)
  • Phase 4: Plugin marketplace/gallery (community-contributed plugins)

Alignment with hier_config Design Philosophy:

  • Composable - Multiple plugins can be combined
  • Declarative - applies_to uses MatchRule system
  • Hierarchical - Operates on HConfig tree structure
  • Platform Agnostic - Plugins work across all drivers
  • Extensible - Open for extension, closed for modification

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions