-
Notifications
You must be signed in to change notification settings - Fork 31
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
refactor(anta.tests): Refactor VerifyISISSegmentRoutingTunnels test case #1037
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
|
@@ -5,7 +5,7 @@ | |||||
|
||||||
from __future__ import annotations | ||||||
|
||||||
from ipaddress import IPv4Address | ||||||
from ipaddress import IPv4Address, IPv4Network | ||||||
from typing import Any, Literal | ||||||
from warnings import warn | ||||||
|
||||||
|
@@ -122,3 +122,63 @@ def __init__(self, **data: Any) -> None: # noqa: ANN401 | |||||
stacklevel=2, | ||||||
) | ||||||
super().__init__(**data) | ||||||
|
||||||
|
||||||
class SRTunnelEntry(BaseModel): | ||||||
"""Model for a IS-IS SR tunnel.""" | ||||||
|
||||||
model_config = ConfigDict(extra="forbid") | ||||||
endpoint: IPv4Network | ||||||
"""Endpoint of the tunnel.""" | ||||||
vias: list[TunnelPath] | None = None | ||||||
"""Optional list of path to reach endpoint.""" | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
|
||||||
def __str__(self) -> str: | ||||||
"""Return a human-readable string representation of the SRTunnelEntry for reporting.""" | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
return f"Endpoint: {self.endpoint}" | ||||||
|
||||||
|
||||||
class TunnelPath(BaseModel): | ||||||
"""Model for a IS-IS tunnel path.""" | ||||||
|
||||||
model_config = ConfigDict(extra="forbid") | ||||||
nexthop: IPv4Address | None = None | ||||||
"""Nexthop of the tunnel.""" | ||||||
type: Literal["ip", "tunnel"] | None = None | ||||||
"""Type of the tunnel.""" | ||||||
interface: Interface | None = None | ||||||
"""Interface of the tunnel.""" | ||||||
tunnel_id: Literal["TI-LFA", "ti-lfa", "unset"] | None = None | ||||||
"""Computation method of the tunnel.""" | ||||||
|
||||||
def __str__(self) -> str: | ||||||
"""Return a human-readable string representation of the TunnelPath for reporting.""" | ||||||
base_string = "" | ||||||
if self.nexthop: | ||||||
base_string += f" Next-hop: {self.nexthop}" | ||||||
if self.type: | ||||||
base_string += f" Type: {self.type}" | ||||||
if self.interface: | ||||||
base_string += f" Interface: {self.interface}" | ||||||
if self.tunnel_id: | ||||||
base_string += f" TunnelID: {self.tunnel_id}" | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
|
||||||
return base_string.lstrip() | ||||||
|
||||||
|
||||||
class Entry(SRTunnelEntry): # pragma: no cover | ||||||
"""Alias for the SRTunnelEntry model to maintain backward compatibility. | ||||||
|
||||||
When initialized, it will emit a deprecation warning and call the SRTunnelEntry model. | ||||||
|
||||||
TODO: Remove this class in ANTA v2.0.0. | ||||||
""" | ||||||
|
||||||
def __init__(self, **data: Any) -> None: # noqa: ANN401 | ||||||
"""Initialize the Entry class, emitting a deprecation warning.""" | ||||||
warn( | ||||||
message="Entry model is deprecated and will be removed in ANTA v2.0.0. Use the SRTunnelEntry model instead.", | ||||||
category=DeprecationWarning, | ||||||
stacklevel=2, | ||||||
) | ||||||
super().__init__(**data) |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -7,13 +7,11 @@ | |
# mypy: disable-error-code=attr-defined | ||
from __future__ import annotations | ||
|
||
from ipaddress import IPv4Address, IPv4Network | ||
from typing import Any, ClassVar, Literal | ||
from typing import Any, ClassVar | ||
|
||
from pydantic import BaseModel, field_validator | ||
from pydantic import field_validator | ||
|
||
from anta.custom_types import Interface | ||
from anta.input_models.routing.isis import InterfaceCount, InterfaceState, ISISInstance, IsisInstance, ISISInterface | ||
from anta.input_models.routing.isis import Entry, InterfaceCount, InterfaceState, ISISInstance, IsisInstance, ISISInterface, SRTunnelEntry, TunnelPath | ||
from anta.models import AntaCommand, AntaTemplate, AntaTest | ||
from anta.tools import get_item, get_value | ||
|
||
|
@@ -391,32 +389,13 @@ class VerifyISISSegmentRoutingTunnels(AntaTest): | |
class Input(AntaTest.Input): | ||
"""Input model for the VerifyISISSegmentRoutingTunnels test.""" | ||
|
||
entries: list[Entry] | ||
entries: list[SRTunnelEntry] | ||
"""List of tunnels to check on device.""" | ||
Entry: ClassVar[type[Entry]] = Entry | ||
|
||
class Entry(BaseModel): | ||
"""Definition of a tunnel entry.""" | ||
|
||
endpoint: IPv4Network | ||
"""Endpoint IP of the tunnel.""" | ||
vias: list[Vias] | None = None | ||
"""Optional list of path to reach endpoint.""" | ||
|
||
class Vias(BaseModel): | ||
"""Definition of a tunnel path.""" | ||
|
||
nexthop: IPv4Address | None = None | ||
"""Nexthop of the tunnel. If None, then it is not tested. Default: None""" | ||
type: Literal["ip", "tunnel"] | None = None | ||
"""Type of the tunnel. If None, then it is not tested. Default: None""" | ||
interface: Interface | None = None | ||
"""Interface of the tunnel. If None, then it is not tested. Default: None""" | ||
tunnel_id: Literal["TI-LFA", "ti-lfa", "unset"] | None = None | ||
"""Computation method of the tunnel. If None, then it is not tested. Default: None""" | ||
|
||
def _eos_entry_lookup(self, search_value: IPv4Network, entries: dict[str, Any], search_key: str = "endpoint") -> dict[str, Any] | None: | ||
def _eos_entry_lookup(self, search_value: str, entries: dict[str, Any], search_key: str = "endpoint") -> dict[str, Any] | None: | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do we need that function? Can we use |
||
return next( | ||
(entry_value for entry_id, entry_value in entries.items() if str(entry_value[search_key]) == str(search_value)), | ||
(entry_value for entry_id, entry_value in entries.items() if str(entry_value[search_key]) == search_value), | ||
None, | ||
) | ||
|
||
|
@@ -431,25 +410,26 @@ def test(self) -> None: | |
self.result.is_success() | ||
|
||
if len(command_output["entries"]) == 0: | ||
self.result.is_skipped("IS-IS-SR is not running on device.") | ||
self.result.is_skipped("IS-IS-SR not configured") | ||
return | ||
|
||
for input_entry in self.inputs.entries: | ||
eos_entry = self._eos_entry_lookup(search_value=input_entry.endpoint, entries=command_output["entries"]) | ||
eos_entry = self._eos_entry_lookup(search_value=str(input_entry.endpoint), entries=command_output["entries"]) | ||
if eos_entry is None: | ||
self.result.is_failure(f"Tunnel to {input_entry.endpoint!s} is not found.") | ||
self.result.is_failure(f"{input_entry} - Tunnel not found") | ||
|
||
elif input_entry.vias is not None: | ||
for via_input in input_entry.vias: | ||
via_search_result = any(self._via_matches(via_input, eos_via) for eos_via in eos_entry["vias"]) | ||
if not via_search_result: | ||
self.result.is_failure(f"Tunnel to {input_entry.endpoint!s} is incorrect.") | ||
self.result.is_failure(f"{input_entry} {via_input} - Tunnel is incorrect") | ||
|
||
def _via_matches(self, via_input: VerifyISISSegmentRoutingTunnels.Input.Entry.Vias, eos_via: dict[str, Any]) -> bool: | ||
def _via_matches(self, via_input: TunnelPath, eos_via: dict[str, Any]) -> bool: | ||
"""Check if the via input matches the eos via. | ||
|
||
Parameters | ||
---------- | ||
via_input : VerifyISISSegmentRoutingTunnels.Input.Entry.Vias | ||
via_input : TunnelPath | ||
The input via to check. | ||
eos_via : dict[str, Any] | ||
The EOS via to compare against. | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -1812,7 +1812,7 @@ | |
}, | ||
"expected": { | ||
"result": "skipped", | ||
"messages": ["IS-IS-SR is not running on device."], | ||
"messages": ["IS-IS-SR not configured"], | ||
}, | ||
}, | ||
{ | ||
|
@@ -1841,7 +1841,7 @@ | |
}, | ||
"expected": { | ||
"result": "failure", | ||
"messages": ["Tunnel to 1.0.0.122/32 is not found."], | ||
"messages": ["Endpoint: 1.0.0.122/32 - Tunnel not found"], | ||
}, | ||
}, | ||
{ | ||
|
@@ -1922,7 +1922,7 @@ | |
}, | ||
"expected": { | ||
"result": "failure", | ||
"messages": ["Tunnel to 1.0.0.13/32 is incorrect."], | ||
"messages": ["Endpoint: 1.0.0.13/32 Type: tunnel - Tunnel is incorrect"], | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What is incorrect here? |
||
}, | ||
}, | ||
{ | ||
|
@@ -2010,7 +2010,7 @@ | |
}, | ||
"expected": { | ||
"result": "failure", | ||
"messages": ["Tunnel to 1.0.0.122/32 is incorrect."], | ||
"messages": ["Endpoint: 1.0.0.122/32 Next-hop: 10.0.1.2 Type: ip Interface: Ethernet1 - Tunnel is incorrect"], | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same thing here. The user doesn't know what is incorrect about the tunnel. |
||
}, | ||
}, | ||
{ | ||
|
@@ -2098,7 +2098,7 @@ | |
}, | ||
"expected": { | ||
"result": "failure", | ||
"messages": ["Tunnel to 1.0.0.122/32 is incorrect."], | ||
"messages": ["Endpoint: 1.0.0.122/32 Next-hop: 10.0.1.1 Type: ip Interface: Ethernet4 - Tunnel is incorrect"], | ||
}, | ||
}, | ||
{ | ||
|
@@ -2186,7 +2186,7 @@ | |
}, | ||
"expected": { | ||
"result": "failure", | ||
"messages": ["Tunnel to 1.0.0.122/32 is incorrect."], | ||
"messages": ["Endpoint: 1.0.0.122/32 Next-hop: 10.0.1.2 Type: ip Interface: Ethernet1 - Tunnel is incorrect"], | ||
}, | ||
}, | ||
{ | ||
|
@@ -2251,7 +2251,7 @@ | |
"vias": [ | ||
{ | ||
"type": "tunnel", | ||
"tunnelId": {"type": "TI-LFA", "index": 4}, | ||
"tunnelId": {"type": "unset", "index": 4}, | ||
"labels": ["3"], | ||
} | ||
], | ||
|
@@ -2266,14 +2266,14 @@ | |
{ | ||
"endpoint": "1.0.0.111/32", | ||
"vias": [ | ||
{"type": "tunnel", "tunnel_id": "unset"}, | ||
{"type": "tunnel", "tunnel_id": "ti-lfa"}, | ||
], | ||
}, | ||
] | ||
}, | ||
"expected": { | ||
"result": "failure", | ||
"messages": ["Tunnel to 1.0.0.111/32 is incorrect."], | ||
"messages": ["Endpoint: 1.0.0.111/32 Type: tunnel TunnelID: ti-lfa - Tunnel is incorrect"], | ||
}, | ||
}, | ||
{ | ||
|
@@ -2294,7 +2294,7 @@ | |
}, | ||
"expected": { | ||
"result": "skipped", | ||
"messages": ["IS-IS-SR is not running on device."], | ||
"messages": ["IS-IS-SR not configured"], | ||
}, | ||
}, | ||
] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We are already using
Segment
+TunnelPath
so I thinkTunnel
makes sense.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You also need to add a ClassVar for the deprecated
Vias
model for backward compatibility.