-
Notifications
You must be signed in to change notification settings - Fork 29
Description
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:
- Fork and modify driver code (hard to maintain across upgrades)
- Write complex post-processing scripts (breaks the cohesive driver design)
- 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:
- 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- 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]- 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- 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 appliedBenefits:
- ✅ 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_toMatchRules - ✅ Clean separation between core and user-specific logic
Describe alternatives you've considered
-
Driver Subclassing (Current Workaround)
- Users subclass driver and override methods
- Cons: Requires forking, hard to maintain, can't easily share
-
Global Registry
- Plugins register globally instead of per-driver
- Pros: Simpler registration
- Cons: Unexpected behavior across different drivers, harder to scope
-
Declarative Plugin Configuration
- Define plugins via YAML/JSON configuration files
- Pros: No code required for simple transforms
- Cons: Limited flexibility, harder to debug
-
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:
- 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- 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}")- 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 policyImplementation 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_touses MatchRule system - Hierarchical - Operates on HConfig tree structure
- Platform Agnostic - Plugins work across all drivers
- Extensible - Open for extension, closed for modification