Skip to content

Rename lib in jdiff #72

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

Merged
merged 3 commits into from
Jul 28, 2022
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: 1 addition & 1 deletion .bandit.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,4 @@ skips: []
# No need to check for security issues in the test scripts!
exclude_dirs:
- "./tests/"
- "./netcompare/tests/"
- "./jdiff/tests/"
4 changes: 2 additions & 2 deletions .github/ISSUE_TEMPLATE/bug_report.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
---
name: 🐛 Bug Report
about: Report a reproducible bug in the current release of netcompare
about: Report a reproducible bug in the current release of jdiff
---

### Environment
* Python version: <!-- Example: 3.7.7 -->
* netcompare version: <!-- Example: 1.0.0 -->
* jdiff version: <!-- Example: 1.0.0 -->

<!-- What did you expect to happen? -->
### Expected Behavior
Expand Down
2 changes: 1 addition & 1 deletion .github/ISSUE_TEMPLATE/feature_request.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ about: Propose a new feature or enhancement
---

### Environment
* netcompare version: <!-- Example: 1.0.0 -->
* jdiff version: <!-- Example: 1.0.0 -->

<!--
Describe in detail the new functionality you are proposing.
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ on: # yamllint disable-line rule:truthy rule:comments
- "pull_request"

env:
IMAGE_NAME: "netcompare"
IMAGE_NAME: "jdiff"

jobs:
black:
Expand Down
36 changes: 18 additions & 18 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
# netcompare
# jdiff

The `netcompare` is a light-weight library to examine structured data. `netcompare` provides an interface to intelligently compare json data objects as well as test for the presence (or absence) of keys, and to examine and compare their values.
The `jdiff` is a light-weight library to examine structured data. `jdiff` provides an interface to intelligently compare json data objects as well as test for the presence (or absence) of keys, and to examine and compare their values.

The library heavily relies on [jmespath](https://jmespath.org/) for traversing the json object and finding the value(s) to be evaluated. More on that [here](#customized-jmespath).

## Usage

A `netcompare` Check accepts two objects as input: the reference object, and the comparison object. The reference object is used as the intended or accepted state and it's keys and values are compared against the comparison object.
A `jdiff` Check accepts two objects as input: the reference object, and the comparison object. The reference object is used as the intended or accepted state and it's keys and values are compared against the comparison object.

`netcompare` does not collect the data for you, it simply works on data passed into it. This allows for maximum flexibility in collecting the data.
`jdiff` does not collect the data for you, it simply works on data passed into it. This allows for maximum flexibility in collecting the data.

For instance, the reference state can be collected from the network directly using any method that returns structured data: ansible, napalm, nornir to name a few. You could also choose to generate the reference state from an SoT, such as [Nautobot](https://github.com/nautobot/nautobot/), and have a true intended state.

`netcompare` is perfectly suited to work with data gathered from network devices via show commands, Ansible playbooks, as well as in applications such as [Nautobot](https://github.com/nautobot/nautobot/), or [Netbox](https://github.com/netbox-community/netbox). `netcompare` is focused on being the 'plumbing' behind a full network automation validation solution.
`jdiff` is perfectly suited to work with data gathered from network devices via show commands, Ansible playbooks, as well as in applications such as [Nautobot](https://github.com/nautobot/nautobot/), or [Netbox](https://github.com/netbox-community/netbox). `jdiff` is focused on being the 'plumbing' behind a full network automation validation solution.

### Testing data structures

Expand All @@ -29,9 +29,9 @@ Briefly, these tests, or `CheckTypes`, are provided to test the objects, to aide

## Workflow

| ![netcompare workflow](./docs/images/workflow.png) |
| ![jdiff workflow](./docs/images/workflow.png) |
|:---:|
| **`netcompare` workflow** |
| **`jdiff` workflow** |


1. The reference state object is assembled. The structured data may be collected from:
Expand All @@ -41,13 +41,13 @@ Briefly, these tests, or `CheckTypes`, are provided to test the objects, to aide

2. The Network Engineer makes changes to the network, whether it is an upgrade, peering change, or migration.
3. The comparison state is collected, typically directly from the network, but any method is acceptable.
4. The reference state is then compared to the current state using the netcompare library.
4. The reference state is then compared to the current state using the jdiff library.

## Library Architecture

| ![netcompare HLD](./docs/images/hld.png) |
| ![jdiff HLD](./docs/images/hld.png) |
|:---:|
| **`netcompare` architecture** |
| **`jdiff` architecture** |

An instance of `CheckType` object must be created first before passing one of the below check types as an argument:

Expand All @@ -63,7 +63,7 @@ my_check = "exact_match"
check = CheckType.init(my_check)
```

Next, define a json object as reference data, as well as a JMESPATH expression to extract the value wanted and pass them to `get_value` method. Be aware! `netcompare` works with a customized version of JMESPATH. More on that [below](#customized-jmespath).
Next, define a json object as reference data, as well as a JMESPATH expression to extract the value wanted and pass them to `get_value` method. Be aware! `jdiff` works with a customized version of JMESPATH. More on that [below](#customized-jmespath).

```python
bgp_pre_change = "./pre/bgp.json"
Expand All @@ -88,7 +88,7 @@ results = check.evaluate(post_value, pre_value, **evaluate_args)

## Customized JMESPATH

Since `netcompare` works with json objects as data inputs, JMESPATH was the obvious choice for traversing the data and extracting the value(s) to compare.
Since `jdiff` works with json objects as data inputs, JMESPATH was the obvious choice for traversing the data and extracting the value(s) to compare.

However, JMESPATH comes with a limitation where is not possible to define a `key` to which the `value` belongs to.

Expand Down Expand Up @@ -142,7 +142,7 @@ A JMESPATH expression to extract `state` is shown below.
```

How can we understand that `Idle` is relative to peer 7.7.7.7 and `Connected` to peer `10.1.0.0` ?
We could index the output but that would require some post-processing of the data. For that reason, `netcompare` use a customized version of JMESPATH where it is possible to define a reference key for the value(s) wanted. The reference key must be within `$` sign anchors and defined in a list, together with the value(s):
We could index the output but that would require some post-processing of the data. For that reason, `jdiff` use a customized version of JMESPATH where it is possible to define a reference key for the value(s) wanted. The reference key must be within `$` sign anchors and defined in a list, together with the value(s):

```python
"result[0].vrfs.default.peerList[*].[$peerAddress$,state]
Expand All @@ -166,7 +166,7 @@ Examples:


```python
>>> from netcompare import CheckType
>>> from jdiff import CheckType
>>> pre_data = {
"jsonrpc": "2.0",
"id": "EapiExplorer-1",
Expand Down Expand Up @@ -224,7 +224,7 @@ Examples:
>>> # Create an instance of CheckType object with 'exact_match' as check-type argument.
>>> my_check = CheckType.init(check_type="exact_match")
>>> my_check
>>> <netcompare.check_types.ExactMatchType object at 0x10ac00f10>
>>> <jdiff.check_types.ExactMatchType object at 0x10ac00f10>
>>> # Extract the wanted value from pre_dat to later compare with post_data. As we want compare all the body (excluding "interfaceStatistics"), we do not need to define any reference key
>>> pre_value = my_check.get_value(output=pre_data, path=my_jmspath, exclude=exclude_fields)
>>> pre_value
Expand Down Expand Up @@ -256,7 +256,7 @@ Let's see a better way to run `exact_match` for this specific case. Since we are
```
Targeting only the `interfaceStatus` key, we would need to define a reference key (in this case `$name$`), we would not define any exclusion list.

The anchor logic for the reference key applies to all check-types available in `netcompare`
The anchor logic for the reference key applies to all check-types available in `jdiff`


### Tolerance
Expand Down Expand Up @@ -329,7 +329,7 @@ Lets have a look to a couple of examples:
>>> pre_value = my_check.get_value(pre_data, my_jmspath)
>>> post_value = my_check.get_value(post_data, my_jmspath)
>>> actual_results = my_check.evaluate(post_value, pre_value, **my_tolerance_arguments)
>>> # Netcompare returns the value that are not within the 10%
>>> # jdiff returns the value that are not within the 10%
>>> actual_results
({'10.1.0.0': {'accepted_prefixes': {'new_value': 500, 'old_value': 900}, 'received_prefixes': {'new_value': 599, 'old_value': 999}, 'sent_prefixes': {'new_value': 511, 'old_value': 1011}}}, False)
>>> # Let's difine a higher tolerance
Expand Down Expand Up @@ -443,7 +443,7 @@ Let's run an example where we want to check the `burnedInAddress` key has a stri
>>> # What if we want "no-match"?
>>> regex_args = {"regex": "(?:[0-9a-fA-F]:?){12}", "mode": "no-match"}
>>> result = check.evaluate(value, **regex_args)
>>> # Netcompare return the failing data as the regex match the value
>>> # jdiff return the failing data as the regex match the value
>>> result
({'Management1': {'burnedInAddress': '08:00:27:e6:b2:f8'}}, False)
```
Expand Down
File renamed without changes.
2 changes: 1 addition & 1 deletion netcompare/check_types.py → jdiff/check_types.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ class CheckType(ABC):
"""Check Type Base Abstract Class."""

@staticmethod
def init(check_type: str):
def create(check_type: str):
"""Factory pattern to get the appropriate CheckType implementation.

Args:
Expand Down
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
"""
jmespath expression parsers and related utilities.

This utility interfaces the custom netcompare jmespath expression with the jmespath library.
From one expression defined in netcompare, we will derive two expressions: one expression that traverse the json output and get the
This utility interfaces the custom jdiff jmespath expression with the jmespath library.
From one expression defined in jdiff, we will derive two expressions: one expression that traverse the json output and get the
evaluated bit of it, the second will target the reference key relative to the value to evaluate. More on README.md
"""
import re
Expand Down
6 changes: 3 additions & 3 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
[tool.poetry]
name = "netcompare"
name = "jdiff"
version = "0.0.1-beta.1"
description = "A light-weight library to compare structured output from network devices show commands."
authors = ["Network to Code, LLC <info@networktocode.com>"]
license = "Apache-2.0"
homepage = "https://github.com/networktocode-llc/netcompare"
repository = "https://github.com/networktocode-llc/netcompare"
homepage = "https://github.com/networktocode-llc/jdiff"
repository = "https://github.com/networktocode-llc/jdiff"
readme = "README.md"
keywords = ["json", "diff", "network"]
classifiers = [
Expand Down
2 changes: 1 addition & 1 deletion tests/test_data_normalization.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
"Flatten list unit test"
import pytest
from netcompare.utils.data_normalization import flatten_list
from jdiff.utils.data_normalization import flatten_list
from .utility import ASSERT_FAIL_MESSAGE


Expand Down
6 changes: 3 additions & 3 deletions tests/test_diff_generator.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
"""Diff generator tests."""
import pytest
from netcompare.evaluators import diff_generator
from netcompare.check_types import CheckType
from jdiff.evaluators import diff_generator
from jdiff.check_types import CheckType
from .utility import load_mocks, ASSERT_FAIL_MESSAGE


Expand Down Expand Up @@ -162,7 +162,7 @@
)

# Test GitLab Issues
# Test issue #44: https://github.com/networktocode-llc/netcompare/issues/44
# Test issue #44: https://github.com/networktocode-llc/jdiff/issues/44
test_issue_44_case_1 = (
"raw_novalue_exclude",
"result[*].interfaces.*.[$name$,interfaceStatus]",
Expand Down
2 changes: 1 addition & 1 deletion tests/test_diff_helpers.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
"""DIff helpers unit tests."""
from netcompare.utils.diff_helpers import dict_merger, group_value, fix_deepdiff_key_names, get_diff_iterables_items
from jdiff.utils.diff_helpers import dict_merger, group_value, fix_deepdiff_key_names, get_diff_iterables_items


def test_dict_merger():
Expand Down
2 changes: 1 addition & 1 deletion tests/test_filter_parsers.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
"Filter parser unit tests."
import pytest
from netcompare.utils.data_normalization import exclude_filter
from jdiff.utils.data_normalization import exclude_filter
from .utility import ASSERT_FAIL_MESSAGE


Expand Down
4 changes: 2 additions & 2 deletions tests/test_get_value.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
"""Test GitHub issues."""
import pytest
from netcompare import CheckType
from jdiff import CheckType


my_data = [{"global": {"peers": {"10.1.0.0": "peer1", "10.2.0.0": "peer2"}}}]
Expand All @@ -10,7 +10,7 @@
def test_jmspath_return_none(data):
"""Handle exception when JMSPath retunr None."""
my_jmspath = "global[*]"
my_check = CheckType.init(check_type="exact_match")
my_check = CheckType.create(check_type="exact_match")
with pytest.raises(TypeError) as error:
my_check.get_value(output=data, path=my_jmspath)() # pylint: disable=E0110

Expand Down
2 changes: 1 addition & 1 deletion tests/test_jmespath_parsers.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
"""jmespath parser unit tests."""
import pytest
from netcompare.utils.jmespath_parsers import (
from jdiff.utils.jmespath_parsers import (
jmespath_value_parser,
jmespath_refkey_parser,
keys_values_zipper,
Expand Down
4 changes: 2 additions & 2 deletions tests/test_operators.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
"""Unit tests for operator check-type."""
import pytest
from netcompare.check_types import CheckType
from jdiff.check_types import CheckType
from .utility import load_json_file, ASSERT_FAIL_MESSAGE

operator_all_same = (
Expand Down Expand Up @@ -170,7 +170,7 @@
@pytest.mark.parametrize("filename, check_type_str, evaluate_args, path, expected_result", operator_all_tests)
def test_operator(filename, check_type_str, evaluate_args, path, expected_result):
"""Validate all operator check types."""
check = CheckType.init(check_type_str)
check = CheckType.create(check_type_str)
# There is not concept of "pre" and "post" in operator.
data = load_json_file("api", filename)
value = check.get_value(data, path)
Expand Down
18 changes: 9 additions & 9 deletions tests/test_sw_upgrade_device_state.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
"""Tests for typical software upgrade device state check."""
from copy import deepcopy
import pytest
from netcompare.check_types import CheckType
from jdiff.check_types import CheckType
from .utility import load_json_file


Expand All @@ -21,7 +21,7 @@ def test_show_version(platform, command, jpath, expected_parameter, check_should
filename = f"{platform}_{command}.json"
command = load_json_file("sw_upgrade", filename)

check = CheckType.init("parameter_match")
check = CheckType.create("parameter_match")
value = check.get_value(command, jpath)
eval_results, passed = check.evaluate(value, expected_parameter, "match") # pylint: disable=E1121
assert passed is check_should_pass, f"FAILED, eval_result: {eval_results}"
Expand Down Expand Up @@ -50,7 +50,7 @@ def test_show_interfaces_state(platform, command, jpath, check_should_pass):
command_post[0]["link_status"] = "down"
command_post[1]["protocol_status"] = "down"

check = CheckType.init("exact_match")
check = CheckType.create("exact_match")
pre_value = CheckType.get_value(command_pre, jpath)
post_value = CheckType.get_value(command_post, jpath)
eval_results, passed = check.evaluate(post_value, pre_value)
Expand All @@ -69,7 +69,7 @@ def test_show_ip_route_exact_match(platform, command):
"""Test identical route table pass the test with exact_match."""
command_pre = command_post = load_json_file("sw_upgrade", f"{platform}_{command}.json")

check = CheckType.init("exact_match")
check = CheckType.create("exact_match")
eval_results, passed = check.evaluate(command_post, command_pre)
assert passed is True, f"FAILED, eval_result: {eval_results}"

Expand All @@ -85,7 +85,7 @@ def test_show_ip_route_exact_match(platform, command):
def test_show_ip_route_missing_and_additional_routes(platform, command):
"""Test missing or additional routes fail the test with exact_match."""
command_pre = command_post = load_json_file("sw_upgrade", f"{platform}_{command}.json")
check = CheckType.init("exact_match")
check = CheckType.create("exact_match")
print(len(command_pre))
eval_results_missing, passed_missing = check.evaluate(command_post[:30], command_pre)
eval_results_additional, passed_additional = check.evaluate(command_post, command_pre[:30])
Expand Down Expand Up @@ -114,7 +114,7 @@ def test_bgp_neighbor_state(platform, command, jpath, check_should_pass):
state_key = "state" if "arista" in platform else "bgp_state"
command_post[0][state_key] = "Idle"

check = CheckType.init("exact_match")
check = CheckType.create("exact_match")
pre_value = CheckType.get_value(command_pre, jpath)
post_value = CheckType.get_value(command_post, jpath)
eval_results, passed = check.evaluate(post_value, pre_value)
Expand All @@ -139,7 +139,7 @@ def test_bgp_prefix_tolerance(platform, command, prfx_post_value, tolerance, che

command_post[1]["state_pfxrcd"] = command_post[1]["state_pfxrcd"] = prfx_post_value

check = CheckType.init("tolerance")
check = CheckType.create("tolerance")
jpath = "[*].[$bgp_neigh$,state_pfxrcd]"
pre_value = CheckType.get_value(command_pre, jpath)
post_value = CheckType.get_value(command_post, jpath)
Expand Down Expand Up @@ -168,7 +168,7 @@ def test_ospf_neighbor_state(platform, command, jpath, check_should_pass):
command_post[0]["state"] = "2WAY"
command_post = command_post[:1]

check = CheckType.init("exact_match")
check = CheckType.create("exact_match")
pre_value = CheckType.get_value(command_pre, jpath)
post_value = CheckType.get_value(command_post, jpath)
eval_results, passed = check.evaluate(post_value, pre_value)
Expand Down Expand Up @@ -199,6 +199,6 @@ def test_lldp_neighbor_state(platform, command, check_should_pass):
if check_should_pass is False:
command_post = command_post[:2]

check = CheckType.init("exact_match")
check = CheckType.create("exact_match")
eval_results, passed = check.evaluate(command_post, command_pre)
assert passed is check_should_pass, f"FAILED, eval_result: {eval_results}"
Loading