Skip to content
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

Add tolerance percentage to validate #1813

Merged
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
4 changes: 3 additions & 1 deletion docs/validate/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,9 @@ A few notes:
* We can also use comparison on the conditions of numerical validate. For example, if you want
to validate there that the ``cpu``and ``memory`` into ``get_environment`` are ``15%`` or less.
We can use writing comparison operators such as ``<15.0`` or ``>10.0`` in this case, or range
with the operator syntax of ``<->`` such as ``10.0<->20.0`` or ``10<->20``.
with the operator syntax of ``<->`` such as ``10.0<->20.0`` or ``10<->20``. In a similar vain
a percentage tolerance can be validated upon, for example ``10%20`` allows a 10% tolerance
either side of 20 (a range of 18 to 22).
* Some methods require extra arguments, for example ``ping``. You can pass arguments to those
methods using the magic keyword ``_kwargs``. In addition, an optional keyword ``_name`` can
be specified to override the name in the report. Useful for having a more descriptive report
Expand Down
21 changes: 21 additions & 0 deletions napalm/base/validate.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import yaml
import copy
import re
from math import isclose
from typing import Dict, List, Union, TypeVar, Optional, TYPE_CHECKING

if TYPE_CHECKING:
Expand All @@ -16,6 +17,7 @@

# We put it here to compile it only once
numeric_compare_regex = re.compile(r"^(<|>|<=|>=|==|!=)(\d+(\.\d+){0,1})$")
numeric_tolerance_regex = re.compile(r"^(\d+)%(\d+)$")


def _get_validation_file(validation_file: str) -> Dict[str, Dict]:
Expand Down Expand Up @@ -155,6 +157,9 @@ def compare(
elif "<->" in src and len(src.split("<->")) == 2:
cmp_result = _compare_range(src, dst)
return cmp_result
elif re.search(r"^\d+%\d+$", src):
cmp_result = _compare_tolerance(src, dst)
return cmp_result
else:
m = re.search(src, str(dst))
if m:
Expand Down Expand Up @@ -214,6 +219,22 @@ def _compare_range(src_num: str, dst_num: str) -> bool:
return False


def _compare_tolerance(src_num: str, dst_num: str) -> bool:
"""Compare against a tolerance percentage either side. You can use 't%%d'."""
dst_num = float(dst_num)

match = numeric_tolerance_regex.match(src_num)
if not match:
error = "Failed tolerance comparison. Collected: {}. Expected: {}".format(
dst_num, src_num
)
raise ValueError(error)

src_num = float(match.group(2))
max_diff = src_num * int(match.group(1)) / 100
return isclose(src_num, dst_num, abs_tol=max_diff)


def empty_tree(input_list: List) -> bool:
"""Recursively iterate through values in nested lists."""
for item in input_list:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,22 @@
"received_prefixes": 0
}
}
},
"192.0.2.4": {
"is_enabled": true,
"uptime": -1,
"remote_as": 65020,
"description": "",
"remote_id": "192.168.0.4",
"local_as": 65000,
"is_up": false,
"address_family": {
"ipv4": {
"sent_prefixes": 19,
"accepted_prefixes": 0,
"received_prefixes": 12
}
}
}
}
}
Expand Down
28 changes: 28 additions & 0 deletions test/base/validate/mocked_data/non_strict_fail/report.yml
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,34 @@ get_bgp_neighbors:
is_enabled: {complies: true, nested: false}
nested: true
192.0.2.3: {complies: true, nested: true}
192.0.2.4:
complies: false
diff:
complies: false
extra: []
missing: []
present:
address_family:
complies: false
diff:
complies: false
extra: []
missing: []
present:
ipv4:
complies: false
diff:
complies: false
extra: []
missing: []
present:
received_prefixes: {actual_value: 12, expected_value: "10%10", complies: False,
nested: false}
sent_prefixes: {complies: true, nested: False}
nested: true
nested: true
is_enabled: {complies: true, nested: false}
nested: true
nested: true
router_id: {actual_value: 192.0.2.2, expected_value: 192.6.6.6, complies: false, nested: false}
nested: true
Expand Down
12 changes: 9 additions & 3 deletions test/base/validate/mocked_data/non_strict_fail/validate.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,12 @@
sent_prefixes: 5
ipv6:
sent_prefixes: 6
192.0.2.4:
is_enabled: true
address_family:
ipv4:
sent_prefixes: "5%20"
received_prefixes: "10%10"

- get_interfaces_ip:
Ethernet2/1:
Expand All @@ -35,9 +41,9 @@
destination: 185.155.180.192/26
"10.155.180.192/26":
list:
- next_hop: 10.155.180.22
outgoing_interface: "irb.0"
protocol: "OSPF"
- next_hop: 10.155.180.22
outgoing_interface: "irb.0"
protocol: "OSPF"

- get_environment:
memory:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,22 @@
"received_prefixes": 0
}
}
},
"192.0.2.4": {
"is_enabled": true,
"uptime": -1,
"remote_as": 65020,
"description": "",
"remote_id": "192.168.0.4",
"local_as": 65000,
"is_up": false,
"address_family": {
"ipv4": {
"sent_prefixes": 19,
"accepted_prefixes": 0,
"received_prefixes": 12
}
}
}
}
}
Expand Down
12 changes: 9 additions & 3 deletions test/base/validate/mocked_data/non_strict_pass/validate.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,12 @@
sent_prefixes: 5
ipv6:
sent_prefixes: 2
192.0.2.4:
is_enabled: true
address_family:
ipv4:
sent_prefixes: "5%20"
received_prefixes: "20%10"

- get_interfaces_ip:
Ethernet2/1:
Expand All @@ -33,9 +39,9 @@
destination: 185.155.180.192/26
"10.155.180.192/26":
list:
- next_hop: 10.155.180.22
outgoing_interface: "irb.0"
protocol: "BGP"
- next_hop: 10.155.180.22
outgoing_interface: "irb.0"
protocol: "BGP"

- get_environment:
memory:
Expand Down
2 changes: 2 additions & 0 deletions test/base/validate/test_unit.py
Original file line number Diff line number Diff line change
Expand Up @@ -414,6 +414,8 @@ def test_numeric_comparison(self):
assert validate._compare_numeric("<=2", 2)
assert validate._compare_numeric("<3", "2")
assert validate._compare_numeric("!=3", "2")
assert validate._compare_tolerance("10%20", 18)
assert validate._compare_tolerance("10%20", 22)
with pytest.raises(ValueError):
assert validate._compare_numeric("a2a", 2)
with pytest.raises(ValueError):
Expand Down
Loading