Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Performance benchmarks for parsing, remediation, and iteration (#202).
Skipped by default; run with `poetry run pytest -m benchmark -v -s`.

- Added support for Huawei VRP with a new driver and test suite (#238).

### Fixed

- `DuplicateChildError` raised when parsing IOS-XR configs with indented `!` section
Expand Down
11 changes: 1 addition & 10 deletions hier_config/child.py
Original file line number Diff line number Diff line change
Expand Up @@ -128,16 +128,7 @@ def lines(self, *, sectional_exiting: bool = False) -> Iterable[str]:

@property
def sectional_exit(self) -> str | None:
for rule in self.driver.rules.sectional_exiting:
if self.is_lineage_match(rule.match_rules):
if exit_text := rule.exit_text:
return exit_text
return None

if not self.children:
return None

return "exit"
return self.driver.sectional_exit(self)

@property
def sectional_exit_text_parent_level(self) -> bool:
Expand Down
2 changes: 2 additions & 0 deletions hier_config/constructors.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
from .platforms.hp_comware5.driver import HConfigDriverHPComware5
from .platforms.hp_procurve.driver import HConfigDriverHPProcurve
from .platforms.hp_procurve.view import HConfigViewHPProcurve
from .platforms.huawei_vrp.driver import HConfigDriverHuaweiVrp
from .platforms.juniper_junos.driver import HConfigDriverJuniperJUNOS
from .platforms.view_base import HConfigViewBase
from .platforms.vyos.driver import HConfigDriverVYOS
Expand All @@ -40,6 +41,7 @@ def get_hconfig_driver(platform: Platform) -> HConfigDriverBase:
Platform.GENERIC: HConfigDriverGeneric,
Platform.HP_PROCURVE: HConfigDriverHPProcurve,
Platform.HP_COMWARE5: HConfigDriverHPComware5,
Platform.HUAWEI_VRP: HConfigDriverHuaweiVrp,
Platform.JUNIPER_JUNOS: HConfigDriverJuniperJUNOS,
Platform.VYOS: HConfigDriverVYOS,
}
Expand Down
1 change: 1 addition & 0 deletions hier_config/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,7 @@ class Platform(str, Enum):
GENERIC = auto() # used in cases where the specific platform is unimportant/unknown
HP_COMWARE5 = auto()
HP_PROCURVE = auto()
HUAWEI_VRP = auto()
JUNIPER_JUNOS = auto()
VYOS = auto()

Expand Down
10 changes: 10 additions & 0 deletions hier_config/platforms/driver_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,16 @@ def negate_with(self, config: HConfigChild) -> str | None:
return with_rule.use
return None

def sectional_exit(self, config: HConfigChild) -> str | None:
for exit_rule in self.rules.sectional_exiting:
if config.is_lineage_match(exit_rule.match_rules):
if exit_text := exit_rule.exit_text:
return exit_text
return None
if config.children:
return "exit"
return None

def swap_negation(self, child: HConfigChild) -> HConfigChild:
"""Swap negation of a `child.text`."""
if child.text.startswith(self.negation_prefix):
Expand Down
Empty file.
52 changes: 52 additions & 0 deletions hier_config/platforms/huawei_vrp/driver.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import re

from hier_config.child import HConfigChild
from hier_config.models import PerLineSubRule
from hier_config.platforms.driver_base import HConfigDriverBase, HConfigDriverRules


class HConfigDriverHuaweiVrp(HConfigDriverBase):
"""Driver for Huawei VRP operating system.
Platform enum: ``Platform.HUAWEI_VRP``.
"""

@property
def negation_prefix(self) -> str:
return "undo "

def swap_negation(self, child: HConfigChild) -> HConfigChild:
if child.text.startswith(self.negation_prefix):
child.text = child.text.removeprefix(self.negation_prefix)
return child

text = child.text
if text.startswith("description "):
text = "description"
elif text.startswith("alias "):
text = "alias"
elif " remark " in text or text.startswith("remark "):
text = re.sub(r"^(.*?remark) .*", r"\1", text)
elif text.startswith("snmp-agent community "):
text = re.sub(
r"^(snmp-agent community (?:read |write )?(?:cipher )?\S+).*",
r"\1",
text,
)

child.text = f"{self.negation_prefix}{text}"
return child

def sectional_exit(self, config: HConfigChild) -> str | None:
result = super().sectional_exit(config)
if result == "exit":
return "quit"
return result

@staticmethod
def _instantiate_rules() -> HConfigDriverRules:
return HConfigDriverRules(
per_line_sub=[
PerLineSubRule(search="^\\s*[#!].*", replace=""),
],
)
1 change: 1 addition & 0 deletions hier_config/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
"eos": Platform.ARISTA_EOS,
"junos": Platform.JUNIPER_JUNOS,
"vyos": Platform.VYOS,
"huawei_vrp": Platform.HUAWEI_VRP,
}


Expand Down
105 changes: 105 additions & 0 deletions tests/test_driver_huawei_vrp.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
from hier_config import get_hconfig_fast_load
from hier_config.constructors import get_hconfig
from hier_config.models import Platform


def test_merge_with_undo() -> None:
platform = Platform.HUAWEI_VRP
running_config = get_hconfig_fast_load(
platform, ("test_for_undo", "undo test_for_redo")
)
generated_config = get_hconfig_fast_load(
platform, ("undo test_for_undo", "test_for_redo")
)
remediation_config = running_config.config_to_get_to(generated_config)
assert remediation_config.dump_simple() == ("undo test_for_undo", "test_for_redo")


def test_negate_description() -> None:
platform = Platform.HUAWEI_VRP
running_config = get_hconfig_fast_load(
platform, ("interface GigabitEthernet0/0/0", " description some old blabla")
)
generated_config = get_hconfig_fast_load(
platform, ("interface GigabitEthernet0/0/0",)
)
remediation_config = running_config.config_to_get_to(generated_config)
assert remediation_config.dump_simple() == (
"interface GigabitEthernet0/0/0",
" undo description",
)


def test_negate_remark() -> None:
platform = Platform.HUAWEI_VRP
running_config = get_hconfig_fast_load(
platform, ("acl number 2000", " rule 5 remark some old remark")
)
generated_config = get_hconfig_fast_load(platform, ("acl number 2000",))
remediation_config = running_config.config_to_get_to(generated_config)
assert remediation_config.dump_simple() == (
"acl number 2000",
" undo rule 5 remark",
)


def test_negate_alias() -> None:
platform = Platform.HUAWEI_VRP
running_config = get_hconfig_fast_load(
platform, ("interface GigabitEthernet0/0/0", " alias some old alias")
)
generated_config = get_hconfig_fast_load(
platform, ("interface GigabitEthernet0/0/0",)
)
remediation_config = running_config.config_to_get_to(generated_config)
assert remediation_config.dump_simple() == (
"interface GigabitEthernet0/0/0",
" undo alias",
)


def test_negate_snmp_agent_community() -> None:
platform = Platform.HUAWEI_VRP
running_config = get_hconfig_fast_load(
platform, ("snmp-agent community read cipher %^%#blabla%^%# acl 2000",)
)
generated_config = get_hconfig(platform)
remediation_config = running_config.config_to_get_to(generated_config)
assert remediation_config.dump_simple() == (
"undo snmp-agent community read cipher %^%#blabla%^%#",
)


def test_comments_stripped() -> None:
platform = Platform.HUAWEI_VRP
config = get_hconfig_fast_load(
platform,
(
"#",
"! this is a comment",
"interface GigabitEthernet0/0/0",
" # another comment",
" description test",
"! yet another comment",
),
)
assert config.dump_simple() == (
"interface GigabitEthernet0/0/0",
" description test",
)


def test_sectional_exit_is_quit() -> None:
platform = Platform.HUAWEI_VRP
config = get_hconfig_fast_load(
platform,
(
"interface GigabitEthernet0/0/0",
" description test",
),
)
assert config.dump_simple(sectional_exiting=True) == (
"interface GigabitEthernet0/0/0",
" description test",
" quit",
)
Loading