From e45d38944504fb6b04fb61ff6dbb86310bf2719e Mon Sep 17 00:00:00 2001 From: itdependsnetworks Date: Wed, 22 Sep 2021 16:36:20 -0400 Subject: [PATCH 01/12] Update pyproject.toml build-server --- pyproject.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/pyproject.toml b/pyproject.toml index 108c33c9..447f8410 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -91,6 +91,7 @@ notes = """, python_paths = "./" testpaths = "tests/" addopts = "-vv --doctest-modules -p no:warnings --ignore-glob='*mock*'" +[build-system] requires = ["poetry>=0.12"] build-backend = "poetry.masonry.api" From 98c14b6fb7eef30116dfb0630233085525ad8497 Mon Sep 17 00:00:00 2001 From: itdependsnetworks Date: Fri, 24 Sep 2021 08:12:45 -0400 Subject: [PATCH 02/12] Add get peer address, fixes #48 --- netutils/ip.py | 29 +++++++++++++++++++++++++++++ netutils/utils.py | 1 + tests/unit/test_ip.py | 32 ++++++++++++++++++++++++++++++++ 3 files changed, 62 insertions(+) diff --git a/netutils/ip.py b/netutils/ip.py index ccdc3675..1770e8f6 100644 --- a/netutils/ip.py +++ b/netutils/ip.py @@ -247,6 +247,35 @@ def get_first_usable(ip_network): return str(net[1]) +def get_peer_ip(ip_interface): + """Given an IP interface (an ip address, with subnet mask) that is on a peer network, return the peer IP. + + Args: + ip_interface (str): An IP interface in string format that is able to be converted by `ipaddress` library. + + Returns: + str: IP address formatted string with the cooresponding peer IP. + + Example: + >>> from netutils.ip import get_peer_ip + >>> get_peer_ip('10.0.0.1/255.255.255.252') + '10.0.0.2' + >>> get_peer_ip('10.0.0.2/30') + '10.0.0.1' + >>> get_peer_ip('10.0.0.1/255.255.255.254') + '10.0.0.0' + >>> get_peer_ip('10.0.0.0/31') + '10.0.0.1' + >>> + """ + ip_obj = ipaddress.ip_interface(ip_interface) + last_bit = int(ip_obj) & 1 + mask_type = ip_obj._prefixlen & 1 + if mask_type ^ last_bit: + return ip_addition(str(ip_obj.ip), 1) + return ip_subtract(str(ip_obj.ip), 1) + + def get_usable_range(ip_network): """Given a network, return the string of usable IP addresses. diff --git a/netutils/utils.py b/netutils/utils.py index c51835d0..63a83bbe 100644 --- a/netutils/utils.py +++ b/netutils/utils.py @@ -33,6 +33,7 @@ "get_all_host": "ip.get_all_host", "get_broadcast_address": "ip.get_broadcast_address", "get_first_usable": "ip.get_first_usable", + "get_peer_ip": "ip.get_peer_ip", "get_usable_range": "ip.get_usable_range", "is_valid_mac": "mac.is_valid_mac", "mac_to_format": "mac.mac_to_format", diff --git a/tests/unit/test_ip.py b/tests/unit/test_ip.py index b2d4efda..98e977cf 100644 --- a/tests/unit/test_ip.py +++ b/tests/unit/test_ip.py @@ -153,6 +153,34 @@ }, ] +GET_PEER = [ + { + "sent": {"ip_interface": "10.0.0.1/255.255.255.252"}, + "received": "10.0.0.2", + }, + { + "sent": {"ip_interface": "10.0.0.2/30"}, + "received": "10.0.0.1", + }, + { + "sent": {"ip_interface": "10.0.0.1/31"}, + "received": "10.0.0.0", + }, + { + "sent": {"ip_interface": "10.0.0.0/255.255.255.254"}, + "received": "10.0.0.1", + }, + { + "sent": {"ip_interface": "2001::1/126"}, + "received": "2001::2", + }, + { + "sent": {"ip_interface": "2001::1/127"}, + "received": "2001::", + }, +] + + USABLE_RANGE = [ { "sent": {"ip_network": "10.1.1.0/24"}, @@ -291,3 +319,7 @@ def test_cidr_to_netmask_fail(): with pytest.raises(ValueError, match=r"Parameter must be an integer between 0 and 32."): data = {"cidr": 37} ip.cidr_to_netmask(**data) + +@pytest.mark.parametrize("data", GET_PEER) +def test_get_peer_ip(data): + assert ip.get_peer_ip(**data["sent"]) == data["received"] From 2cdcba3b23b0374b18cdc377a879a10798aa3899 Mon Sep 17 00:00:00 2001 From: itdependsnetworks Date: Sat, 25 Sep 2021 11:41:22 -0400 Subject: [PATCH 03/12] Update get_peer_ip validation, tests, and update a few tests, fix issue with route type checking --- netutils/ip.py | 19 +++++++++++++----- netutils/mac.py | 3 +-- netutils/route.py | 2 +- tests/unit/test_ip.py | 42 ++++++++++++++++++++++++++++++++++++++++ tests/unit/test_route.py | 10 ++++++++++ tests/unit/test_vlan.py | 2 ++ 6 files changed, 70 insertions(+), 8 deletions(-) diff --git a/netutils/ip.py b/netutils/ip.py index 1770e8f6..cf80e70b 100644 --- a/netutils/ip.py +++ b/netutils/ip.py @@ -269,11 +269,20 @@ def get_peer_ip(ip_interface): >>> """ ip_obj = ipaddress.ip_interface(ip_interface) - last_bit = int(ip_obj) & 1 - mask_type = ip_obj._prefixlen & 1 - if mask_type ^ last_bit: - return ip_addition(str(ip_obj.ip), 1) - return ip_subtract(str(ip_obj.ip), 1) + if isinstance(ip_obj, ipaddress.IPv4Address) and ip_obj.network.prefixlen not in [30, 31]: + raise ValueError(f"{ip_obj} did not conform to IPv4 acceptable masks of 30 or 31") + if isinstance(ip_obj, ipaddress.IPv6Address) and ip_obj.network.prefixlen not in [126, 127]: + raise ValueError(f"{ip_obj} did not conform to IPv6 acceptable masks of 126 or 127") + if ip_obj.network.prefixlen in [30, 126] and ip_obj.ip in [ + ip_obj.network.network_address, + ip_obj.network.broadcast_address, + ]: + raise ValueError(f"{ip_obj} is not an IP in the point-to-point link usable range.") + # The host lists returns all usable IPs, remove the matching one, return the first element. This can be optimized greatly, but left + # like this for simplicity. Note: IPv6 technically does not have a broadcast address, but for ptp, this is not considered. + val = list(get_all_host(str(ip_obj.network))) + val.remove(str(ip_obj.ip)) + return val[0] def get_usable_range(ip_network): diff --git a/netutils/mac.py b/netutils/mac.py index 64b2c8dd..73d5affd 100644 --- a/netutils/mac.py +++ b/netutils/mac.py @@ -89,7 +89,7 @@ def mac_to_int(mac): @_valid_mac -def mac_type(mac): +def mac_type(mac): # pylint: disable=inconsistent-return-statements """Retuns the "type" of MAC address, as defined by the regex pattern names. Args: @@ -109,7 +109,6 @@ def mac_type(mac): for name, pattern in MAC_REGEX.items(): if re.fullmatch(pattern, mac): return name - raise ValueError("MAC pattern not found.") @_valid_mac diff --git a/netutils/route.py b/netutils/route.py index 7a8db680..bfce8ca8 100644 --- a/netutils/route.py +++ b/netutils/route.py @@ -31,7 +31,7 @@ def longest_prefix_match(ip_addr, routes): if isinstance(ip_addr, str): ip_addr = ipaddress.ip_address(ip_addr) else: - if not isinstance(ipaddress.ip_address, ip_addr): + if not isinstance(ip_addr, ipaddress._IPAddressBase): raise TypeError(f"'ip_addr' should be a str, got {type(ip_addr)}") networks = [ diff --git a/tests/unit/test_ip.py b/tests/unit/test_ip.py index 98e977cf..98472c04 100644 --- a/tests/unit/test_ip.py +++ b/tests/unit/test_ip.py @@ -179,6 +179,29 @@ "received": "2001::", }, ] +GET_PEER_BAD_MASK = [ + { + "sent": {"ip_interface": "10.0.0.1/255.255.255.255"}, + }, + { + "sent": {"ip_interface": "10.0.0.2/24"}, + }, + { + "sent": {"ip_interface": "2001::/64"}, + }, +] + +GET_PEER_BAD_IP = [ + { + "sent": {"ip_interface": "10.0.0.0/255.255.255.252"}, + }, + { + "sent": {"ip_interface": "10.0.0.3/30"}, + }, + { + "sent": {"ip_interface": "2001::/126"}, + }, +] USABLE_RANGE = [ @@ -315,11 +338,30 @@ def test_cidr_to_netmaskv6(data): assert ip.cidr_to_netmaskv6(**data["sent"]) == data["received"] +def test_cidr_to_netmaskv6_fail(): + with pytest.raises(ValueError, match=r"Parameter must be an integer between 0 and 128.*"): + data = {"cidr": 129} + ip.cidr_to_netmaskv6(**data) + + def test_cidr_to_netmask_fail(): with pytest.raises(ValueError, match=r"Parameter must be an integer between 0 and 32."): data = {"cidr": 37} ip.cidr_to_netmask(**data) + @pytest.mark.parametrize("data", GET_PEER) def test_get_peer_ip(data): assert ip.get_peer_ip(**data["sent"]) == data["received"] + + +@pytest.mark.parametrize("data", GET_PEER_BAD_MASK) +def test_get_peer_ip_fail_subnet(data): + with pytest.raises(ValueError, match=r".*acceptable masks.*"): + ip.get_peer_ip(**data["sent"]) + + +@pytest.mark.parametrize("data", GET_PEER_BAD_IP) +def test_get_peer_ip_fail_ip(data): + with pytest.raises(ValueError, match=r".*usable range.*"): + ip.get_peer_ip(**data["sent"]) diff --git a/tests/unit/test_route.py b/tests/unit/test_route.py index 1b21c7f0..b4a43431 100644 --- a/tests/unit/test_route.py +++ b/tests/unit/test_route.py @@ -101,3 +101,13 @@ def test_route_no_best_route_found(): {"network": "10.1.1.0", "mask": "24"}, ] longest_prefix_match(lookup, routes) + + +def test_route_non_ip_sent(): + """Test when sending a non-ip.""" + with pytest.raises(TypeError): + lookup = 12345 + routes = [ + {"network": "10.1.1.240", "mask": "255.255.255.240"}, + ] + longest_prefix_match(lookup, routes) diff --git a/tests/unit/test_vlan.py b/tests/unit/test_vlan.py index df21b267..44b1d544 100644 --- a/tests/unit/test_vlan.py +++ b/tests/unit/test_vlan.py @@ -40,3 +40,5 @@ def test_to_list_failure(): vlan.vlanconfig_to_list("switchport trunk allowed vlan 1025,1069-1072,4099") with pytest.raises(ValueError, match=r"There were non-digits and dashes*"): vlan.vlanconfig_to_list("switchport trunk allowed vlan 1025,1069-1072,BADDATA") + with pytest.raises(ValueError, match=r"No digits found in line *"): + vlan.vlanconfig_to_list("switchport trunk allowed vlan BADDATA") From c3a41324bb24142e94d4f15a0ffc8789ade40714 Mon Sep 17 00:00:00 2001 From: itdependsnetworks Date: Mon, 27 Sep 2021 09:52:55 -0400 Subject: [PATCH 04/12] Update testing and spelling --- netutils/ip.py | 2 +- netutils/route.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/netutils/ip.py b/netutils/ip.py index cf80e70b..5bfd9283 100644 --- a/netutils/ip.py +++ b/netutils/ip.py @@ -254,7 +254,7 @@ def get_peer_ip(ip_interface): ip_interface (str): An IP interface in string format that is able to be converted by `ipaddress` library. Returns: - str: IP address formatted string with the cooresponding peer IP. + str: IP address formatted string with the corresponding peer IP. Example: >>> from netutils.ip import get_peer_ip diff --git a/netutils/route.py b/netutils/route.py index bfce8ca8..da11a918 100644 --- a/netutils/route.py +++ b/netutils/route.py @@ -31,7 +31,7 @@ def longest_prefix_match(ip_addr, routes): if isinstance(ip_addr, str): ip_addr = ipaddress.ip_address(ip_addr) else: - if not isinstance(ip_addr, ipaddress._IPAddressBase): + if not isinstance(ip_addr, (ipaddress.IPv4Address, ipaddress.IPv6Address)): raise TypeError(f"'ip_addr' should be a str, got {type(ip_addr)}") networks = [ From d9ec4c9d0c24a806d94bb2ed018ce9c508777791 Mon Sep 17 00:00:00 2001 From: itdependsnetworks Date: Mon, 27 Sep 2021 20:38:00 -0400 Subject: [PATCH 05/12] update version in toml and init files --- netutils/__init__.py | 2 +- pyproject.toml | 2 +- tests/unit/test_version.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/netutils/__init__.py b/netutils/__init__.py index 19c175f2..2c1b8acd 100644 --- a/netutils/__init__.py +++ b/netutils/__init__.py @@ -1,3 +1,3 @@ """Initialization file for library.""" -__version__ = "0.2.2" +__version__ = "0.2.3" diff --git a/pyproject.toml b/pyproject.toml index 447f8410..5247580a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "netutils" -version = "0.2.2" +version = "0.2.3" description = "Common helper functions useful in network automation." authors = ["Network to Code, LLC "] license = "Apache-2.0" diff --git a/tests/unit/test_version.py b/tests/unit/test_version.py index f481462f..2636ffda 100644 --- a/tests/unit/test_version.py +++ b/tests/unit/test_version.py @@ -1,4 +1,4 @@ -"""Basic tests that do not require Django.""" +"""Basic test for version check.""" import unittest import os import toml From f9070b243fe1d4773207e3d047003c8e1680a38f Mon Sep 17 00:00:00 2001 From: Viktor Kertesz <74217042+viktorkertesz@users.noreply.github.com> Date: Fri, 15 Oct 2021 21:36:13 +0200 Subject: [PATCH 06/12] add interface_range_compress function (#33) * add interface_range_generator * small fix for variable length interfaces mixed in input * comment updates * interface_range_generator testing added * renamed, linting1 * linting and doc fixes * linting and doc fixes * refactor assemble_port, cleanup * suppress pylint messages * more test cases added * linting fix * interface_range_compress refactor to more basic function * fix conflict problems * linting and typo fixes * linting fix * doc update * doc update * doc update * more readable interface assembly and examples Co-authored-by: Viktor Kertesz --- netutils/interface.py | 64 +++++++++++++++++++++++++++++++++++- netutils/utils.py | 1 + tests/unit/test_interface.py | 32 ++++++++++++++++++ 3 files changed, 96 insertions(+), 1 deletion(-) diff --git a/netutils/interface.py b/netutils/interface.py index df1db892..9e2e33b2 100644 --- a/netutils/interface.py +++ b/netutils/interface.py @@ -5,7 +5,6 @@ from abc import ABC, abstractmethod, abstractproperty from functools import total_ordering from operator import itemgetter - from .constants import BASE_INTERFACES, REVERSE_MAPPING @@ -519,3 +518,66 @@ def _check_order_option_exists(order): """ if order not in INTERFACE_LIST_ORDERING_OPTIONS.keys(): raise ValueError(f"{order} is not one of the supported orderings") + + +def _ranges_in_list(numbers: t.List[int]): + """Find contiguous ranges in a list of numbers. + + Example: + >>> _ranges_in_list([1, 2, 3, 5, 6, 8]) + [[1, 2, 3], [5, 6], [8]] + + Args: + numbers: list of numbers + + Returns: + list: list of ranges in input + """ + return [list(map(itemgetter(1), g)) for k, g in itertools.groupby(enumerate(numbers), lambda x: x[0] - x[1])] + + +def interface_range_compress(interface_list: t.List[str]) -> t.List[str]: + """Function which takes interfaces and return interface ranges. + + Whitespace and special characters are ignored in the input. Input must contain only interfaces, + there is no check against correct interface names! Also interface names must use the same abbreviation! + E.g. Gi =! GigabitEthernet + + Example: + >>> interface_range_compress(["Gi1/0/1", "Gi1/0/2", "Gi1/0/3", "Gi1/0/5"]) + ['Gi1/0/1-3', 'Gi1/0/5'] + >>> interface_range_compress(["Gi0/1", "Gi0/2", "Gi0/4", "Gi1/0", "Gi1/1"]) + ['Gi0/1-2', 'Gi0/4', 'Gi1/0-1'] + + Args: + interface_list: list of interfaces + + Returns: + list: list of interface ranges + """ + result_dict = {} + final_result_list = [] + sorted_ints = [_split_interface_tuple(x) for x in sort_interface_list(interface_list)] + if not sorted_ints: + return [] + current_match = sorted_ints[0][0:-1] + for interface in sorted_ints: + if interface[0:-1] == current_match: + module = "".join([x.val for x in current_match]) + if result_dict.get(module): + result_dict[module] += [int(interface[-1].val)] + else: + result_dict[module] = [int(interface[-1].val)] + else: + current_match = interface[0:-1] + result_dict["".join([x.val for x in current_match])] = [int(interface[-1].val)] + for module, ports in result_dict.items(): + # find ranges in this port list + ranges = _ranges_in_list(ports) + # assemble module and port ranges + for range_group in ranges: + if len(range_group) > 1: + final_result_list.append(f"{module}{range_group[0]}-{range_group[-1]}") + else: + final_result_list.append(f"{module}{range_group[0]}") + return final_result_list diff --git a/netutils/utils.py b/netutils/utils.py index 63a83bbe..f2f3de60 100644 --- a/netutils/utils.py +++ b/netutils/utils.py @@ -15,6 +15,7 @@ "is_fqdn_valid": "dns.is_fqdn_valid", "is_fqdn_resolvable": "dns.is_fqdn_resolvable", "interface_range_expansion": "interface.interface_range_expansion", + "interface_range_compress": "interface.interface_range_compress", "split_interface": "interface.split_interface", "canonical_interface_name": "interface.canonical_interface_name", "canonical_interface_name_list": "interface.canonical_interface_name_list", diff --git a/tests/unit/test_interface.py b/tests/unit/test_interface.py index 6ad11810..3dd1d1ce 100644 --- a/tests/unit/test_interface.py +++ b/tests/unit/test_interface.py @@ -457,6 +457,33 @@ }, ] +INTERFACE_RANGE_COMPRESS = [ + {"sent": [], "received": []}, + {"sent": ["Gi1"], "received": ["Gi1"]}, + {"sent": ["Gi1", "Gi2", "Gi3"], "received": ["Gi1-3"]}, + {"sent": ["Gi1", "Gi1/1/1/1/1", "Gi1/1/1/1/2"], "received": ["Gi1", "Gi1/1/1/1/1-2"]}, + {"sent": ["Gi0/1", "Gi0/2", "Gi0/4", "Gi1/0", "Gi1/1"], "received": ["Gi0/1-2", "Gi0/4", "Gi1/0-1"]}, + { + "sent": ["Gi1", "Gi3", "Gi5", "Gi7", "Gi9", "Gi11", "Gi0/1", "Gi0/2", "Gi0/3", "Gi0/0", "Gi0/4"], + "received": ["Gi0/0-4", "Gi1", "Gi3", "Gi5", "Gi7", "Gi9", "Gi11"], + }, + { + "sent": [ + "Gi152/1/0/2", + "Gi152/1/0/3", + "Gi152/1/0/4", + "Gi152/1/0/5", + "Gi152/1/0/6", + "Gi152/2/0/1", + "Gi152/2/0/2", + "Gi152/2/0/2", + "Gi152/2/0/3", + "Gi152/2/0/5", + ], + "received": ["Gi152/1/0/2-6", "Gi152/2/0/1-3", "Gi152/2/0/5"], + }, +] + @pytest.mark.parametrize("data", SPLIT_INTERFACE) def test_split_interface(data): @@ -524,6 +551,11 @@ def test_interface_range_expansion(data): assert interface.interface_range_expansion(data["sent"]) == data["received"] +@pytest.mark.parametrize("data", INTERFACE_RANGE_COMPRESS) +def test_interface_range_compress(data): + assert interface.interface_range_compress(data["sent"]) == data["received"] + + @pytest.mark.parametrize("data", BAD_INTERFACE_NAMES) def test_split_interface_tuple_fails(data): with pytest.raises(ValueError): From e5d2d267639943c552e2f7b277f9d09ba8fbaca4 Mon Sep 17 00:00:00 2001 From: itdependsnetworks Date: Sun, 17 Oct 2021 21:09:10 -0400 Subject: [PATCH 07/12] Fix lack of zero padding on ip to binary conversion --- netutils/ip.py | 5 +++-- tests/unit/test_ip.py | 4 ++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/netutils/ip.py b/netutils/ip.py index 5bfd9283..95de4ff6 100644 --- a/netutils/ip.py +++ b/netutils/ip.py @@ -52,10 +52,11 @@ def ip_to_bin(ip): Example: >>> from netutils.ip import ip_to_bin >>> ip_to_bin("10.100.100.100") - '1010011001000110010001100100' + '00001010011001000110010001100100' >>> """ - return bin(int(ipaddress.ip_address(ip)))[2:] + ip_obj = ipaddress.ip_address(ip) + return bin(int(ip_obj))[2:].zfill(ip_obj.max_prefixlen) def ip_subtract(ip, val): diff --git a/tests/unit/test_ip.py b/tests/unit/test_ip.py index 98472c04..a1f69080 100644 --- a/tests/unit/test_ip.py +++ b/tests/unit/test_ip.py @@ -53,13 +53,13 @@ "sent": { "ip": "10.1.1.1", }, - "received": "1010000000010000000100000001", + "received": "00001010000000010000000100000001", }, { "sent": { "ip": "2001:db8:3333:4444:5555:6666:7777:8888", }, - "received": "100000000000010000110110111000001100110011001101000100010001000101010101010101011001100110011001110111011101111000100010001000", + "received": "00100000000000010000110110111000001100110011001101000100010001000101010101010101011001100110011001110111011101111000100010001000", }, ] From ba1453078789db0605c4c2901be5b86c3b4a279a Mon Sep 17 00:00:00 2001 From: Uros Bajzelj Date: Wed, 20 Oct 2021 10:36:30 +0200 Subject: [PATCH 08/12] CI/CD implementation on GitHub actions (#64) --- .github/workflows/ci.yml | 213 +++++++++++++++++++++++++++++++++++++++ .travis.yml | 74 -------------- 2 files changed, 213 insertions(+), 74 deletions(-) create mode 100644 .github/workflows/ci.yml delete mode 100644 .travis.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 00000000..0a89c8bb --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,213 @@ +--- +name: "CI" +on: # yamllint disable + - "push" + - "pull_request" + +jobs: + black: + runs-on: "ubuntu-20.04" + env: + INVOKE_LOCAL: "True" + steps: + - name: "Check out repository code" + uses: "actions/checkout@v2" + - name: "Setup environment" + uses: "networktocode/gh-action-setup-poetry-environment@v2" + - name: "Linting: black" + run: "poetry run invoke black" + bandit: + runs-on: "ubuntu-20.04" + env: + INVOKE_LOCAL: "True" + steps: + - name: "Check out repository code" + uses: "actions/checkout@v2" + - name: "Setup environment" + uses: "networktocode/gh-action-setup-poetry-environment@v2" + - name: "Linting: bandit" + run: "poetry run invoke bandit" + needs: + - "black" + pydocstyle: + runs-on: "ubuntu-20.04" + env: + INVOKE_LOCAL: "True" + steps: + - name: "Check out repository code" + uses: "actions/checkout@v2" + - name: "Setup environment" + uses: "networktocode/gh-action-setup-poetry-environment@v2" + - name: "Linting: pydocstyle" + run: "poetry run invoke pydocstyle" + needs: + - "black" + flake8: + runs-on: "ubuntu-20.04" + env: + INVOKE_LOCAL: "True" + steps: + - name: "Check out repository code" + uses: "actions/checkout@v2" + - name: "Setup environment" + uses: "networktocode/gh-action-setup-poetry-environment@v2" + - name: "Linting: flake8" + run: "poetry run invoke flake8" + needs: + - "black" + yamllint: + runs-on: "ubuntu-20.04" + env: + INVOKE_LOCAL: "True" + steps: + - name: "Check out repository code" + uses: "actions/checkout@v2" + - name: "Setup environment" + uses: "networktocode/gh-action-setup-poetry-environment@v2" + - name: "Linting: yamllint" + run: "poetry run invoke yamllint" + needs: + - "black" + build: + strategy: + fail-fast: true + matrix: + python-version: ["3.6", "3.7", "3.8", "3.9"] + runs-on: "ubuntu-20.04" + env: + PYTHON_VER: "${{ matrix.python-version }}" + IMAGE_NAME: "netutils" + steps: + - name: "Check out repository code" + uses: "actions/checkout@v2" + - name: "Setup environment" + uses: "networktocode/gh-action-setup-poetry-environment@v2" + - name: "Create tmp docker dir" + run: "mkdir /tmp/docker" + - name: "Cache Docker images" + uses: "actions/cache@v2" + id: "cached-docker-images" + with: + path: "/tmp/docker" + key: "${{ runner.os }}-docker-${{ matrix.python-version }}-${{ hashFiles('./Dockerfile') }}" + - name: "Build Container" + if: "steps.cached-docker-images.outputs.cache-hit != 'true'" + run: "poetry run invoke build" + - name: "Show Docker images" + if: "steps.cached-docker-images.outputs.cache-hit != 'true'" + run: "docker image ls" + - name: "Save the Docker image" + if: "steps.cached-docker-images.outputs.cache-hit != 'true'" + run: "docker save `echo ${IMAGE_NAME}`:`poetry version -s`-py${{ matrix.python-version }} > /tmp/docker/netutils-py${{ matrix.python-version }}.tar" + - name: "Show files" + run: "ls -al /tmp/docker" + needs: + - "bandit" + - "pydocstyle" + - "flake8" + - "yamllint" + pylint: + strategy: + fail-fast: true + matrix: + python-version: ["3.7"] + runs-on: "ubuntu-20.04" + steps: + - name: "Check out repository code" + uses: "actions/checkout@v2" + - name: "Setup environment" + uses: "networktocode/gh-action-setup-poetry-environment@v2" + - name: "Cache Docker images" + uses: "actions/cache@v2" + id: "cached-docker-images" + with: + path: "/tmp/docker" + key: "${{ runner.os }}-docker-${{ matrix.python-version }}-${{ hashFiles('./Dockerfile') }}" + - name: "Load docker image" + run: "docker load < /tmp/docker/netutils-py${{ matrix.python-version }}.tar" + - name: "Show docker images" + run: "docker image ls" + - name: "Linting: Pylint" + run: "poetry run invoke pylint" + needs: + - "build" + pytest: + strategy: + fail-fast: true + matrix: + python-version: ["3.6", "3.7", "3.8", "3.9"] + runs-on: "ubuntu-20.04" + env: + PYTHON_VER: "${{ matrix.python-version }}" + steps: + - name: "Check out repository code" + uses: "actions/checkout@v2" + - name: "Setup environment" + uses: "networktocode/gh-action-setup-poetry-environment@v2" + - name: "Cache Docker images" + uses: "actions/cache@v2" + id: "cached-docker-images" + with: + path: "/tmp/docker" + key: "${{ runner.os }}-docker-${{ matrix.python-version }}-${{ hashFiles('./Dockerfile') }}" + - name: "Load docker image" + run: "docker load < /tmp/docker/netutils-py${{ matrix.python-version }}.tar" + - name: "Run Tests" + run: "poetry run invoke pytest" + needs: + - "pylint" + publish_gh: + name: "Publish to GitHub" + runs-on: "ubuntu-20.04" + if: "startsWith(github.ref, 'refs/tags/v')" + steps: + - name: "Check out repository code" + uses: "actions/checkout@v2" + - name: "Set up Python" + uses: "actions/setup-python@v2" + with: + python-version: "3.9" + - name: "Install Python Packages" + run: "pip install poetry" + - name: "Set env" + run: "echo RELEASE_VERSION=${GITHUB_REF:10} >> $GITHUB_ENV" + - name: "Run Poetry Version" + run: "poetry version $RELEASE_VERSION" + - name: "Run Poetry Build" + run: "poetry build" + - name: "Upload binaries to release" + uses: "svenstaro/upload-release-action@v2" + with: + repo_token: "${{ secrets.NTC_GITHUB_TOKEN }}" + file: "dist/*" + tag: "${{ github.ref }}" + overwrite: true + file_glob: true + needs: + - "pytest" + publish_pypi: + name: "Push Package to PyPI" + runs-on: "ubuntu-20.04" + if: "startsWith(github.ref, 'refs/tags/v')" + steps: + - name: "Check out repository code" + uses: "actions/checkout@v2" + - name: "Set up Python" + uses: "actions/setup-python@v2" + with: + python-version: "3.9" + - name: "Install Python Packages" + run: "pip install poetry" + - name: "Set env" + run: "echo RELEASE_VERSION=${GITHUB_REF:10} >> $GITHUB_ENV" + - name: "Run Poetry Version" + run: "poetry version $RELEASE_VERSION" + - name: "Run Poetry Build" + run: "poetry build" + - name: "Push to PyPI" + uses: "pypa/gh-action-pypi-publish@release/v1" + with: + user: "__token__" + password: "${{ secrets.PYPI_API_TOKEN }}" + needs: + - "pytest" diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index db3baf71..00000000 --- a/.travis.yml +++ /dev/null @@ -1,74 +0,0 @@ ---- -# Add additional stages in the order of execution here, and then under the job:include: key -stages: - - name: "lint" - - name: "test" - - name: "deploy_github" - if: "tag IS present" - - name: "deploy_pypi" - if: "tag IS present" - -language: "python" - -env: - global: - - "INVOKE_LOCAL=True" - matrix: - - "PYTHON_VER=3.6" - - "PYTHON_VER=3.7" - - "PYTHON_VER=3.8" - - "PYTHON_VER=3.9" -before_script: - - "pip install --upgrade pip" - - "pip install invoke poetry toml" - - "poetry config virtualenvs.create false" - - "poetry install --no-interaction" -script: - - "invoke pytest" - -jobs: - include: - - stage: "lint" - env: - - "INVOKE_LOCAL=True" - before_script: - - "pip install --upgrade pip" - - "pip install invoke poetry toml" - - "poetry config virtualenvs.create false" - - "poetry install --no-interaction" - script: - - "invoke black" - - "invoke bandit" # Bandit fails to function on > Py3.8 https://github.com/PyCQA/bandit/issues/639 - - "invoke pydocstyle" - - "invoke flake8" - - "invoke yamllint" - - "invoke pylint" - - stage: "deploy_github" - before_script: - - "pip install --upgrade pip" - - "pip install poetry" - script: - - "echo Deploying the release to GitHub" - - "poetry version $TRAVIS_TAG" - - "poetry build" - deploy: - provider: "releases" - api_key: "$GITHUB_AUTH_TOKEN" - file_glob: true - file: "dist/*" - skip_cleanup: true - "on": - all_branches: true - - stage: "deploy_pypi" - before_script: - - "pip install --upgrade pip" - - "pip install poetry" - script: - - "echo Deploying the release to PyPI" - - "poetry version $TRAVIS_TAG" - deploy: - provider: "script" - skip_cleanup: true - script: "poetry publish --build -u __token__ -p $PYPI_TOKEN" - "on": - all_branches: true From 2a9690d4401ded5b7be66f46205ca27ce5130510 Mon Sep 17 00:00:00 2001 From: Justin Drew <2396364+jdrew82@users.noreply.github.com> Date: Wed, 27 Oct 2021 09:16:42 -0500 Subject: [PATCH 09/12] Add Bandwidth (#59) * Add bandwidth conversion methods and tests --- netutils/bandwidth.py | 208 +++++++++++++++++++++++++++++++++++ netutils/utils.py | 5 + tests/unit/test_bandwidth.py | 178 ++++++++++++++++++++++++++++++ 3 files changed, 391 insertions(+) create mode 100644 netutils/bandwidth.py create mode 100644 tests/unit/test_bandwidth.py diff --git a/netutils/bandwidth.py b/netutils/bandwidth.py new file mode 100644 index 00000000..7a14bf93 --- /dev/null +++ b/netutils/bandwidth.py @@ -0,0 +1,208 @@ +"""Functions for performing bandwidth calculations.""" +import re + + +def _get_bits_mapping(): + bits_value = 0 + bits_mapping = {} + for _bit in ["bps", "Kbps", "Mbps", "Gbps", "Tbps", "Pbps", "Ebps", "Zbps"]: + bits_mapping[_bit] = {"low": bits_value} + if bits_value == 0: + bits_value = 1000 + else: + bits_value = bits_value * 1000 + bits_mapping[_bit]["high"] = bits_value + return bits_mapping + + +BITS_MAPPING = _get_bits_mapping() + + +def _get_bytes_mapping(): + bytes_value = 0 + bytes_mapping = {} + for _byte in ["Bps", "KBps", "MBps", "GBps", "TBps", "PBps", "EBps", "ZBps"]: + bytes_mapping[_byte] = {"low": bytes_value} + if bytes_value == 0: + bytes_value = 8000 + else: + bytes_value = bytes_value * 1000 + bytes_mapping[_byte]["high"] = bytes_value + return bytes_mapping + + +BYTES_MAPPING = _get_bytes_mapping() + + +def name_to_bits(speed: str) -> int: + """Method to convert a short bandwidth name to int value in bps. + + Args: + speed (str): Bandwidth to be converted like `100Gbps` to bps. + + Returns: + int: value of bandwidth to be converted to bps + + Example: + >>> from netutils.bandwidth import name_to_bits + >>> name_to_bits("10Gbps") + 10000000000 + >>> name_to_bits("33.6Kbps") + 33600 + >>> name_to_bits("2.5Gbps") + 2500000000 + """ + if not isinstance(speed, str): + raise ValueError(f"Speed of {speed} was not a valid speed representation.") + speed = speed.replace(" ", "") + match = re.match(r"([0-9.]+)([A-Z]?[bB]ps)", speed) + if not match: + raise ValueError(f"Speed of {speed} was not a valid speed representation.") + bit_speed, bit_name = match.groups() + if bit_name in BITS_MAPPING.keys(): + return int(float(bit_speed) * BITS_MAPPING[bit_name]["low"]) + if bit_name in BYTES_MAPPING.keys(): + return int(float(bit_speed) * BYTES_MAPPING[bit_name]["low"]) + raise ValueError(f"Speed of {speed} was not a valid speed representation.") + + +def name_to_bytes(speed: str) -> float: + """Method to convert a short bandwidth name to float value in Bps. + + Args: + speed (str): Bandwidth to be converted like `100GBps` to Bps. + + Returns: + float: value of bandwidth to be converted to Bps + + Example: + >>> from netutils.bandwidth import name_to_bytes + >>> name_to_bytes("10Gbps") + 1250000000.0 + >>> name_to_bytes("100Mbps") + 12500000.0 + >>> name_to_bytes("100GBps") + 100000000000.0 + """ + if not isinstance(speed, str): + raise ValueError(f"Speed of {speed} was not a valid speed representation.") + speed = speed.replace(" ", "") + match = re.match(r"([0-9.]+)([A-Z]?[bB]ps)", speed) + if not match: + raise ValueError(f"Speed of {speed} was not a valid speed representation.") + byte_speed, byte_name = match.groups() + if byte_name in BYTES_MAPPING.keys(): + return (float(byte_speed) * BYTES_MAPPING[byte_name]["low"]) / 8 + if byte_name in BITS_MAPPING.keys(): + return (float(byte_speed) * BITS_MAPPING[byte_name]["low"]) * 0.125 + raise ValueError(f"Speed of {speed} was not a valid speed representation.") + + +def bits_to_name( # pylint: disable=too-many-branches,too-many-return-statements + speed: int, nbr_decimal: int = 0 +) -> str: + """Method to convert an int value for speed int bits to the name value. + + Args: + speed (int): Speed in bits to be converted. + nbr_decimal (int, optional): Precision of end result, ie number of decimal points to round to. Defaults to 0. + + Returns: + str: Name value for speed in bits + + Example: + >>> from netutils.bandwidth import bits_to_name + >>> bits_to_name(125000) + '125.0Kbps' + >>> bits_to_name(1000000000) + '1.0Gbps' + """ + if not isinstance(speed, int): + raise ValueError(f"Speed of {speed} was not a valid speed integer.") + + for bit_type, val in BITS_MAPPING.items(): + if val["low"] <= speed < val["high"]: + try: + return f"{round(speed / val['low'], nbr_decimal)}{bit_type}" + except ZeroDivisionError: + return f"{round(speed, nbr_decimal)}{bit_type}" + raise ValueError(f"Speed of {speed} was not a valid speed representation.") + + +def bytes_to_name(speed: float, nbr_decimal: int = 0) -> str: + """Method to convert an int value for speed in bytes to the name value. + + Args: + speed (int): Speed in bytes to be converted. + nbr_decimal (int, optional): Precision of end result, ie number of decimal points to round to. Defaults to 0. + + Returns: + str: Name value for speed in bytes + + Example: + >>> from netutils.bandwidth import bytes_to_name + >>> bytes_to_name(10000.0) + '10.0KBps' + >>> bytes_to_name(10000000.0) + '10.0MBps' + """ + if not isinstance(speed, float): + raise ValueError(f"Speed of {speed} was not a valid speed.") + + byte_speed = speed * 8 + for bit_type, val in BYTES_MAPPING.items(): + if val["low"] <= speed < val["high"]: + try: + return f"{round(byte_speed / val['low'], nbr_decimal)}{bit_type}" + except ZeroDivisionError: + return f"{round(byte_speed, nbr_decimal)}{bit_type}" + raise ValueError(f"Speed of {speed} was not a valid speed representation.") + + +def name_to_name(speed: str, speed_type: str, nbr_decimal: int = 0) -> str: + """Method to convert a short bandwidth name to another bandwdth name. + + Args: + speed (str): Bandwidth to be converted like `100GBps`. + speed_type (str): Name to convert the bandwdth to like `MBps`. + nbr_decimal (int, optional): Precision of end result, ie number of decimal points to round to. Defaults to 0. + + Returns: + str: The named value which user wishes to return to. + + Example: + >>> from netutils.bandwidth import name_to_name + >>> name_to_name("10Gbps", "Kbps") + '10000000.0Kbps' + >>> name_to_name("10GBps", "Kbps") + '80000000.0Kbps' + >>> name_to_name("10KBps", "Gbps", 4) + '0.0001Gbps' + """ + if not isinstance(speed, str): + raise ValueError(f"Speed of {speed} was not a valid speed representation.") + speed = speed.replace(" ", "") + match = re.match(r"([0-9.]+)([A-Z]?[bB]ps)", speed) + if not match: + raise ValueError(f"Speed of {speed} was not a valid speed representation.") + _, name = match.groups() + # Find out if the `original` value is a bit or a byte + if name in BYTES_MAPPING.keys(): + bit_value = name_to_bytes(speed) * 8 + elif name in BITS_MAPPING.keys(): + bit_value = name_to_bits(speed) + else: + raise ValueError(f"Speed of {speed} was not a valid speed representation.") + + # Find out if the `expected` value is a bit or a byte + if speed_type in BYTES_MAPPING.keys(): + bit_multiplier = BYTES_MAPPING[speed_type]["low"] + elif speed_type in BITS_MAPPING.keys(): + bit_multiplier = BITS_MAPPING[speed_type]["low"] + else: + raise ValueError(f"Speed type of {speed_type} was not a valid type.") + + try: + return f"{round(bit_value / bit_multiplier, nbr_decimal)}{speed_type}" + except ZeroDivisionError: + return f"{round(bit_value, nbr_decimal)}{speed_type}" diff --git a/netutils/utils.py b/netutils/utils.py index f2f3de60..1456c7db 100644 --- a/netutils/utils.py +++ b/netutils/utils.py @@ -51,6 +51,11 @@ "longest_prefix_match": "route.longest_prefix_match", "vlanlist_to_config": "vlan.vlanlist_to_config", "vlanconfig_to_list": "vlan.vlanconfig_to_list", + "name_to_bits": "bandwidth.name_to_bits", + "name_to_bytes": "bandwidth.name_to_bytes", + "bits_to_name": "bandwidth.bits_to_name", + "bytes_to_name": "bandwidth.bytes_to_name", + "name_to_name": "bandwidth.name_to_name", } diff --git a/tests/unit/test_bandwidth.py b/tests/unit/test_bandwidth.py new file mode 100644 index 00000000..f256e6c6 --- /dev/null +++ b/tests/unit/test_bandwidth.py @@ -0,0 +1,178 @@ +"""Test for the Bandwidth functions.""" + +import pytest +from netutils import bandwidth + + +name_to_bits = [ + {"sent": "10Mbps", "received": 10000000}, + {"sent": "10 Mbps", "received": 10000000}, + {"sent": "1Gbps", "received": 1000000000}, + {"sent": "1 Gbps", "received": 1000000000}, + {"sent": "100Gbps", "received": 100000000000}, + {"sent": "100 Gbps", "received": 100000000000}, + {"sent": "10GBps", "received": 80000000000}, +] + + +@pytest.mark.parametrize("data", name_to_bits) +def test_name_to_bits(data): + assert bandwidth.name_to_bits(data["sent"]) == data["received"] + + +name_to_bits_exceptions = [ + {"speed": "1 Bbps"}, + {"speed": "1 Qbps"}, + {"speed": "9"}, + {"speed": "bps"}, + {"speed": 1.0}, + {"speed": ("10Kbps",)}, +] + + +@pytest.mark.parametrize("data", name_to_bits_exceptions) +def test_name_to_bits_exceptions(data): + with pytest.raises(ValueError): + assert bandwidth.name_to_bits(**data) + + +bits_to_name = [ + {"sent": {"speed": 950}, "received": "950bps"}, + {"sent": {"speed": 1000}, "received": "1.0Kbps"}, + {"sent": {"speed": 1000, "nbr_decimal": 1}, "received": "1.0Kbps"}, + {"sent": {"speed": 1000000}, "received": "1.0Mbps"}, + {"sent": {"speed": 1000000, "nbr_decimal": 1}, "received": "1.0Mbps"}, + {"sent": {"speed": 1000000000}, "received": "1.0Gbps"}, + {"sent": {"speed": 1000000000, "nbr_decimal": 1}, "received": "1.0Gbps"}, + {"sent": {"speed": 1000000000000}, "received": "1.0Tbps"}, + {"sent": {"speed": 1000000000000, "nbr_decimal": 1}, "received": "1.0Tbps"}, +] + + +@pytest.mark.parametrize("data", bits_to_name) +def test_bits_to_name(data): + assert bandwidth.bits_to_name(**data["sent"]) == data["received"] + + +bits_to_name_exceptions = [ + {"speed": "1 Bbps"}, + {"speed": "1 Qbps"}, + {"speed": "9"}, + {"speed": "bps"}, + {"speed": 1.0}, + {"speed": ("10Kbps",)}, + {"speed": -10.0}, + {"speed": 9999999999999999999999999999}, +] + + +@pytest.mark.parametrize("data", bits_to_name_exceptions) +def test_bits_to_name_exceptions(data): + with pytest.raises(ValueError): + assert bandwidth.bits_to_name(**data) + + +bytes_to_name = [ + {"sent": {"speed": 950.0}, "received": "7600.0Bps"}, + {"sent": {"speed": 1000.0}, "received": "8000.0Bps"}, + {"sent": {"speed": 1000000.0}, "received": "1000.0KBps"}, + {"sent": {"speed": 1000000000.0}, "received": "1000.0MBps"}, + {"sent": {"speed": 1000000000000.0}, "received": "1000.0GBps"}, +] + + +@pytest.mark.parametrize("data", bytes_to_name) +def test_bytes_to_name(data): + assert bandwidth.bytes_to_name(**data["sent"]) == data["received"] + + +bytes_to_name_exceptions = [ + {"speed": "1 BBps"}, + {"speed": "1 QBps"}, + {"speed": "9"}, + {"speed": "Bps"}, + {"speed": 10}, + {"speed": ("10KBps",)}, + {"speed": -10.0}, +] + + +@pytest.mark.parametrize("data", bytes_to_name_exceptions) +def test_bytes_to_name_exceptions(data): + with pytest.raises(ValueError): + assert bandwidth.bytes_to_name(**data) + + +name_to_bytes = [ + {"sent": "10MBps", "received": 10000000.0}, + {"sent": "10 MBps", "received": 10000000.0}, + {"sent": "1GBps", "received": 1000000000.0}, + {"sent": "1 GBps", "received": 1000000000.0}, + {"sent": "2.5GBps", "received": 2500000000.0}, + {"sent": "2.5 GBps", "received": 2500000000.0}, + {"sent": "100GBps", "received": 100000000000.0}, + {"sent": "100 GBps", "received": 100000000000.0}, + {"sent": "1TBps", "received": 1000000000000.0}, + {"sent": "1 TBps", "received": 1000000000000.0}, + {"sent": "10Gbps", "received": 1250000000.0}, +] + + +@pytest.mark.parametrize("data", name_to_bytes) +def test_name_to_bytes(data): + assert bandwidth.name_to_bytes(data["sent"]) == data["received"] + + +name_to_bytes_exceptions = [ + {"speed": "1 BBps"}, + {"speed": "1 QBps"}, + {"speed": "9"}, + {"speed": "Bps"}, + {"speed": 1.0}, + {"speed": ("10Kbps",)}, + {"speed": "1 bbps"}, + {"speed": "kBps"}, +] + + +@pytest.mark.parametrize("data", name_to_bytes_exceptions) +def test_name_to_bytes_exceptions(data): + with pytest.raises(ValueError): + assert bandwidth.name_to_bytes(**data) + + +name_to_name = [ + {"sent": {"speed": "10Mbps", "speed_type": "Kbps"}, "received": "10000.0Kbps"}, + {"sent": {"speed": "10Mbps", "speed_type": "KBps"}, "received": "1250.0KBps"}, + {"sent": {"speed": "10MBps", "speed_type": "Kbps"}, "received": "80000.0Kbps"}, + {"sent": {"speed": "10MBps", "speed_type": "KBps"}, "received": "10000.0KBps"}, + {"sent": {"speed": "1Gbps", "speed_type": "Kbps"}, "received": "1000000.0Kbps"}, + {"sent": {"speed": "1Gbps", "speed_type": "KBps"}, "received": "125000.0KBps"}, + {"sent": {"speed": "2.5Gbps", "speed_type": "Kbps"}, "received": "2500000.0Kbps"}, + {"sent": {"speed": "2.5Gbps", "speed_type": "KBps"}, "received": "312500.0KBps"}, +] + + +@pytest.mark.parametrize("data", name_to_name) +def test_name_to_name(data): + assert bandwidth.name_to_name(**data["sent"]) == data["received"] + + +named_exceptions = [ + {"speed": "1 Bbps", "speed_type": "Kbps"}, + {"speed": "1 Qbps", "speed_type": "KBps"}, + {"speed": "9", "speed_type": "bps"}, + {"speed": "bps", "speed_type": "Mbps"}, + {"speed": 10.0, "speed_type": "Mbps"}, + {"speed": (10.0,), "speed_type": "Mbps"}, + {"speed": 10, "speed_type": "Mbps"}, + {"speed": "-bps", "speed_type": "Mbps"}, + {"speed": "10Mbps", "speed_type": "Qbps"}, + {"speed": "10kBps", "speed_type": "Gbps"}, +] + + +@pytest.mark.parametrize("data", named_exceptions) +def test_name_to_name_exceptions(data): + with pytest.raises(ValueError): + assert bandwidth.name_to_name(**data) From b314c992e40ac4dd3b2db60b6786ca92d5b32a49 Mon Sep 17 00:00:00 2001 From: Jeff Kala <48843785+jeffkala@users.noreply.github.com> Date: Fri, 12 Nov 2021 11:08:28 -0600 Subject: [PATCH 10/12] Add Fortinet Fortios Parser support (#68) * add parser support for fortinet --- docs/source/netutils/configs/index.rst | 9 +- netutils/config/compliance.py | 1 + netutils/config/parser.py | 119 +++ poetry.lock | 469 +++++++----- pyproject.toml | 4 +- .../fortinet_fortios/fortios_backup.txt | 313 ++++++++ .../fortinet_fortios/fortios_feature.py | 4 + .../fortinet_fortios/fortios_intended.txt | 313 ++++++++ .../fortinet_fortios/fortios_received.json | 22 + .../fortinet_fortios/fortios_full_received.py | 716 ++++++++++++++++++ .../fortinet_fortios/fortios_full_sent.txt | 466 ++++++++++++ tests/unit/test_compliance.py | 3 +- tests/unit/test_parser.py | 3 +- 13 files changed, 2230 insertions(+), 212 deletions(-) create mode 100644 tests/unit/mock/config/compliance/compliance/fortinet_fortios/fortios_backup.txt create mode 100644 tests/unit/mock/config/compliance/compliance/fortinet_fortios/fortios_feature.py create mode 100644 tests/unit/mock/config/compliance/compliance/fortinet_fortios/fortios_intended.txt create mode 100644 tests/unit/mock/config/compliance/compliance/fortinet_fortios/fortios_received.json create mode 100644 tests/unit/mock/config/parser/fortinet_fortios/fortios_full_received.py create mode 100644 tests/unit/mock/config/parser/fortinet_fortios/fortios_full_sent.txt diff --git a/docs/source/netutils/configs/index.rst b/docs/source/netutils/configs/index.rst index add1a649..1ae43f59 100644 --- a/docs/source/netutils/configs/index.rst +++ b/docs/source/netutils/configs/index.rst @@ -9,4 +9,11 @@ Configs :members: .. automodule:: netutils.config.parser - :members: \ No newline at end of file + :members: + +Edge Cases +============== + +Fortinet Fortios Parser +----------------------- +- In order to support html blocks that exist in Fortios configurations, some preprocessing is executed, this is a regex that specifically grabs everything between quotes after the 'set buffer' sub-command. It's explicitly looking for double quote followed by a newline ("\n) to end the captured data. This support for html data will not support any other html that doesn't follow this convention. diff --git a/netutils/config/compliance.py b/netutils/config/compliance.py index 599284db..84bac72d 100644 --- a/netutils/config/compliance.py +++ b/netutils/config/compliance.py @@ -11,6 +11,7 @@ "bigip_f5": parser.F5ConfigParser, "juniper_junos": parser.JunosConfigParser, "cisco_asa": parser.ASAConfigParser, + "fortinet_fortios": parser.FortinetConfigParser, } default_feature = { diff --git a/netutils/config/parser.py b/netutils/config/parser.py index d89def98..ef466b61 100644 --- a/netutils/config/parser.py +++ b/netutils/config/parser.py @@ -678,3 +678,122 @@ def build_config_relationship(self): self._update_config_lines(line) return self.config_lines + + +class FortinetConfigParser(BaseSpaceConfigParser): + """Fortinet Fortios config parser.""" + + comment_chars = [] + banner_start = [] + + def __init__(self, config): + """Create ConfigParser Object. + + Args: + config (str): The config text to parse. + """ + self.uncommon_data = self._get_uncommon_lines(config) + super(FortinetConfigParser, self).__init__(config) + + def is_end_next(self, line): # pylint: disable=no-self-use + """Determine if line has 'end' or 'next' in it. + + Args: + line (str): A config line from the device. + + Returns: + bool: True if line has 'end' or 'next', else False. + + Example: + >>> FortinetConfigParser("config system virtual-switch").is_end_next("config system virtual-switch") + False + >>> FortinetConfigParser("end").is_end_next("end") + True + >>> + """ + for end_next in ["end", "next"]: + if line.lstrip() == end_next: + return True + return False + + def _parse_out_offending(self, config): # pylint: disable=no-self-use + """Preprocess out strings that offend the normal spaced configuration syntax. + + Args: + config (str): full config as a string. + """ + # This will grab everything between quotes after the 'set buffer' sub-command. + # Its explicitly looking for "\n to end the captured data. This is to support html + # data that is supported in Fortinet config with double quotes within the html. + pattern = r"(config system replacemsg.*(\".*\")\n)(\s{4}set\sbuffer\s\"[\S\s]*?\"\n)" + return re.sub(pattern, r"\1 [\2]\n", config) + + @property + def config_lines_only(self): + """Remove spaces and comments from config lines. + + Returns: + str: The non-space and non-comment lines from ``config``. + """ + # Specific to fortinet to remove uncommon data patterns for use later in _build_nested_config. + self.config = self._parse_out_offending(self.config) + if self._config is None: + config_lines = ( + line.rstrip() + for line in self.config.splitlines() + if line and not self.is_comment(line) and not line.isspace() and not self.is_end_next(line) + ) + self._config = "\n".join(config_lines) + return self._config + + def _get_uncommon_lines(self, config): # pylint: disable=no-self-use + """Regex to find replacemsg lines which can contain html/css data. + + Args: + config (str): Original config before parsing. + + Returns: + dict: dictionary with replace message name as key, html/css data as value. + """ + pattern = r"(config system replacemsg.*\n)(\s{4}set\sbuffer\s\"[\S\s]*?\"\n)" + regex_result = re.findall(pattern, config) + result = {} + for group_match in regex_result: + result.update({group_match[0].split('"')[1]: group_match[1]}) + return result + + def _build_nested_config(self, line): + """Handle building child config sections. + + Args: + line (str): A configuration line from the configuration text. + + Returns: + str: The next top-level configuration line in the configuration text. + None: When the last line of configuration text is a nested configuration line. + + Raises: + IndexError: When the number of parents does not match the expected deindent level. + """ + if "[" in line: + line = self.uncommon_data.get(line.split('"')[1]) + self._update_config_lines(line) + for line in self.generator_config: + if not line[0].isspace(): + self._current_parents = () + self.indent_level = 0 + return line + + spaces = self.get_leading_space_count(line) + if spaces == self.indent_level: + pass + elif spaces > self.indent_level: + previous_config = self.config_lines[-1] + self._current_parents += (previous_config.config_line,) + else: + self._current_parents = self._remove_parents(line, spaces) + + if spaces != self.indent_level: + self.indent_level = spaces + + self._update_config_lines(line) diff --git a/poetry.lock b/poetry.lock index 08181176..11d6633b 100644 --- a/poetry.lock +++ b/poetry.lock @@ -6,17 +6,9 @@ category = "dev" optional = false python-versions = "*" -[[package]] -name = "appdirs" -version = "1.4.4" -description = "A small Python module for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." -category = "dev" -optional = false -python-versions = "*" - [[package]] name = "astroid" -version = "2.5.6" +version = "2.8.4" description = "An abstract syntax tree for Python with inference support." category = "dev" optional = false @@ -25,7 +17,8 @@ python-versions = "~=3.6" [package.dependencies] lazy-object-proxy = ">=1.4.0" typed-ast = {version = ">=1.4.0,<1.5", markers = "implementation_name == \"cpython\" and python_version < \"3.8\""} -wrapt = ">=1.11,<1.13" +typing-extensions = {version = ">=3.10", markers = "python_version < \"3.10\""} +wrapt = ">=1.11,<1.14" [[package]] name = "atomicwrites" @@ -77,30 +70,36 @@ stevedore = ">=1.20.0" [[package]] name = "black" -version = "20.8b1" +version = "21.10b0" description = "The uncompromising code formatter." category = "dev" optional = false -python-versions = ">=3.6" +python-versions = ">=3.6.2" [package.dependencies] -appdirs = "*" click = ">=7.1.2" dataclasses = {version = ">=0.6", markers = "python_version < \"3.7\""} mypy-extensions = ">=0.4.3" -pathspec = ">=0.6,<1" +pathspec = ">=0.9.0,<1" +platformdirs = ">=2" regex = ">=2020.1.8" -toml = ">=0.10.1" -typed-ast = ">=1.4.0" -typing-extensions = ">=3.7.4" +tomli = ">=0.2.6,<2.0.0" +typed-ast = {version = ">=1.4.2", markers = "python_version < \"3.8\""} +typing-extensions = [ + {version = ">=3.10.0.0", markers = "python_version < \"3.10\""}, + {version = "!=3.10.0.1", markers = "python_version >= \"3.10\""}, +] [package.extras] colorama = ["colorama (>=0.4.3)"] -d = ["aiohttp (>=3.3.2)", "aiohttp-cors"] +d = ["aiohttp (>=3.7.4)"] +jupyter = ["ipython (>=7.8.0)", "tokenize-rt (>=3.2.0)"] +python2 = ["typed-ast (>=1.4.3)"] +uvloop = ["uvloop (>=0.15.2)"] [[package]] name = "certifi" -version = "2021.5.30" +version = "2021.10.8" description = "Python package for providing Mozilla's CA Bundle." category = "dev" optional = false @@ -108,7 +107,7 @@ python-versions = "*" [[package]] name = "charset-normalizer" -version = "2.0.4" +version = "2.0.7" description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." category = "dev" optional = false @@ -119,7 +118,7 @@ unicode_backport = ["unicodedata2"] [[package]] name = "click" -version = "8.0.1" +version = "8.0.3" description = "Composable command line interface toolkit" category = "dev" optional = false @@ -180,38 +179,38 @@ pyflakes = ">=2.3.0,<2.4.0" [[package]] name = "gitdb" -version = "4.0.7" +version = "4.0.9" description = "Git Object Database" category = "dev" optional = false -python-versions = ">=3.4" +python-versions = ">=3.6" [package.dependencies] -smmap = ">=3.0.1,<5" +smmap = ">=3.0.1,<6" [[package]] name = "gitpython" -version = "3.1.17" +version = "3.1.20" description = "Python Git Library" category = "dev" optional = false -python-versions = ">=3.5" +python-versions = ">=3.6" [package.dependencies] gitdb = ">=4.0.1,<5" -typing-extensions = {version = ">=3.7.4.0", markers = "python_version < \"3.8\""} +typing-extensions = {version = ">=3.7.4.3", markers = "python_version < \"3.10\""} [[package]] name = "idna" -version = "2.10" +version = "3.3" description = "Internationalized Domain Names in Applications (IDNA)" category = "dev" optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +python-versions = ">=3.5" [[package]] name = "imagesize" -version = "1.2.0" +version = "1.3.0" description = "Getting image size from png/jpeg/jpeg2000/gif file" category = "dev" optional = false @@ -219,7 +218,7 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" [[package]] name = "importlib-metadata" -version = "4.5.0" +version = "4.8.2" description = "Read metadata from Python packages" category = "dev" optional = false @@ -231,7 +230,8 @@ zipp = ">=0.5" [package.extras] docs = ["sphinx", "jaraco.packaging (>=8.2)", "rst.linker (>=1.9)"] -testing = ["pytest (>=4.6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytest-cov", "pytest-enabler (>=1.0.1)", "packaging", "pep517", "pyfakefs", "flufl.flake8", "pytest-black (>=0.3.7)", "pytest-mypy", "importlib-resources (>=1.3)"] +perf = ["ipython"] +testing = ["pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytest-cov", "pytest-enabler (>=1.0.1)", "packaging", "pep517", "pyfakefs", "flufl.flake8", "pytest-perf (>=0.9.2)", "pytest-black (>=0.3.7)", "pytest-mypy", "importlib-resources (>=1.3)"] [[package]] name = "iniconfig" @@ -243,7 +243,7 @@ python-versions = "*" [[package]] name = "invoke" -version = "1.5.0" +version = "1.6.0" description = "Pythonic task execution" category = "dev" optional = false @@ -251,20 +251,21 @@ python-versions = "*" [[package]] name = "isort" -version = "5.8.0" +version = "5.10.1" description = "A Python utility / library to sort Python imports." category = "dev" optional = false -python-versions = ">=3.6,<4.0" +python-versions = ">=3.6.1,<4.0" [package.extras] pipfile_deprecated_finder = ["pipreqs", "requirementslib"] requirements_deprecated_finder = ["pipreqs", "pip-api"] colors = ["colorama (>=0.4.3,<0.5.0)"] +plugins = ["setuptools"] [[package]] name = "jinja2" -version = "3.0.1" +version = "3.0.3" description = "A very fast and expressive template engine." category = "dev" optional = false @@ -286,7 +287,7 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*" [[package]] name = "m2r2" -version = "0.2.7" +version = "0.2.8" description = "Markdown and reStructuredText in a single file." category = "dev" optional = false @@ -330,52 +331,65 @@ python-versions = "*" [[package]] name = "packaging" -version = "20.9" +version = "21.2" description = "Core utilities for Python packages" category = "dev" optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +python-versions = ">=3.6" [package.dependencies] -pyparsing = ">=2.0.2" +pyparsing = ">=2.0.2,<3" [[package]] name = "pathspec" -version = "0.8.1" +version = "0.9.0" description = "Utility library for gitignore style pattern matching of file paths." category = "dev" optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7" [[package]] name = "pbr" -version = "5.6.0" +version = "5.7.0" description = "Python Build Reasonableness" category = "dev" optional = false python-versions = ">=2.6" +[[package]] +name = "platformdirs" +version = "2.4.0" +description = "A small Python module for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." +category = "dev" +optional = false +python-versions = ">=3.6" + +[package.extras] +docs = ["Sphinx (>=4)", "furo (>=2021.7.5b38)", "proselint (>=0.10.2)", "sphinx-autodoc-typehints (>=1.12)"] +test = ["appdirs (==1.4.4)", "pytest (>=6)", "pytest-cov (>=2.7)", "pytest-mock (>=3.6)"] + [[package]] name = "pluggy" -version = "0.13.1" +version = "1.0.0" description = "plugin and hook calling mechanisms for python" category = "dev" optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +python-versions = ">=3.6" [package.dependencies] importlib-metadata = {version = ">=0.12", markers = "python_version < \"3.8\""} [package.extras] dev = ["pre-commit", "tox"] +testing = ["pytest", "pytest-benchmark"] [[package]] name = "py" -version = "1.10.0" +version = "1.11.0" description = "library with cross-python path, ini-parsing, io, code, log facilities" category = "dev" optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" [[package]] name = "pycodestyle" @@ -406,7 +420,7 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" [[package]] name = "pygments" -version = "2.9.0" +version = "2.10.0" description = "Pygments is a syntax highlighting package written in Python." category = "dev" optional = false @@ -414,18 +428,20 @@ python-versions = ">=3.5" [[package]] name = "pylint" -version = "2.8.3" +version = "2.11.1" description = "python code static checker" category = "dev" optional = false python-versions = "~=3.6" [package.dependencies] -astroid = "2.5.6" +astroid = ">=2.8.0,<2.9" colorama = {version = "*", markers = "sys_platform == \"win32\""} isort = ">=4.2.5,<6" mccabe = ">=0.6,<0.7" +platformdirs = ">=2.2.0" toml = ">=0.7.1" +typing-extensions = {version = ">=3.10.0", markers = "python_version < \"3.10\""} [[package]] name = "pyparsing" @@ -437,7 +453,7 @@ python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" [[package]] name = "pytest" -version = "6.2.4" +version = "6.2.5" description = "pytest: simple powerful testing with Python" category = "dev" optional = false @@ -450,7 +466,7 @@ colorama = {version = "*", markers = "sys_platform == \"win32\""} importlib-metadata = {version = ">=0.12", markers = "python_version < \"3.8\""} iniconfig = "*" packaging = "*" -pluggy = ">=0.12,<1.0.0a1" +pluggy = ">=0.12,<2.0" py = ">=1.8.2" toml = "*" @@ -459,7 +475,7 @@ testing = ["argcomplete", "hypothesis (>=3.56)", "mock", "nose", "requests", "xm [[package]] name = "pytz" -version = "2021.1" +version = "2021.3" description = "World timezone definitions, modern and historical" category = "dev" optional = false @@ -475,7 +491,7 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*" [[package]] name = "regex" -version = "2021.4.4" +version = "2021.11.10" description = "Alternative regular expression module, to replace re." category = "dev" optional = false @@ -509,11 +525,11 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" [[package]] name = "smmap" -version = "4.0.0" +version = "5.0.0" description = "A pure Python implementation of a sliding window memory map manager" category = "dev" optional = false -python-versions = ">=3.5" +python-versions = ">=3.6" [[package]] name = "snowballstemmer" @@ -525,7 +541,7 @@ python-versions = "*" [[package]] name = "sphinx" -version = "4.0.2" +version = "4.3.0" description = "Python documentation generator" category = "dev" optional = false @@ -544,14 +560,14 @@ requests = ">=2.5.0" snowballstemmer = ">=1.1" sphinxcontrib-applehelp = "*" sphinxcontrib-devhelp = "*" -sphinxcontrib-htmlhelp = "*" +sphinxcontrib-htmlhelp = ">=2.0.0" sphinxcontrib-jsmath = "*" sphinxcontrib-qthelp = "*" -sphinxcontrib-serializinghtml = "*" +sphinxcontrib-serializinghtml = ">=1.1.5" [package.extras] docs = ["sphinxcontrib-websupport"] -lint = ["flake8 (>=3.5.0)", "isort", "mypy (>=0.800)", "docutils-stubs"] +lint = ["flake8 (>=3.5.0)", "isort", "mypy (>=0.900)", "docutils-stubs", "types-typed-ast", "types-pkg-resources", "types-requests"] test = ["pytest", "pytest-cov", "html5lib", "cython", "typed-ast"] [[package]] @@ -642,7 +658,7 @@ test = ["pytest"] [[package]] name = "stevedore" -version = "3.3.0" +version = "3.5.0" description = "Manage dynamic plugins for Python applications" category = "dev" optional = false @@ -660,6 +676,14 @@ category = "dev" optional = false python-versions = "*" +[[package]] +name = "tomli" +version = "1.2.2" +description = "A lil' TOML parser" +category = "dev" +optional = false +python-versions = ">=3.6" + [[package]] name = "typed-ast" version = "1.4.3" @@ -670,7 +694,7 @@ python-versions = "*" [[package]] name = "typing-extensions" -version = "3.10.0.0" +version = "3.10.0.2" description = "Backported and Experimental Type Hints for Python 3.5+" category = "dev" optional = false @@ -678,7 +702,7 @@ python-versions = "*" [[package]] name = "urllib3" -version = "1.26.5" +version = "1.26.7" description = "HTTP library with thread-safe connection pooling, file post, and more." category = "dev" optional = false @@ -691,19 +715,19 @@ socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"] [[package]] name = "wrapt" -version = "1.12.1" +version = "1.13.3" description = "Module for decorators, wrappers and monkey patching." category = "dev" optional = false -python-versions = "*" +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7" [[package]] name = "yamllint" -version = "1.26.1" +version = "1.26.3" description = "A linter for YAML files." category = "dev" optional = false -python-versions = ">=3.5.*" +python-versions = ">=3.5" [package.dependencies] pathspec = ">=0.5.3" @@ -711,7 +735,7 @@ pyyaml = "*" [[package]] name = "zipp" -version = "3.4.1" +version = "3.6.0" description = "Backport of pathlib-compatible object wrapper for zip files" category = "dev" optional = false @@ -719,25 +743,21 @@ python-versions = ">=3.6" [package.extras] docs = ["sphinx", "jaraco.packaging (>=8.2)", "rst.linker (>=1.9)"] -testing = ["pytest (>=4.6)", "pytest-checkdocs (>=1.2.3)", "pytest-flake8", "pytest-cov", "pytest-enabler", "jaraco.itertools", "func-timeout", "pytest-black (>=0.3.7)", "pytest-mypy"] +testing = ["pytest (>=4.6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytest-cov", "pytest-enabler (>=1.0.1)", "jaraco.itertools", "func-timeout", "pytest-black (>=0.3.7)", "pytest-mypy"] [metadata] lock-version = "1.1" -python-versions = "^3.6" -content-hash = "ac1fa9891d26160472f643c5d03a53392d54ab68ba65c18784ae90f851d8f52a" +python-versions = "^3.6.2" +content-hash = "301527ceb9222d6763cfdd769ecb38da08335aef803357bcfb27c574a91f12fb" [metadata.files] alabaster = [ {file = "alabaster-0.7.12-py2.py3-none-any.whl", hash = "sha256:446438bdcca0e05bd45ea2de1668c1d9b032e1a9154c2c259092d77031ddd359"}, {file = "alabaster-0.7.12.tar.gz", hash = "sha256:a661d72d58e6ea8a57f7a86e37d86716863ee5e92788398526d58b26a4e4dc02"}, ] -appdirs = [ - {file = "appdirs-1.4.4-py2.py3-none-any.whl", hash = "sha256:a841dacd6b99318a741b166adb07e19ee71a274450e68237b4650ca1055ab128"}, - {file = "appdirs-1.4.4.tar.gz", hash = "sha256:7d5d0167b2b1ba821647616af46a749d1c653740dd0d2415100fe26e27afdf41"}, -] astroid = [ - {file = "astroid-2.5.6-py3-none-any.whl", hash = "sha256:4db03ab5fc3340cf619dbc25e42c2cc3755154ce6009469766d7143d1fc2ee4e"}, - {file = "astroid-2.5.6.tar.gz", hash = "sha256:8a398dfce302c13f14bab13e2b14fe385d32b73f4e4853b9bdfb64598baa1975"}, + {file = "astroid-2.8.4-py3-none-any.whl", hash = "sha256:0755c998e7117078dcb7d0bda621391dd2a85da48052d948c7411ab187325346"}, + {file = "astroid-2.8.4.tar.gz", hash = "sha256:1e83a69fd51b013ebf5912d26b9338d6643a55fec2f20c787792680610eed4a2"}, ] atomicwrites = [ {file = "atomicwrites-1.4.0-py2.py3-none-any.whl", hash = "sha256:6d1784dea7c0c8d4a5172b6c620f40b6e4cbfdf96d783691f2e1302a7b88e197"}, @@ -756,19 +776,20 @@ bandit = [ {file = "bandit-1.7.0.tar.gz", hash = "sha256:8a4c7415254d75df8ff3c3b15cfe9042ecee628a1e40b44c15a98890fbfc2608"}, ] black = [ - {file = "black-20.8b1.tar.gz", hash = "sha256:1c02557aa099101b9d21496f8a914e9ed2222ef70336404eeeac8edba836fbea"}, + {file = "black-21.10b0-py3-none-any.whl", hash = "sha256:6eb7448da9143ee65b856a5f3676b7dda98ad9abe0f87fce8c59291f15e82a5b"}, + {file = "black-21.10b0.tar.gz", hash = "sha256:a9952229092e325fe5f3dae56d81f639b23f7131eb840781947e4b2886030f33"}, ] certifi = [ - {file = "certifi-2021.5.30-py2.py3-none-any.whl", hash = "sha256:50b1e4f8446b06f41be7dd6338db18e0990601dce795c2b1686458aa7e8fa7d8"}, - {file = "certifi-2021.5.30.tar.gz", hash = "sha256:2bbf76fd432960138b3ef6dda3dde0544f27cbf8546c458e60baf371917ba9ee"}, + {file = "certifi-2021.10.8-py2.py3-none-any.whl", hash = "sha256:d62a0163eb4c2344ac042ab2bdf75399a71a2d8c7d47eac2e2ee91b9d6339569"}, + {file = "certifi-2021.10.8.tar.gz", hash = "sha256:78884e7c1d4b00ce3cea67b44566851c4343c120abd683433ce934a68ea58872"}, ] charset-normalizer = [ - {file = "charset-normalizer-2.0.4.tar.gz", hash = "sha256:f23667ebe1084be45f6ae0538e4a5a865206544097e4e8bbcacf42cd02a348f3"}, - {file = "charset_normalizer-2.0.4-py3-none-any.whl", hash = "sha256:0c8911edd15d19223366a194a513099a302055a962bca2cec0f54b8b63175d8b"}, + {file = "charset-normalizer-2.0.7.tar.gz", hash = "sha256:e019de665e2bcf9c2b64e2e5aa025fa991da8720daa3c1138cadd2fd1856aed0"}, + {file = "charset_normalizer-2.0.7-py3-none-any.whl", hash = "sha256:f7af805c321bfa1ce6714c51f254e0d5bb5e5834039bc17db7ebe3a4cec9492b"}, ] click = [ - {file = "click-8.0.1-py3-none-any.whl", hash = "sha256:fba402a4a47334742d782209a7c79bc448911afe1149d07bdabdf480b3e2f4b6"}, - {file = "click-8.0.1.tar.gz", hash = "sha256:8c04c11192119b1ef78ea049e0a6f0463e4c48ef00a30160c704337586f3ad7a"}, + {file = "click-8.0.3-py3-none-any.whl", hash = "sha256:353f466495adaeb40b6b5f592f9f91cb22372351c84caeb068132442a4518ef3"}, + {file = "click-8.0.3.tar.gz", hash = "sha256:410e932b050f5eed773c4cda94de75971c89cdb3155a72a0831139a79e5ecb5b"}, ] colorama = [ {file = "colorama-0.4.4-py2.py3-none-any.whl", hash = "sha256:9f47eda37229f68eee03b24b9748937c7dc3868f906e8ba69fbcbdd3bc5dc3e2"}, @@ -841,41 +862,41 @@ flake8 = [ {file = "flake8-3.9.2.tar.gz", hash = "sha256:07528381786f2a6237b061f6e96610a4167b226cb926e2aa2b6b1d78057c576b"}, ] gitdb = [ - {file = "gitdb-4.0.7-py3-none-any.whl", hash = "sha256:6c4cc71933456991da20917998acbe6cf4fb41eeaab7d6d67fbc05ecd4c865b0"}, - {file = "gitdb-4.0.7.tar.gz", hash = "sha256:96bf5c08b157a666fec41129e6d327235284cca4c81e92109260f353ba138005"}, + {file = "gitdb-4.0.9-py3-none-any.whl", hash = "sha256:8033ad4e853066ba6ca92050b9df2f89301b8fc8bf7e9324d412a63f8bf1a8fd"}, + {file = "gitdb-4.0.9.tar.gz", hash = "sha256:bac2fd45c0a1c9cf619e63a90d62bdc63892ef92387424b855792a6cabe789aa"}, ] gitpython = [ - {file = "GitPython-3.1.17-py3-none-any.whl", hash = "sha256:29fe82050709760081f588dd50ce83504feddbebdc4da6956d02351552b1c135"}, - {file = "GitPython-3.1.17.tar.gz", hash = "sha256:ee24bdc93dce357630764db659edaf6b8d664d4ff5447ccfeedd2dc5c253f41e"}, + {file = "GitPython-3.1.20-py3-none-any.whl", hash = "sha256:b1e1c269deab1b08ce65403cf14e10d2ef1f6c89e33ea7c5e5bb0222ea593b8a"}, + {file = "GitPython-3.1.20.tar.gz", hash = "sha256:df0e072a200703a65387b0cfdf0466e3bab729c0458cf6b7349d0e9877636519"}, ] idna = [ - {file = "idna-2.10-py2.py3-none-any.whl", hash = "sha256:b97d804b1e9b523befed77c48dacec60e6dcb0b5391d57af6a65a312a90648c0"}, - {file = "idna-2.10.tar.gz", hash = "sha256:b307872f855b18632ce0c21c5e45be78c0ea7ae4c15c828c20788b26921eb3f6"}, + {file = "idna-3.3-py3-none-any.whl", hash = "sha256:84d9dd047ffa80596e0f246e2eab0b391788b0503584e8945f2368256d2735ff"}, + {file = "idna-3.3.tar.gz", hash = "sha256:9d643ff0a55b762d5cdb124b8eaa99c66322e2157b69160bc32796e824360e6d"}, ] imagesize = [ - {file = "imagesize-1.2.0-py2.py3-none-any.whl", hash = "sha256:6965f19a6a2039c7d48bca7dba2473069ff854c36ae6f19d2cde309d998228a1"}, - {file = "imagesize-1.2.0.tar.gz", hash = "sha256:b1f6b5a4eab1f73479a50fb79fcf729514a900c341d8503d62a62dbc4127a2b1"}, + {file = "imagesize-1.3.0-py2.py3-none-any.whl", hash = "sha256:1db2f82529e53c3e929e8926a1fa9235aa82d0bd0c580359c67ec31b2fddaa8c"}, + {file = "imagesize-1.3.0.tar.gz", hash = "sha256:cd1750d452385ca327479d45b64d9c7729ecf0b3969a58148298c77092261f9d"}, ] importlib-metadata = [ - {file = "importlib_metadata-4.5.0-py3-none-any.whl", hash = "sha256:833b26fb89d5de469b24a390e9df088d4e52e4ba33b01dc5e0e4f41b81a16c00"}, - {file = "importlib_metadata-4.5.0.tar.gz", hash = "sha256:b142cc1dd1342f31ff04bb7d022492b09920cb64fed867cd3ea6f80fe3ebd139"}, + {file = "importlib_metadata-4.8.2-py3-none-any.whl", hash = "sha256:53ccfd5c134223e497627b9815d5030edf77d2ed573922f7a0b8f8bb81a1c100"}, + {file = "importlib_metadata-4.8.2.tar.gz", hash = "sha256:75bdec14c397f528724c1bfd9709d660b33a4d2e77387a3358f20b848bb5e5fb"}, ] iniconfig = [ {file = "iniconfig-1.1.1-py2.py3-none-any.whl", hash = "sha256:011e24c64b7f47f6ebd835bb12a743f2fbe9a26d4cecaa7f53bc4f35ee9da8b3"}, {file = "iniconfig-1.1.1.tar.gz", hash = "sha256:bc3af051d7d14b2ee5ef9969666def0cd1a000e121eaea580d4a313df4b37f32"}, ] invoke = [ - {file = "invoke-1.5.0-py2-none-any.whl", hash = "sha256:da7c2d0be71be83ffd6337e078ef9643f41240024d6b2659e7b46e0b251e339f"}, - {file = "invoke-1.5.0-py3-none-any.whl", hash = "sha256:7e44d98a7dc00c91c79bac9e3007276965d2c96884b3c22077a9f04042bd6d90"}, - {file = "invoke-1.5.0.tar.gz", hash = "sha256:f0c560075b5fb29ba14dad44a7185514e94970d1b9d57dcd3723bec5fed92650"}, + {file = "invoke-1.6.0-py2-none-any.whl", hash = "sha256:e6c9917a1e3e73e7ea91fdf82d5f151ccfe85bf30cc65cdb892444c02dbb5f74"}, + {file = "invoke-1.6.0-py3-none-any.whl", hash = "sha256:769e90caeb1bd07d484821732f931f1ad8916a38e3f3e618644687fc09cb6317"}, + {file = "invoke-1.6.0.tar.gz", hash = "sha256:374d1e2ecf78981da94bfaf95366216aaec27c2d6a7b7d5818d92da55aa258d3"}, ] isort = [ - {file = "isort-5.8.0-py3-none-any.whl", hash = "sha256:2bb1680aad211e3c9944dbce1d4ba09a989f04e238296c87fe2139faa26d655d"}, - {file = "isort-5.8.0.tar.gz", hash = "sha256:0a943902919f65c5684ac4e0154b1ad4fac6dcaa5d9f3426b732f1c8b5419be6"}, + {file = "isort-5.10.1-py3-none-any.whl", hash = "sha256:6f62d78e2f89b4500b080fe3a81690850cd254227f27f75c3a0c491a1f351ba7"}, + {file = "isort-5.10.1.tar.gz", hash = "sha256:e8443a5e7a020e9d7f97f1d7d9cd17c88bcb3bc7e218bf9cf5095fe550be2951"}, ] jinja2 = [ - {file = "Jinja2-3.0.1-py3-none-any.whl", hash = "sha256:1f06f2da51e7b56b8f238affdd6b4e2c61e39598a378cc49345bc1bd42a978a4"}, - {file = "Jinja2-3.0.1.tar.gz", hash = "sha256:703f484b47a6af502e743c9122595cc812b0271f661722403114f71a79d0f5a4"}, + {file = "Jinja2-3.0.3-py3-none-any.whl", hash = "sha256:077ce6014f7b40d03b47d1f1ca4b0fc8328a692bd284016f806ed0eaca390ad8"}, + {file = "Jinja2-3.0.3.tar.gz", hash = "sha256:611bb273cd68f3b993fabdc4064fc858c5b47a973cb5aa7999ec1ba405c87cd7"}, ] lazy-object-proxy = [ {file = "lazy-object-proxy-1.6.0.tar.gz", hash = "sha256:489000d368377571c6f982fba6497f2aa13c6d1facc40660963da62f5c379726"}, @@ -902,26 +923,16 @@ lazy-object-proxy = [ {file = "lazy_object_proxy-1.6.0-cp39-cp39-win_amd64.whl", hash = "sha256:f5144c75445ae3ca2057faac03fda5a902eff196702b0a24daf1d6ce0650514b"}, ] m2r2 = [ - {file = "m2r2-0.2.7-py3-none-any.whl", hash = "sha256:a1b87b00f4d5163a3616253fca4c045f8351818cd803ba2e24af8897442a0eae"}, - {file = "m2r2-0.2.7.tar.gz", hash = "sha256:fbf72ade9f648d41658f97c5267687431612451f36b65761a138fbc276294df5"}, + {file = "m2r2-0.2.8-py3-none-any.whl", hash = "sha256:613934a5d02999574c0256407e6a0e71f8c436f2d2757d6e43d52d2faf8dff1c"}, + {file = "m2r2-0.2.8.tar.gz", hash = "sha256:ca39e1db74991818d667c7367e4fc2de13ecefd2a04d69d83b0ffa76d20d7e29"}, ] markupsafe = [ - {file = "MarkupSafe-2.0.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:d8446c54dc28c01e5a2dbac5a25f071f6653e6e40f3a8818e8b45d790fe6ef53"}, - {file = "MarkupSafe-2.0.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:36bc903cbb393720fad60fc28c10de6acf10dc6cc883f3e24ee4012371399a38"}, - {file = "MarkupSafe-2.0.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2d7d807855b419fc2ed3e631034685db6079889a1f01d5d9dac950f764da3dad"}, - {file = "MarkupSafe-2.0.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:add36cb2dbb8b736611303cd3bfcee00afd96471b09cda130da3581cbdc56a6d"}, - {file = "MarkupSafe-2.0.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:168cd0a3642de83558a5153c8bd34f175a9a6e7f6dc6384b9655d2697312a646"}, - {file = "MarkupSafe-2.0.1-cp310-cp310-win32.whl", hash = "sha256:99df47edb6bda1249d3e80fdabb1dab8c08ef3975f69aed437cb69d0a5de1e28"}, - {file = "MarkupSafe-2.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:e0f138900af21926a02425cf736db95be9f4af72ba1bb21453432a07f6082134"}, {file = "MarkupSafe-2.0.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:f9081981fe268bd86831e5c75f7de206ef275defcb82bc70740ae6dc507aee51"}, {file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:0955295dd5eec6cb6cc2fe1698f4c6d84af2e92de33fbcac4111913cd100a6ff"}, {file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:0446679737af14f45767963a1a9ef7620189912317d095f2d9ffa183a4d25d2b"}, {file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:f826e31d18b516f653fe296d967d700fddad5901ae07c622bb3705955e1faa94"}, {file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:fa130dd50c57d53368c9d59395cb5526eda596d3ffe36666cd81a44d56e48872"}, {file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:905fec760bd2fa1388bb5b489ee8ee5f7291d692638ea5f67982d968366bef9f"}, - {file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bf5d821ffabf0ef3533c39c518f3357b171a1651c1ff6827325e4489b0e46c3c"}, - {file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:0d4b31cc67ab36e3392bbf3862cfbadac3db12bdd8b02a2731f509ed5b829724"}, - {file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:baa1a4e8f868845af802979fcdbf0bb11f94f1cb7ced4c4b8a351bb60d108145"}, {file = "MarkupSafe-2.0.1-cp36-cp36m-win32.whl", hash = "sha256:6c4ca60fa24e85fe25b912b01e62cb969d69a23a5d5867682dd3e80b5b02581d"}, {file = "MarkupSafe-2.0.1-cp36-cp36m-win_amd64.whl", hash = "sha256:b2f4bf27480f5e5e8ce285a8c8fd176c0b03e93dcc6646477d4630e83440c6a9"}, {file = "MarkupSafe-2.0.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:0717a7390a68be14b8c793ba258e075c6f4ca819f15edfc2a3a027c823718567"}, @@ -930,21 +941,14 @@ markupsafe = [ {file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:d7f9850398e85aba693bb640262d3611788b1f29a79f0c93c565694658f4071f"}, {file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:6a7fae0dd14cf60ad5ff42baa2e95727c3d81ded453457771d02b7d2b3f9c0c2"}, {file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:b7f2d075102dc8c794cbde1947378051c4e5180d52d276987b8d28a3bd58c17d"}, - {file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e9936f0b261d4df76ad22f8fee3ae83b60d7c3e871292cd42f40b81b70afae85"}, - {file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:2a7d351cbd8cfeb19ca00de495e224dea7e7d919659c2841bbb7f420ad03e2d6"}, - {file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:60bf42e36abfaf9aff1f50f52644b336d4f0a3fd6d8a60ca0d054ac9f713a864"}, {file = "MarkupSafe-2.0.1-cp37-cp37m-win32.whl", hash = "sha256:a30e67a65b53ea0a5e62fe23682cfe22712e01f453b95233b25502f7c61cb415"}, {file = "MarkupSafe-2.0.1-cp37-cp37m-win_amd64.whl", hash = "sha256:611d1ad9a4288cf3e3c16014564df047fe08410e628f89805e475368bd304914"}, - {file = "MarkupSafe-2.0.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:5bb28c636d87e840583ee3adeb78172efc47c8b26127267f54a9c0ec251d41a9"}, {file = "MarkupSafe-2.0.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:be98f628055368795d818ebf93da628541e10b75b41c559fdf36d104c5787066"}, {file = "MarkupSafe-2.0.1-cp38-cp38-manylinux1_i686.whl", hash = "sha256:1d609f577dc6e1aa17d746f8bd3c31aa4d258f4070d61b2aa5c4166c1539de35"}, {file = "MarkupSafe-2.0.1-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:7d91275b0245b1da4d4cfa07e0faedd5b0812efc15b702576d103293e252af1b"}, {file = "MarkupSafe-2.0.1-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:01a9b8ea66f1658938f65b93a85ebe8bc016e6769611be228d797c9d998dd298"}, {file = "MarkupSafe-2.0.1-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:47ab1e7b91c098ab893b828deafa1203de86d0bc6ab587b160f78fe6c4011f75"}, {file = "MarkupSafe-2.0.1-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:97383d78eb34da7e1fa37dd273c20ad4320929af65d156e35a5e2d89566d9dfb"}, - {file = "MarkupSafe-2.0.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6fcf051089389abe060c9cd7caa212c707e58153afa2c649f00346ce6d260f1b"}, - {file = "MarkupSafe-2.0.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:5855f8438a7d1d458206a2466bf82b0f104a3724bf96a1c781ab731e4201731a"}, - {file = "MarkupSafe-2.0.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:3dd007d54ee88b46be476e293f48c85048603f5f516008bee124ddd891398ed6"}, {file = "MarkupSafe-2.0.1-cp38-cp38-win32.whl", hash = "sha256:023cb26ec21ece8dc3907c0e8320058b2e0cb3c55cf9564da612bc325bed5e64"}, {file = "MarkupSafe-2.0.1-cp38-cp38-win_amd64.whl", hash = "sha256:984d76483eb32f1bcb536dc27e4ad56bba4baa70be32fa87152832cdd9db0833"}, {file = "MarkupSafe-2.0.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:2ef54abee730b502252bcdf31b10dacb0a416229b72c18b19e24a4509f273d26"}, @@ -954,9 +958,6 @@ markupsafe = [ {file = "MarkupSafe-2.0.1-cp39-cp39-manylinux2010_i686.whl", hash = "sha256:4efca8f86c54b22348a5467704e3fec767b2db12fc39c6d963168ab1d3fc9135"}, {file = "MarkupSafe-2.0.1-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:ab3ef638ace319fa26553db0624c4699e31a28bb2a835c5faca8f8acf6a5a902"}, {file = "MarkupSafe-2.0.1-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:f8ba0e8349a38d3001fae7eadded3f6606f0da5d748ee53cc1dab1d6527b9509"}, - {file = "MarkupSafe-2.0.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c47adbc92fc1bb2b3274c4b3a43ae0e4573d9fbff4f54cd484555edbf030baf1"}, - {file = "MarkupSafe-2.0.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:37205cac2a79194e3750b0af2a5720d95f786a55ce7df90c3af697bfa100eaac"}, - {file = "MarkupSafe-2.0.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:1f2ade76b9903f39aa442b4aadd2177decb66525062db244b35d71d0ee8599b6"}, {file = "MarkupSafe-2.0.1-cp39-cp39-win32.whl", hash = "sha256:10f82115e21dc0dfec9ab5c0223652f7197feb168c940f3ef61563fc2d6beb74"}, {file = "MarkupSafe-2.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:693ce3f9e70a6cf7d2fb9e6c9d8b204b6b39897a2c4a1aa65728d5ac97dcc1d8"}, {file = "MarkupSafe-2.0.1.tar.gz", hash = "sha256:594c67807fb16238b30c44bdf74f36c02cdf22d1c8cda91ef8a0ed8dabf5620a"}, @@ -974,24 +975,28 @@ mypy-extensions = [ {file = "mypy_extensions-0.4.3.tar.gz", hash = "sha256:2d82818f5bb3e369420cb3c4060a7970edba416647068eb4c5343488a6c604a8"}, ] packaging = [ - {file = "packaging-20.9-py2.py3-none-any.whl", hash = "sha256:67714da7f7bc052e064859c05c595155bd1ee9f69f76557e21f051443c20947a"}, - {file = "packaging-20.9.tar.gz", hash = "sha256:5b327ac1320dc863dca72f4514ecc086f31186744b84a230374cc1fd776feae5"}, + {file = "packaging-21.2-py3-none-any.whl", hash = "sha256:14317396d1e8cdb122989b916fa2c7e9ca8e2be9e8060a6eff75b6b7b4d8a7e0"}, + {file = "packaging-21.2.tar.gz", hash = "sha256:096d689d78ca690e4cd8a89568ba06d07ca097e3306a4381635073ca91479966"}, ] pathspec = [ - {file = "pathspec-0.8.1-py2.py3-none-any.whl", hash = "sha256:aa0cb481c4041bf52ffa7b0d8fa6cd3e88a2ca4879c533c9153882ee2556790d"}, - {file = "pathspec-0.8.1.tar.gz", hash = "sha256:86379d6b86d75816baba717e64b1a3a3469deb93bb76d613c9ce79edc5cb68fd"}, + {file = "pathspec-0.9.0-py2.py3-none-any.whl", hash = "sha256:7d15c4ddb0b5c802d161efc417ec1a2558ea2653c2e8ad9c19098201dc1c993a"}, + {file = "pathspec-0.9.0.tar.gz", hash = "sha256:e564499435a2673d586f6b2130bb5b95f04a3ba06f81b8f895b651a3c76aabb1"}, ] pbr = [ - {file = "pbr-5.6.0-py2.py3-none-any.whl", hash = "sha256:c68c661ac5cc81058ac94247278eeda6d2e6aecb3e227b0387c30d277e7ef8d4"}, - {file = "pbr-5.6.0.tar.gz", hash = "sha256:42df03e7797b796625b1029c0400279c7c34fd7df24a7d7818a1abb5b38710dd"}, + {file = "pbr-5.7.0-py2.py3-none-any.whl", hash = "sha256:60002958e459b195e8dbe61bf22bcf344eedf1b4e03a321a5414feb15566100c"}, + {file = "pbr-5.7.0.tar.gz", hash = "sha256:4651ca1445e80f2781827305de3d76b3ce53195f2227762684eb08f17bc473b7"}, +] +platformdirs = [ + {file = "platformdirs-2.4.0-py3-none-any.whl", hash = "sha256:8868bbe3c3c80d42f20156f22e7131d2fb321f5bc86a2a345375c6481a67021d"}, + {file = "platformdirs-2.4.0.tar.gz", hash = "sha256:367a5e80b3d04d2428ffa76d33f124cf11e8fff2acdaa9b43d545f5c7d661ef2"}, ] pluggy = [ - {file = "pluggy-0.13.1-py2.py3-none-any.whl", hash = "sha256:966c145cd83c96502c3c3868f50408687b38434af77734af1e9ca461a4081d2d"}, - {file = "pluggy-0.13.1.tar.gz", hash = "sha256:15b2acde666561e1298d71b523007ed7364de07029219b604cf808bfa1c765b0"}, + {file = "pluggy-1.0.0-py2.py3-none-any.whl", hash = "sha256:74134bbf457f031a36d68416e1509f34bd5ccc019f0bcc952c7b909d06b37bd3"}, + {file = "pluggy-1.0.0.tar.gz", hash = "sha256:4224373bacce55f955a878bf9cfa763c1e360858e330072059e10bad68531159"}, ] py = [ - {file = "py-1.10.0-py2.py3-none-any.whl", hash = "sha256:3b80836aa6d1feeaa108e046da6423ab8f6ceda6468545ae8d02d9d58d18818a"}, - {file = "py-1.10.0.tar.gz", hash = "sha256:21b81bda15b66ef5e1a777a21c4dcd9c20ad3efd0b3f817e7a809035269e1bd3"}, + {file = "py-1.11.0-py2.py3-none-any.whl", hash = "sha256:607c53218732647dff4acdfcd50cb62615cedf612e72d1724fb1a0cc6405b378"}, + {file = "py-1.11.0.tar.gz", hash = "sha256:51c75c4126074b472f746a24399ad32f6053d1b34b68d2fa41e558e6f4a98719"}, ] pycodestyle = [ {file = "pycodestyle-2.7.0-py2.py3-none-any.whl", hash = "sha256:514f76d918fcc0b55c6680472f0a37970994e07bbb80725808c17089be302068"}, @@ -1006,24 +1011,24 @@ pyflakes = [ {file = "pyflakes-2.3.1.tar.gz", hash = "sha256:f5bc8ecabc05bb9d291eb5203d6810b49040f6ff446a756326104746cc00c1db"}, ] pygments = [ - {file = "Pygments-2.9.0-py3-none-any.whl", hash = "sha256:d66e804411278594d764fc69ec36ec13d9ae9147193a1740cd34d272ca383b8e"}, - {file = "Pygments-2.9.0.tar.gz", hash = "sha256:a18f47b506a429f6f4b9df81bb02beab9ca21d0a5fee38ed15aef65f0545519f"}, + {file = "Pygments-2.10.0-py3-none-any.whl", hash = "sha256:b8e67fe6af78f492b3c4b3e2970c0624cbf08beb1e493b2c99b9fa1b67a20380"}, + {file = "Pygments-2.10.0.tar.gz", hash = "sha256:f398865f7eb6874156579fdf36bc840a03cab64d1cde9e93d68f46a425ec52c6"}, ] pylint = [ - {file = "pylint-2.8.3-py3-none-any.whl", hash = "sha256:792b38ff30903884e4a9eab814ee3523731abd3c463f3ba48d7b627e87013484"}, - {file = "pylint-2.8.3.tar.gz", hash = "sha256:0a049c5d47b629d9070c3932d13bff482b12119b6a241a93bc460b0be16953c8"}, + {file = "pylint-2.11.1-py3-none-any.whl", hash = "sha256:0f358e221c45cbd4dad2a1e4b883e75d28acdcccd29d40c76eb72b307269b126"}, + {file = "pylint-2.11.1.tar.gz", hash = "sha256:2c9843fff1a88ca0ad98a256806c82c5a8f86086e7ccbdb93297d86c3f90c436"}, ] pyparsing = [ {file = "pyparsing-2.4.7-py2.py3-none-any.whl", hash = "sha256:ef9d7589ef3c200abe66653d3f1ab1033c3c419ae9b9bdb1240a85b024efc88b"}, {file = "pyparsing-2.4.7.tar.gz", hash = "sha256:c203ec8783bf771a155b207279b9bccb8dea02d8f0c9e5f8ead507bc3246ecc1"}, ] pytest = [ - {file = "pytest-6.2.4-py3-none-any.whl", hash = "sha256:91ef2131a9bd6be8f76f1f08eac5c5317221d6ad1e143ae03894b862e8976890"}, - {file = "pytest-6.2.4.tar.gz", hash = "sha256:50bcad0a0b9c5a72c8e4e7c9855a3ad496ca6a881a3641b4260605450772c54b"}, + {file = "pytest-6.2.5-py3-none-any.whl", hash = "sha256:7310f8d27bc79ced999e760ca304d69f6ba6c6649c0b60fb0e04a4a77cacc134"}, + {file = "pytest-6.2.5.tar.gz", hash = "sha256:131b36680866a76e6781d13f101efb86cf674ebb9762eb70d3082b6f29889e89"}, ] pytz = [ - {file = "pytz-2021.1-py2.py3-none-any.whl", hash = "sha256:eb10ce3e7736052ed3623d49975ce333bcd712c7bb19a58b9e2089d4057d0798"}, - {file = "pytz-2021.1.tar.gz", hash = "sha256:83a4a90894bf38e243cf052c8b58f381bfe9a7a483f6a9cab140bc7f702ac4da"}, + {file = "pytz-2021.3-py2.py3-none-any.whl", hash = "sha256:3672058bc3453457b622aab7a1c3bfd5ab0bdae451512f6cf25f64ed37f5b87c"}, + {file = "pytz-2021.3.tar.gz", hash = "sha256:acad2d8b20a1af07d4e4c9d2e9285c5ed9104354062f275f3fcd88dcef4f1326"}, ] pyyaml = [ {file = "PyYAML-5.4.1-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:3b2b1824fe7112845700f815ff6a489360226a5609b96ec2190a45e62a9fc922"}, @@ -1032,72 +1037,72 @@ pyyaml = [ {file = "PyYAML-5.4.1-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:bb4191dfc9306777bc594117aee052446b3fa88737cd13b7188d0e7aa8162185"}, {file = "PyYAML-5.4.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:6c78645d400265a062508ae399b60b8c167bf003db364ecb26dcab2bda048253"}, {file = "PyYAML-5.4.1-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:4e0583d24c881e14342eaf4ec5fbc97f934b999a6828693a99157fde912540cc"}, - {file = "PyYAML-5.4.1-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:72a01f726a9c7851ca9bfad6fd09ca4e090a023c00945ea05ba1638c09dc3347"}, - {file = "PyYAML-5.4.1-cp36-cp36m-manylinux2014_s390x.whl", hash = "sha256:895f61ef02e8fed38159bb70f7e100e00f471eae2bc838cd0f4ebb21e28f8541"}, {file = "PyYAML-5.4.1-cp36-cp36m-win32.whl", hash = "sha256:3bd0e463264cf257d1ffd2e40223b197271046d09dadf73a0fe82b9c1fc385a5"}, {file = "PyYAML-5.4.1-cp36-cp36m-win_amd64.whl", hash = "sha256:e4fac90784481d221a8e4b1162afa7c47ed953be40d31ab4629ae917510051df"}, {file = "PyYAML-5.4.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:5accb17103e43963b80e6f837831f38d314a0495500067cb25afab2e8d7a4018"}, {file = "PyYAML-5.4.1-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:e1d4970ea66be07ae37a3c2e48b5ec63f7ba6804bdddfdbd3cfd954d25a82e63"}, - {file = "PyYAML-5.4.1-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:cb333c16912324fd5f769fff6bc5de372e9e7a202247b48870bc251ed40239aa"}, - {file = "PyYAML-5.4.1-cp37-cp37m-manylinux2014_s390x.whl", hash = "sha256:fe69978f3f768926cfa37b867e3843918e012cf83f680806599ddce33c2c68b0"}, {file = "PyYAML-5.4.1-cp37-cp37m-win32.whl", hash = "sha256:dd5de0646207f053eb0d6c74ae45ba98c3395a571a2891858e87df7c9b9bd51b"}, {file = "PyYAML-5.4.1-cp37-cp37m-win_amd64.whl", hash = "sha256:08682f6b72c722394747bddaf0aa62277e02557c0fd1c42cb853016a38f8dedf"}, {file = "PyYAML-5.4.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:d2d9808ea7b4af864f35ea216be506ecec180628aced0704e34aca0b040ffe46"}, {file = "PyYAML-5.4.1-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:8c1be557ee92a20f184922c7b6424e8ab6691788e6d86137c5d93c1a6ec1b8fb"}, - {file = "PyYAML-5.4.1-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:fd7f6999a8070df521b6384004ef42833b9bd62cfee11a09bda1079b4b704247"}, - {file = "PyYAML-5.4.1-cp38-cp38-manylinux2014_s390x.whl", hash = "sha256:bfb51918d4ff3d77c1c856a9699f8492c612cde32fd3bcd344af9be34999bfdc"}, {file = "PyYAML-5.4.1-cp38-cp38-win32.whl", hash = "sha256:fa5ae20527d8e831e8230cbffd9f8fe952815b2b7dae6ffec25318803a7528fc"}, {file = "PyYAML-5.4.1-cp38-cp38-win_amd64.whl", hash = "sha256:0f5f5786c0e09baddcd8b4b45f20a7b5d61a7e7e99846e3c799b05c7c53fa696"}, {file = "PyYAML-5.4.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:294db365efa064d00b8d1ef65d8ea2c3426ac366c0c4368d930bf1c5fb497f77"}, {file = "PyYAML-5.4.1-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:74c1485f7707cf707a7aef42ef6322b8f97921bd89be2ab6317fd782c2d53183"}, - {file = "PyYAML-5.4.1-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:d483ad4e639292c90170eb6f7783ad19490e7a8defb3e46f97dfe4bacae89122"}, - {file = "PyYAML-5.4.1-cp39-cp39-manylinux2014_s390x.whl", hash = "sha256:fdc842473cd33f45ff6bce46aea678a54e3d21f1b61a7750ce3c498eedfe25d6"}, {file = "PyYAML-5.4.1-cp39-cp39-win32.whl", hash = "sha256:49d4cdd9065b9b6e206d0595fee27a96b5dd22618e7520c33204a4a3239d5b10"}, {file = "PyYAML-5.4.1-cp39-cp39-win_amd64.whl", hash = "sha256:c20cfa2d49991c8b4147af39859b167664f2ad4561704ee74c1de03318e898db"}, {file = "PyYAML-5.4.1.tar.gz", hash = "sha256:607774cbba28732bfa802b54baa7484215f530991055bb562efbed5b2f20a45e"}, ] regex = [ - {file = "regex-2021.4.4-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:619d71c59a78b84d7f18891fe914446d07edd48dc8328c8e149cbe0929b4e000"}, - {file = "regex-2021.4.4-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:47bf5bf60cf04d72bf6055ae5927a0bd9016096bf3d742fa50d9bf9f45aa0711"}, - {file = "regex-2021.4.4-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:281d2fd05555079448537fe108d79eb031b403dac622621c78944c235f3fcf11"}, - {file = "regex-2021.4.4-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:bd28bc2e3a772acbb07787c6308e00d9626ff89e3bfcdebe87fa5afbfdedf968"}, - {file = "regex-2021.4.4-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:7c2a1af393fcc09e898beba5dd59196edaa3116191cc7257f9224beaed3e1aa0"}, - {file = "regex-2021.4.4-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:c38c71df845e2aabb7fb0b920d11a1b5ac8526005e533a8920aea97efb8ec6a4"}, - {file = "regex-2021.4.4-cp36-cp36m-manylinux2014_i686.whl", hash = "sha256:96fcd1888ab4d03adfc9303a7b3c0bd78c5412b2bfbe76db5b56d9eae004907a"}, - {file = "regex-2021.4.4-cp36-cp36m-manylinux2014_x86_64.whl", hash = "sha256:ade17eb5d643b7fead300a1641e9f45401c98eee23763e9ed66a43f92f20b4a7"}, - {file = "regex-2021.4.4-cp36-cp36m-win32.whl", hash = "sha256:e8e5b509d5c2ff12f8418006d5a90e9436766133b564db0abaec92fd27fcee29"}, - {file = "regex-2021.4.4-cp36-cp36m-win_amd64.whl", hash = "sha256:11d773d75fa650cd36f68d7ca936e3c7afaae41b863b8c387a22aaa78d3c5c79"}, - {file = "regex-2021.4.4-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:d3029c340cfbb3ac0a71798100ccc13b97dddf373a4ae56b6a72cf70dfd53bc8"}, - {file = "regex-2021.4.4-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:18c071c3eb09c30a264879f0d310d37fe5d3a3111662438889ae2eb6fc570c31"}, - {file = "regex-2021.4.4-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:4c557a7b470908b1712fe27fb1ef20772b78079808c87d20a90d051660b1d69a"}, - {file = "regex-2021.4.4-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:01afaf2ec48e196ba91b37451aa353cb7eda77efe518e481707e0515025f0cd5"}, - {file = "regex-2021.4.4-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:3a9cd17e6e5c7eb328517969e0cb0c3d31fd329298dd0c04af99ebf42e904f82"}, - {file = "regex-2021.4.4-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:90f11ff637fe8798933fb29f5ae1148c978cccb0452005bf4c69e13db951e765"}, - {file = "regex-2021.4.4-cp37-cp37m-manylinux2014_i686.whl", hash = "sha256:919859aa909429fb5aa9cf8807f6045592c85ef56fdd30a9a3747e513db2536e"}, - {file = "regex-2021.4.4-cp37-cp37m-manylinux2014_x86_64.whl", hash = "sha256:339456e7d8c06dd36a22e451d58ef72cef293112b559010db3d054d5560ef439"}, - {file = "regex-2021.4.4-cp37-cp37m-win32.whl", hash = "sha256:67bdb9702427ceddc6ef3dc382455e90f785af4c13d495f9626861763ee13f9d"}, - {file = "regex-2021.4.4-cp37-cp37m-win_amd64.whl", hash = "sha256:32e65442138b7b76dd8173ffa2cf67356b7bc1768851dded39a7a13bf9223da3"}, - {file = "regex-2021.4.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1e1c20e29358165242928c2de1482fb2cf4ea54a6a6dea2bd7a0e0d8ee321500"}, - {file = "regex-2021.4.4-cp38-cp38-manylinux1_i686.whl", hash = "sha256:314d66636c494ed9c148a42731b3834496cc9a2c4251b1661e40936814542b14"}, - {file = "regex-2021.4.4-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:6d1b01031dedf2503631d0903cb563743f397ccaf6607a5e3b19a3d76fc10480"}, - {file = "regex-2021.4.4-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:741a9647fcf2e45f3a1cf0e24f5e17febf3efe8d4ba1281dcc3aa0459ef424dc"}, - {file = "regex-2021.4.4-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:4c46e22a0933dd783467cf32b3516299fb98cfebd895817d685130cc50cd1093"}, - {file = "regex-2021.4.4-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:e512d8ef5ad7b898cdb2d8ee1cb09a8339e4f8be706d27eaa180c2f177248a10"}, - {file = "regex-2021.4.4-cp38-cp38-manylinux2014_i686.whl", hash = "sha256:980d7be47c84979d9136328d882f67ec5e50008681d94ecc8afa8a65ed1f4a6f"}, - {file = "regex-2021.4.4-cp38-cp38-manylinux2014_x86_64.whl", hash = "sha256:ce15b6d103daff8e9fee13cf7f0add05245a05d866e73926c358e871221eae87"}, - {file = "regex-2021.4.4-cp38-cp38-win32.whl", hash = "sha256:a91aa8619b23b79bcbeb37abe286f2f408d2f2d6f29a17237afda55bb54e7aac"}, - {file = "regex-2021.4.4-cp38-cp38-win_amd64.whl", hash = "sha256:c0502c0fadef0d23b128605d69b58edb2c681c25d44574fc673b0e52dce71ee2"}, - {file = "regex-2021.4.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:598585c9f0af8374c28edd609eb291b5726d7cbce16be6a8b95aa074d252ee17"}, - {file = "regex-2021.4.4-cp39-cp39-manylinux1_i686.whl", hash = "sha256:ee54ff27bf0afaf4c3b3a62bcd016c12c3fdb4ec4f413391a90bd38bc3624605"}, - {file = "regex-2021.4.4-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:7d9884d86dd4dd489e981d94a65cd30d6f07203d90e98f6f657f05170f6324c9"}, - {file = "regex-2021.4.4-cp39-cp39-manylinux2010_i686.whl", hash = "sha256:bf5824bfac591ddb2c1f0a5f4ab72da28994548c708d2191e3b87dd207eb3ad7"}, - {file = "regex-2021.4.4-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:563085e55b0d4fb8f746f6a335893bda5c2cef43b2f0258fe1020ab1dd874df8"}, - {file = "regex-2021.4.4-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:b9c3db21af35e3b3c05764461b262d6f05bbca08a71a7849fd79d47ba7bc33ed"}, - {file = "regex-2021.4.4-cp39-cp39-manylinux2014_i686.whl", hash = "sha256:3916d08be28a1149fb97f7728fca1f7c15d309a9f9682d89d79db75d5e52091c"}, - {file = "regex-2021.4.4-cp39-cp39-manylinux2014_x86_64.whl", hash = "sha256:fd45ff9293d9274c5008a2054ecef86a9bfe819a67c7be1afb65e69b405b3042"}, - {file = "regex-2021.4.4-cp39-cp39-win32.whl", hash = "sha256:fa4537fb4a98fe8fde99626e4681cc644bdcf2a795038533f9f711513a862ae6"}, - {file = "regex-2021.4.4-cp39-cp39-win_amd64.whl", hash = "sha256:97f29f57d5b84e73fbaf99ab3e26134e6687348e95ef6b48cfd2c06807005a07"}, - {file = "regex-2021.4.4.tar.gz", hash = "sha256:52ba3d3f9b942c49d7e4bc105bb28551c44065f139a65062ab7912bef10c9afb"}, + {file = "regex-2021.11.10-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:9345b6f7ee578bad8e475129ed40123d265464c4cfead6c261fd60fc9de00bcf"}, + {file = "regex-2021.11.10-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:416c5f1a188c91e3eb41e9c8787288e707f7d2ebe66e0a6563af280d9b68478f"}, + {file = "regex-2021.11.10-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e0538c43565ee6e703d3a7c3bdfe4037a5209250e8502c98f20fea6f5fdf2965"}, + {file = "regex-2021.11.10-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7ee1227cf08b6716c85504aebc49ac827eb88fcc6e51564f010f11a406c0a667"}, + {file = "regex-2021.11.10-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6650f16365f1924d6014d2ea770bde8555b4a39dc9576abb95e3cd1ff0263b36"}, + {file = "regex-2021.11.10-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:30ab804ea73972049b7a2a5c62d97687d69b5a60a67adca07eb73a0ddbc9e29f"}, + {file = "regex-2021.11.10-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:68a067c11463de2a37157930d8b153005085e42bcb7ad9ca562d77ba7d1404e0"}, + {file = "regex-2021.11.10-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:162abfd74e88001d20cb73ceaffbfe601469923e875caf9118333b1a4aaafdc4"}, + {file = "regex-2021.11.10-cp310-cp310-win32.whl", hash = "sha256:98ba568e8ae26beb726aeea2273053c717641933836568c2a0278a84987b2a1a"}, + {file = "regex-2021.11.10-cp310-cp310-win_amd64.whl", hash = "sha256:780b48456a0f0ba4d390e8b5f7c661fdd218934388cde1a974010a965e200e12"}, + {file = "regex-2021.11.10-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:dba70f30fd81f8ce6d32ddeef37d91c8948e5d5a4c63242d16a2b2df8143aafc"}, + {file = "regex-2021.11.10-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e1f54b9b4b6c53369f40028d2dd07a8c374583417ee6ec0ea304e710a20f80a0"}, + {file = "regex-2021.11.10-cp36-cp36m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fbb9dc00e39f3e6c0ef48edee202f9520dafb233e8b51b06b8428cfcb92abd30"}, + {file = "regex-2021.11.10-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:666abff54e474d28ff42756d94544cdfd42e2ee97065857413b72e8a2d6a6345"}, + {file = "regex-2021.11.10-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5537f71b6d646f7f5f340562ec4c77b6e1c915f8baae822ea0b7e46c1f09b733"}, + {file = "regex-2021.11.10-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ed2e07c6a26ed4bea91b897ee2b0835c21716d9a469a96c3e878dc5f8c55bb23"}, + {file = "regex-2021.11.10-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:ca5f18a75e1256ce07494e245cdb146f5a9267d3c702ebf9b65c7f8bd843431e"}, + {file = "regex-2021.11.10-cp36-cp36m-win32.whl", hash = "sha256:93a5051fcf5fad72de73b96f07d30bc29665697fb8ecdfbc474f3452c78adcf4"}, + {file = "regex-2021.11.10-cp36-cp36m-win_amd64.whl", hash = "sha256:b483c9d00a565633c87abd0aaf27eb5016de23fed952e054ecc19ce32f6a9e7e"}, + {file = "regex-2021.11.10-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:fff55f3ce50a3ff63ec8e2a8d3dd924f1941b250b0aac3d3d42b687eeff07a8e"}, + {file = "regex-2021.11.10-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e32d2a2b02ccbef10145df9135751abea1f9f076e67a4e261b05f24b94219e36"}, + {file = "regex-2021.11.10-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:53db2c6be8a2710b359bfd3d3aa17ba38f8aa72a82309a12ae99d3c0c3dcd74d"}, + {file = "regex-2021.11.10-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2207ae4f64ad3af399e2d30dde66f0b36ae5c3129b52885f1bffc2f05ec505c8"}, + {file = "regex-2021.11.10-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d5ca078bb666c4a9d1287a379fe617a6dccd18c3e8a7e6c7e1eb8974330c626a"}, + {file = "regex-2021.11.10-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:dd33eb9bdcfbabab3459c9ee651d94c842bc8a05fabc95edf4ee0c15a072495e"}, + {file = "regex-2021.11.10-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:05b7d6d7e64efe309972adab77fc2af8907bb93217ec60aa9fe12a0dad35874f"}, + {file = "regex-2021.11.10-cp37-cp37m-win32.whl", hash = "sha256:e71255ba42567d34a13c03968736c5d39bb4a97ce98188fafb27ce981115beec"}, + {file = "regex-2021.11.10-cp37-cp37m-win_amd64.whl", hash = "sha256:07856afef5ffcc052e7eccf3213317fbb94e4a5cd8177a2caa69c980657b3cb4"}, + {file = "regex-2021.11.10-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:ba05430e819e58544e840a68b03b28b6d328aff2e41579037e8bab7653b37d83"}, + {file = "regex-2021.11.10-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:7f301b11b9d214f83ddaf689181051e7f48905568b0c7017c04c06dfd065e244"}, + {file = "regex-2021.11.10-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4aaa4e0705ef2b73dd8e36eeb4c868f80f8393f5f4d855e94025ce7ad8525f50"}, + {file = "regex-2021.11.10-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:788aef3549f1924d5c38263104dae7395bf020a42776d5ec5ea2b0d3d85d6646"}, + {file = "regex-2021.11.10-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f8af619e3be812a2059b212064ea7a640aff0568d972cd1b9e920837469eb3cb"}, + {file = "regex-2021.11.10-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:85bfa6a5413be0ee6c5c4a663668a2cad2cbecdee367630d097d7823041bdeec"}, + {file = "regex-2021.11.10-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f23222527b307970e383433daec128d769ff778d9b29343fb3496472dc20dabe"}, + {file = "regex-2021.11.10-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:da1a90c1ddb7531b1d5ff1e171b4ee61f6345119be7351104b67ff413843fe94"}, + {file = "regex-2021.11.10-cp38-cp38-win32.whl", hash = "sha256:0617383e2fe465732af4509e61648b77cbe3aee68b6ac8c0b6fe934db90be5cc"}, + {file = "regex-2021.11.10-cp38-cp38-win_amd64.whl", hash = "sha256:a3feefd5e95871872673b08636f96b61ebef62971eab044f5124fb4dea39919d"}, + {file = "regex-2021.11.10-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:f7f325be2804246a75a4f45c72d4ce80d2443ab815063cdf70ee8fb2ca59ee1b"}, + {file = "regex-2021.11.10-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:537ca6a3586931b16a85ac38c08cc48f10fc870a5b25e51794c74df843e9966d"}, + {file = "regex-2021.11.10-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:eef2afb0fd1747f33f1ee3e209bce1ed582d1896b240ccc5e2697e3275f037c7"}, + {file = "regex-2021.11.10-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:432bd15d40ed835a51617521d60d0125867f7b88acf653e4ed994a1f8e4995dc"}, + {file = "regex-2021.11.10-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b43c2b8a330a490daaef5a47ab114935002b13b3f9dc5da56d5322ff218eeadb"}, + {file = "regex-2021.11.10-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:962b9a917dd7ceacbe5cd424556914cb0d636001e393b43dc886ba31d2a1e449"}, + {file = "regex-2021.11.10-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fa8c626d6441e2d04b6ee703ef2d1e17608ad44c7cb75258c09dd42bacdfc64b"}, + {file = "regex-2021.11.10-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:3c5fb32cc6077abad3bbf0323067636d93307c9fa93e072771cf9a64d1c0f3ef"}, + {file = "regex-2021.11.10-cp39-cp39-win32.whl", hash = "sha256:3b5df18db1fccd66de15aa59c41e4f853b5df7550723d26aa6cb7f40e5d9da5a"}, + {file = "regex-2021.11.10-cp39-cp39-win_amd64.whl", hash = "sha256:83ee89483672b11f8952b158640d0c0ff02dc43d9cb1b70c1564b49abe92ce29"}, + {file = "regex-2021.11.10.tar.gz", hash = "sha256:f341ee2df0999bfdf7a95e448075effe0db212a59387de1a70690e4acb03d4c6"}, ] requests = [ {file = "requests-2.26.0-py2.py3-none-any.whl", hash = "sha256:6c1246513ecd5ecd4528a0906f910e8f0f9c6b8ec72030dc9fd154dc1a6efd24"}, @@ -1108,16 +1113,16 @@ six = [ {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, ] smmap = [ - {file = "smmap-4.0.0-py2.py3-none-any.whl", hash = "sha256:a9a7479e4c572e2e775c404dcd3080c8dc49f39918c2cf74913d30c4c478e3c2"}, - {file = "smmap-4.0.0.tar.gz", hash = "sha256:7e65386bd122d45405ddf795637b7f7d2b532e7e401d46bbe3fb49b9986d5182"}, + {file = "smmap-5.0.0-py3-none-any.whl", hash = "sha256:2aba19d6a040e78d8b09de5c57e96207b09ed71d8e55ce0959eeee6c8e190d94"}, + {file = "smmap-5.0.0.tar.gz", hash = "sha256:c840e62059cd3be204b0c9c9f74be2c09d5648eddd4580d9314c3ecde0b30936"}, ] snowballstemmer = [ {file = "snowballstemmer-2.1.0-py2.py3-none-any.whl", hash = "sha256:b51b447bea85f9968c13b650126a888aabd4cb4463fca868ec596826325dedc2"}, {file = "snowballstemmer-2.1.0.tar.gz", hash = "sha256:e997baa4f2e9139951b6f4c631bad912dfd3c792467e2f03d7239464af90e914"}, ] sphinx = [ - {file = "Sphinx-4.0.2-py3-none-any.whl", hash = "sha256:d1cb10bee9c4231f1700ec2e24a91be3f3a3aba066ea4ca9f3bbe47e59d5a1d4"}, - {file = "Sphinx-4.0.2.tar.gz", hash = "sha256:b5c2ae4120bf00c799ba9b3699bc895816d272d120080fbc967292f29b52b48c"}, + {file = "Sphinx-4.3.0-py3-none-any.whl", hash = "sha256:7e2b30da5f39170efcd95c6270f07669d623c276521fee27ad6c380f49d2bf5b"}, + {file = "Sphinx-4.3.0.tar.gz", hash = "sha256:6d051ab6e0d06cba786c4656b0fe67ba259fe058410f49e95bee6e49c4052cbf"}, ] sphinx-rtd-theme = [ {file = "sphinx_rtd_theme-0.5.2-py2.py3-none-any.whl", hash = "sha256:4a05bdbe8b1446d77a01e20a23ebc6777c74f43237035e76be89699308987d6f"}, @@ -1148,13 +1153,17 @@ sphinxcontrib-serializinghtml = [ {file = "sphinxcontrib_serializinghtml-1.1.5-py2.py3-none-any.whl", hash = "sha256:352a9a00ae864471d3a7ead8d7d79f5fc0b57e8b3f95e9867eb9eb28999b92fd"}, ] stevedore = [ - {file = "stevedore-3.3.0-py3-none-any.whl", hash = "sha256:50d7b78fbaf0d04cd62411188fa7eedcb03eb7f4c4b37005615ceebe582aa82a"}, - {file = "stevedore-3.3.0.tar.gz", hash = "sha256:3a5bbd0652bf552748871eaa73a4a8dc2899786bc497a2aa1fcb4dcdb0debeee"}, + {file = "stevedore-3.5.0-py3-none-any.whl", hash = "sha256:a547de73308fd7e90075bb4d301405bebf705292fa90a90fc3bcf9133f58616c"}, + {file = "stevedore-3.5.0.tar.gz", hash = "sha256:f40253887d8712eaa2bb0ea3830374416736dc8ec0e22f5a65092c1174c44335"}, ] toml = [ {file = "toml-0.10.1-py2.py3-none-any.whl", hash = "sha256:bda89d5935c2eac546d648028b9901107a595863cb36bae0c73ac804a9b4ce88"}, {file = "toml-0.10.1.tar.gz", hash = "sha256:926b612be1e5ce0634a2ca03470f95169cf16f939018233a670519cb4ac58b0f"}, ] +tomli = [ + {file = "tomli-1.2.2-py3-none-any.whl", hash = "sha256:f04066f68f5554911363063a30b108d2b5a5b1a010aa8b6132af78489fe3aade"}, + {file = "tomli-1.2.2.tar.gz", hash = "sha256:c6ce0015eb38820eaf32b5db832dbc26deb3dd427bd5f6556cf0acac2c214fee"}, +] typed-ast = [ {file = "typed_ast-1.4.3-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:2068531575a125b87a41802130fa7e29f26c09a2833fea68d9a40cf33902eba6"}, {file = "typed_ast-1.4.3-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:c907f561b1e83e93fad565bac5ba9c22d96a54e7ea0267c708bffe863cbe4075"}, @@ -1188,21 +1197,71 @@ typed-ast = [ {file = "typed_ast-1.4.3.tar.gz", hash = "sha256:fb1bbeac803adea29cedd70781399c99138358c26d05fcbd23c13016b7f5ec65"}, ] typing-extensions = [ - {file = "typing_extensions-3.10.0.0-py2-none-any.whl", hash = "sha256:0ac0f89795dd19de6b97debb0c6af1c70987fd80a2d62d1958f7e56fcc31b497"}, - {file = "typing_extensions-3.10.0.0-py3-none-any.whl", hash = "sha256:779383f6086d90c99ae41cf0ff39aac8a7937a9283ce0a414e5dd782f4c94a84"}, - {file = "typing_extensions-3.10.0.0.tar.gz", hash = "sha256:50b6f157849174217d0656f99dc82fe932884fb250826c18350e159ec6cdf342"}, + {file = "typing_extensions-3.10.0.2-py2-none-any.whl", hash = "sha256:d8226d10bc02a29bcc81df19a26e56a9647f8b0a6d4a83924139f4a8b01f17b7"}, + {file = "typing_extensions-3.10.0.2-py3-none-any.whl", hash = "sha256:f1d25edafde516b146ecd0613dabcc61409817af4766fbbcfb8d1ad4ec441a34"}, + {file = "typing_extensions-3.10.0.2.tar.gz", hash = "sha256:49f75d16ff11f1cd258e1b988ccff82a3ca5570217d7ad8c5f48205dd99a677e"}, ] urllib3 = [ - {file = "urllib3-1.26.5-py2.py3-none-any.whl", hash = "sha256:753a0374df26658f99d826cfe40394a686d05985786d946fbe4165b5148f5a7c"}, - {file = "urllib3-1.26.5.tar.gz", hash = "sha256:a7acd0977125325f516bda9735fa7142b909a8d01e8b2e4c8108d0984e6e0098"}, + {file = "urllib3-1.26.7-py2.py3-none-any.whl", hash = "sha256:c4fdf4019605b6e5423637e01bc9fe4daef873709a7973e195ceba0a62bbc844"}, + {file = "urllib3-1.26.7.tar.gz", hash = "sha256:4987c65554f7a2dbf30c18fd48778ef124af6fab771a377103da0585e2336ece"}, ] wrapt = [ - {file = "wrapt-1.12.1.tar.gz", hash = "sha256:b62ffa81fb85f4332a4f609cab4ac40709470da05643a082ec1eb88e6d9b97d7"}, + {file = "wrapt-1.13.3-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:e05e60ff3b2b0342153be4d1b597bbcfd8330890056b9619f4ad6b8d5c96a81a"}, + {file = "wrapt-1.13.3-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:85148f4225287b6a0665eef08a178c15097366d46b210574a658c1ff5b377489"}, + {file = "wrapt-1.13.3-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:2dded5496e8f1592ec27079b28b6ad2a1ef0b9296d270f77b8e4a3a796cf6909"}, + {file = "wrapt-1.13.3-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:e94b7d9deaa4cc7bac9198a58a7240aaf87fe56c6277ee25fa5b3aa1edebd229"}, + {file = "wrapt-1.13.3-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:498e6217523111d07cd67e87a791f5e9ee769f9241fcf8a379696e25806965af"}, + {file = "wrapt-1.13.3-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:ec7e20258ecc5174029a0f391e1b948bf2906cd64c198a9b8b281b811cbc04de"}, + {file = "wrapt-1.13.3-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:87883690cae293541e08ba2da22cacaae0a092e0ed56bbba8d018cc486fbafbb"}, + {file = "wrapt-1.13.3-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:f99c0489258086308aad4ae57da9e8ecf9e1f3f30fa35d5e170b4d4896554d80"}, + {file = "wrapt-1.13.3-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:6a03d9917aee887690aa3f1747ce634e610f6db6f6b332b35c2dd89412912bca"}, + {file = "wrapt-1.13.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:936503cb0a6ed28dbfa87e8fcd0a56458822144e9d11a49ccee6d9a8adb2ac44"}, + {file = "wrapt-1.13.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:f9c51d9af9abb899bd34ace878fbec8bf357b3194a10c4e8e0a25512826ef056"}, + {file = "wrapt-1.13.3-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:220a869982ea9023e163ba915077816ca439489de6d2c09089b219f4e11b6785"}, + {file = "wrapt-1.13.3-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:0877fe981fd76b183711d767500e6b3111378ed2043c145e21816ee589d91096"}, + {file = "wrapt-1.13.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:43e69ffe47e3609a6aec0fe723001c60c65305784d964f5007d5b4fb1bc6bf33"}, + {file = "wrapt-1.13.3-cp310-cp310-win32.whl", hash = "sha256:78dea98c81915bbf510eb6a3c9c24915e4660302937b9ae05a0947164248020f"}, + {file = "wrapt-1.13.3-cp310-cp310-win_amd64.whl", hash = "sha256:ea3e746e29d4000cd98d572f3ee2a6050a4f784bb536f4ac1f035987fc1ed83e"}, + {file = "wrapt-1.13.3-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:8c73c1a2ec7c98d7eaded149f6d225a692caa1bd7b2401a14125446e9e90410d"}, + {file = "wrapt-1.13.3-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:086218a72ec7d986a3eddb7707c8c4526d677c7b35e355875a0fe2918b059179"}, + {file = "wrapt-1.13.3-cp35-cp35m-manylinux2010_i686.whl", hash = "sha256:e92d0d4fa68ea0c02d39f1e2f9cb5bc4b4a71e8c442207433d8db47ee79d7aa3"}, + {file = "wrapt-1.13.3-cp35-cp35m-manylinux2010_x86_64.whl", hash = "sha256:d4a5f6146cfa5c7ba0134249665acd322a70d1ea61732723c7d3e8cc0fa80755"}, + {file = "wrapt-1.13.3-cp35-cp35m-win32.whl", hash = "sha256:8aab36778fa9bba1a8f06a4919556f9f8c7b33102bd71b3ab307bb3fecb21851"}, + {file = "wrapt-1.13.3-cp35-cp35m-win_amd64.whl", hash = "sha256:944b180f61f5e36c0634d3202ba8509b986b5fbaf57db3e94df11abee244ba13"}, + {file = "wrapt-1.13.3-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:2ebdde19cd3c8cdf8df3fc165bc7827334bc4e353465048b36f7deeae8ee0918"}, + {file = "wrapt-1.13.3-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:610f5f83dd1e0ad40254c306f4764fcdc846641f120c3cf424ff57a19d5f7ade"}, + {file = "wrapt-1.13.3-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:5601f44a0f38fed36cc07db004f0eedeaadbdcec90e4e90509480e7e6060a5bc"}, + {file = "wrapt-1.13.3-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:e6906d6f48437dfd80464f7d7af1740eadc572b9f7a4301e7dd3d65db285cacf"}, + {file = "wrapt-1.13.3-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:766b32c762e07e26f50d8a3468e3b4228b3736c805018e4b0ec8cc01ecd88125"}, + {file = "wrapt-1.13.3-cp36-cp36m-win32.whl", hash = "sha256:5f223101f21cfd41deec8ce3889dc59f88a59b409db028c469c9b20cfeefbe36"}, + {file = "wrapt-1.13.3-cp36-cp36m-win_amd64.whl", hash = "sha256:f122ccd12fdc69628786d0c947bdd9cb2733be8f800d88b5a37c57f1f1d73c10"}, + {file = "wrapt-1.13.3-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:46f7f3af321a573fc0c3586612db4decb7eb37172af1bc6173d81f5b66c2e068"}, + {file = "wrapt-1.13.3-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:778fd096ee96890c10ce96187c76b3e99b2da44e08c9e24d5652f356873f6709"}, + {file = "wrapt-1.13.3-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:0cb23d36ed03bf46b894cfec777eec754146d68429c30431c99ef28482b5c1df"}, + {file = "wrapt-1.13.3-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:96b81ae75591a795d8c90edc0bfaab44d3d41ffc1aae4d994c5aa21d9b8e19a2"}, + {file = "wrapt-1.13.3-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:7dd215e4e8514004c8d810a73e342c536547038fb130205ec4bba9f5de35d45b"}, + {file = "wrapt-1.13.3-cp37-cp37m-win32.whl", hash = "sha256:47f0a183743e7f71f29e4e21574ad3fa95676136f45b91afcf83f6a050914829"}, + {file = "wrapt-1.13.3-cp37-cp37m-win_amd64.whl", hash = "sha256:fd76c47f20984b43d93de9a82011bb6e5f8325df6c9ed4d8310029a55fa361ea"}, + {file = "wrapt-1.13.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:b73d4b78807bd299b38e4598b8e7bd34ed55d480160d2e7fdaabd9931afa65f9"}, + {file = "wrapt-1.13.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:ec9465dd69d5657b5d2fa6133b3e1e989ae27d29471a672416fd729b429eb554"}, + {file = "wrapt-1.13.3-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:dd91006848eb55af2159375134d724032a2d1d13bcc6f81cd8d3ed9f2b8e846c"}, + {file = "wrapt-1.13.3-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:ae9de71eb60940e58207f8e71fe113c639da42adb02fb2bcbcaccc1ccecd092b"}, + {file = "wrapt-1.13.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:51799ca950cfee9396a87f4a1240622ac38973b6df5ef7a41e7f0b98797099ce"}, + {file = "wrapt-1.13.3-cp38-cp38-win32.whl", hash = "sha256:4b9c458732450ec42578b5642ac53e312092acf8c0bfce140ada5ca1ac556f79"}, + {file = "wrapt-1.13.3-cp38-cp38-win_amd64.whl", hash = "sha256:7dde79d007cd6dfa65afe404766057c2409316135cb892be4b1c768e3f3a11cb"}, + {file = "wrapt-1.13.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:981da26722bebb9247a0601e2922cedf8bb7a600e89c852d063313102de6f2cb"}, + {file = "wrapt-1.13.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:705e2af1f7be4707e49ced9153f8d72131090e52be9278b5dbb1498c749a1e32"}, + {file = "wrapt-1.13.3-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:25b1b1d5df495d82be1c9d2fad408f7ce5ca8a38085e2da41bb63c914baadff7"}, + {file = "wrapt-1.13.3-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:77416e6b17926d953b5c666a3cb718d5945df63ecf922af0ee576206d7033b5e"}, + {file = "wrapt-1.13.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:865c0b50003616f05858b22174c40ffc27a38e67359fa1495605f96125f76640"}, + {file = "wrapt-1.13.3-cp39-cp39-win32.whl", hash = "sha256:0a017a667d1f7411816e4bf214646d0ad5b1da2c1ea13dec6c162736ff25a374"}, + {file = "wrapt-1.13.3-cp39-cp39-win_amd64.whl", hash = "sha256:81bd7c90d28a4b2e1df135bfbd7c23aee3050078ca6441bead44c42483f9ebfb"}, + {file = "wrapt-1.13.3.tar.gz", hash = "sha256:1fea9cd438686e6682271d36f3481a9f3636195578bab9ca3382e2f5f01fc185"}, ] yamllint = [ - {file = "yamllint-1.26.1.tar.gz", hash = "sha256:87d9462b3ed7e9dfa19caa177f7a77cd9888b3dc4044447d6ae0ab233bcd1324"}, + {file = "yamllint-1.26.3.tar.gz", hash = "sha256:3934dcde484374596d6b52d8db412929a169f6d9e52e20f9ade5bf3523d9b96e"}, ] zipp = [ - {file = "zipp-3.4.1-py3-none-any.whl", hash = "sha256:51cb66cc54621609dd593d1787f286ee42a5c0adbb4b29abea5a63edc3e03098"}, - {file = "zipp-3.4.1.tar.gz", hash = "sha256:3607921face881ba3e026887d8150cca609d517579abe052ac81fc5aeffdbd76"}, + {file = "zipp-3.6.0-py3-none-any.whl", hash = "sha256:9fe5ea21568a0a70e50f273397638d39b03353731e6cbbb3fd8502a33fec40bc"}, + {file = "zipp-3.6.0.tar.gz", hash = "sha256:71c644c5369f4a6e07636f0aa966270449561fcea2e3d6747b8d23efaa9d7832"}, ] diff --git a/pyproject.toml b/pyproject.toml index 5247580a..f50f26f4 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -26,11 +26,11 @@ include = [ ] [tool.poetry.dependencies] -python = "^3.6" +python = "^3.6.2" [tool.poetry.dev-dependencies] bandit = "^1.6.2" -black = "^20.8b1" +black = "^21.10b0" coverage = "^5.5" invoke = "^1.4.1" flake8 = "^3.8.3" diff --git a/tests/unit/mock/config/compliance/compliance/fortinet_fortios/fortios_backup.txt b/tests/unit/mock/config/compliance/compliance/fortinet_fortios/fortios_backup.txt new file mode 100644 index 00000000..bca6008c --- /dev/null +++ b/tests/unit/mock/config/compliance/compliance/fortinet_fortios/fortios_backup.txt @@ -0,0 +1,313 @@ +config system global + set admin-concurrent enable + set admin-console-timeout 0 + set admin-hsts-max-age 15552000 + set admin-https-pki-required disable + set admin-https-redirect enable + set admin-https-ssl-versions tlsv1-1 tlsv1-2 tlsv1-3 + set admin-lockout-duration 60 + set admin-lockout-threshold 3 + set admin-login-max 100 +end +config wireless-controller global + set name '' + set location '' + set image-download enable + set max-retransmit 3 + set control-message-offload ebp-frame aeroscout-tag ap-list sta-list sta-cap-list stats aeroscout-mu sta-health spectral-analysis + set data-ethernet-II enable + set link-aggregation disable + set mesh-eth-type 8755 + set fiapp-eth-type 5252 + set discovery-mc-addr 224.0.1.140 + set max-clients 0 + set rogue-scan-mac-adjacency 7 + set ipsec-base-ip 169.254.0.1 + set wtp-share disable + set ap-log-server disable +end +config system switch-interface +end +config system lte-modem + set status disable + set extra-init '' + set authtype none + set apn '' + set modem-port 255 +end +config system interface + edit "mgmt" + set vrf 0 + set distance 5 + set priority 0 + set dhcp-relay-service disable + set ip 10.0.0.41 255.255.252.0 + set allowaccess ping https ssh snmp http fgfm + set fail-detect disable + set arpforward enable + set broadcast-forward disable + set bfd global + set l2forward disable + set icmp-send-redirect enable + set icmp-accept-redirect enable + set vlanforward disable + set stpforward disable + set ips-sniffer-mode disable + set ident-accept disable + set ipmac disable + set subst disable + set substitute-dst-mac 00:00:00:00:00:00 + set status up + set netbios-forward disable + set wins-ip 0.0.0.0 + set type physical + set dedicated-to management + set netflow-sampler disable + set sflow-sampler disable + set src-check enable + set sample-rate 2000 + set polling-interval 20 + set sample-direction both + set explicit-web-proxy disable + set explicit-ftp-proxy disable + set proxy-captive-portal disable + set tcp-mss 0 + set inbandwidth 0 + set outbandwidth 0 + set egress-shaping-profile '' + set ingress-shaping-profile '' + set disconnect-threshold 0 + set spillover-threshold 0 + set ingress-spillover-threshold 0 + set weight 0 + set external disable + set description '' + set alias '' + set device-identification disable + set lldp-reception vdom + set lldp-transmission vdom + set estimated-upstream-bandwidth 0 + set estimated-downstream-bandwidth 0 + set measured-upstream-bandwidth 0 + set measured-downstream-bandwidth 0 + set bandwidth-measure-time 0 + set monitor-bandwidth disable + set vrrp-virtual-mac disable + set role lan + set snmp-index 1 + set secondary-IP disable + set preserve-session-route disable + set auto-auth-extension-device disable + set ap-discover enable + set switch-controller-igmp-snooping-proxy disable + set switch-controller-igmp-snooping-fast-leave disable + config ipv6 + set ip6-mode static + set nd-mode basic + set ip6-address ::/0 + unset ip6-allowaccess + set icmp6-send-redirect enable + set ip6-reachable-time 0 + set ip6-retrans-time 0 + set ip6-hop-limit 0 + set vrrp-virtual-mac6 disable + set vrip6_link_local :: + set ip6-send-adv disable + set autoconf disable + end + set defaultgw enable + set dns-server-override enable + set speed auto + set trust-ip-1 0.0.0.0 0.0.0.0 + set trust-ip-2 0.0.0.0 0.0.0.0 + set trust-ip-3 0.0.0.0 0.0.0.0 + set trust-ip6-1 ::/0 + set trust-ip6-2 ::/0 + set trust-ip6-3 ::/0 + set mtu-override disable + set wccp disable + set drop-overlapped-fragment disable + set drop-fragment disable + next + edit "ha" + set vdom "root" + set vrf 0 + set fortilink disable + set mode static + set dhcp-relay-service disable + set management-ip 0.0.0.0 0.0.0.0 + set ip 0.0.0.0 0.0.0.0 + unset allowaccess + set fail-detect disable + set arpforward enable + set broadcast-forward disable + set bfd global + set l2forward disable + set icmp-send-redirect enable + set icmp-accept-redirect enable + set vlanforward disable + set stpforward disable + set ips-sniffer-mode disable + set ident-accept disable + set ipmac disable + set subst disable + set substitute-dst-mac 00:00:00:00:00:00 + set status up + set netbios-forward disable + set wins-ip 0.0.0.0 + set type physical + set netflow-sampler disable + set sflow-sampler disable + set src-check enable + set sample-rate 2000 + set polling-interval 20 + set sample-direction both + set explicit-web-proxy disable + set explicit-ftp-proxy disable + set proxy-captive-portal disable + set tcp-mss 0 + set inbandwidth 0 + set outbandwidth 0 + set egress-shaping-profile '' + set ingress-shaping-profile '' + set disconnect-threshold 0 + set spillover-threshold 0 + set ingress-spillover-threshold 0 + set weight 0 + set external disable + set description '' + set alias '' + set security-mode none + set device-identification disable + set lldp-reception vdom + set lldp-transmission vdom + set estimated-upstream-bandwidth 0 + set estimated-downstream-bandwidth 0 + set measured-upstream-bandwidth 0 + set measured-downstream-bandwidth 0 + set bandwidth-measure-time 0 + set monitor-bandwidth disable + set vrrp-virtual-mac disable + set role undefined + set snmp-index 2 + set secondary-IP disable + set preserve-session-route disable + set auto-auth-extension-device disable + set ap-discover enable + set ip-managed-by-fortiipam disable + set switch-controller-mgmt-vlan 4094 + set switch-controller-igmp-snooping-proxy disable + set switch-controller-igmp-snooping-fast-leave disable + set swc-first-create 0 + config ipv6 + set ip6-mode static + set nd-mode basic + set ip6-address ::/0 + unset ip6-allowaccess + set icmp6-send-redirect enable + set ip6-reachable-time 0 + set ip6-retrans-time 0 + set ip6-hop-limit 0 + set dhcp6-prefix-delegation disable + set dhcp6-information-request disable + set vrrp-virtual-mac6 disable + set vrip6_link_local :: + set ip6-send-adv disable + set autoconf disable + set dhcp6-relay-service disable + end + set speed auto + set mtu-override disable + set wccp disable + set drop-overlapped-fragment disable + set drop-fragment disable + next +end +config system virtual-switch + edit "lan" + set physical-switch "sw0" + set span disable + config port + edit "port1" + set speed auto + set status up + set alias '' + next + edit "port2" + set speed auto + set status up + set alias '' + next + edit "port3" + set speed auto + set status up + set alias '' + next + edit "port4" + set speed auto + set status up + set alias '' + next + edit "port5" + set speed auto + set status up + set alias '' + next + edit "port6" + set speed auto + set status up + set alias '' + next + edit "port7" + set speed auto + set status up + set alias '' + next + edit "port8" + set speed auto + set status up + set alias '' + next + edit "port9" + set speed auto + set status up + set alias '' + next + edit "port10" + set speed auto + set status up + set alias '' + next + edit "port11" + set speed auto + set status up + set alias '' + next + edit "port12" + set speed auto + set status up + set alias '' + next + edit "port13" + set speed auto + set status up + set alias '' + next + edit "port14" + set speed auto + set status up + set alias '' + next + edit "port15" + set speed auto + set status up + set alias '' + next + edit "port16" + set speed auto + set status up + set alias '' + next + end + next +end \ No newline at end of file diff --git a/tests/unit/mock/config/compliance/compliance/fortinet_fortios/fortios_feature.py b/tests/unit/mock/config/compliance/compliance/fortinet_fortios/fortios_feature.py new file mode 100644 index 00000000..afe282d2 --- /dev/null +++ b/tests/unit/mock/config/compliance/compliance/fortinet_fortios/fortios_feature.py @@ -0,0 +1,4 @@ +features = [ + {"name": "global", "ordered": False, "section": ["config system global"]}, + {"name": "interfaces", "ordered": False, "section": ["config system virtual-switch"]}, +] diff --git a/tests/unit/mock/config/compliance/compliance/fortinet_fortios/fortios_intended.txt b/tests/unit/mock/config/compliance/compliance/fortinet_fortios/fortios_intended.txt new file mode 100644 index 00000000..bca6008c --- /dev/null +++ b/tests/unit/mock/config/compliance/compliance/fortinet_fortios/fortios_intended.txt @@ -0,0 +1,313 @@ +config system global + set admin-concurrent enable + set admin-console-timeout 0 + set admin-hsts-max-age 15552000 + set admin-https-pki-required disable + set admin-https-redirect enable + set admin-https-ssl-versions tlsv1-1 tlsv1-2 tlsv1-3 + set admin-lockout-duration 60 + set admin-lockout-threshold 3 + set admin-login-max 100 +end +config wireless-controller global + set name '' + set location '' + set image-download enable + set max-retransmit 3 + set control-message-offload ebp-frame aeroscout-tag ap-list sta-list sta-cap-list stats aeroscout-mu sta-health spectral-analysis + set data-ethernet-II enable + set link-aggregation disable + set mesh-eth-type 8755 + set fiapp-eth-type 5252 + set discovery-mc-addr 224.0.1.140 + set max-clients 0 + set rogue-scan-mac-adjacency 7 + set ipsec-base-ip 169.254.0.1 + set wtp-share disable + set ap-log-server disable +end +config system switch-interface +end +config system lte-modem + set status disable + set extra-init '' + set authtype none + set apn '' + set modem-port 255 +end +config system interface + edit "mgmt" + set vrf 0 + set distance 5 + set priority 0 + set dhcp-relay-service disable + set ip 10.0.0.41 255.255.252.0 + set allowaccess ping https ssh snmp http fgfm + set fail-detect disable + set arpforward enable + set broadcast-forward disable + set bfd global + set l2forward disable + set icmp-send-redirect enable + set icmp-accept-redirect enable + set vlanforward disable + set stpforward disable + set ips-sniffer-mode disable + set ident-accept disable + set ipmac disable + set subst disable + set substitute-dst-mac 00:00:00:00:00:00 + set status up + set netbios-forward disable + set wins-ip 0.0.0.0 + set type physical + set dedicated-to management + set netflow-sampler disable + set sflow-sampler disable + set src-check enable + set sample-rate 2000 + set polling-interval 20 + set sample-direction both + set explicit-web-proxy disable + set explicit-ftp-proxy disable + set proxy-captive-portal disable + set tcp-mss 0 + set inbandwidth 0 + set outbandwidth 0 + set egress-shaping-profile '' + set ingress-shaping-profile '' + set disconnect-threshold 0 + set spillover-threshold 0 + set ingress-spillover-threshold 0 + set weight 0 + set external disable + set description '' + set alias '' + set device-identification disable + set lldp-reception vdom + set lldp-transmission vdom + set estimated-upstream-bandwidth 0 + set estimated-downstream-bandwidth 0 + set measured-upstream-bandwidth 0 + set measured-downstream-bandwidth 0 + set bandwidth-measure-time 0 + set monitor-bandwidth disable + set vrrp-virtual-mac disable + set role lan + set snmp-index 1 + set secondary-IP disable + set preserve-session-route disable + set auto-auth-extension-device disable + set ap-discover enable + set switch-controller-igmp-snooping-proxy disable + set switch-controller-igmp-snooping-fast-leave disable + config ipv6 + set ip6-mode static + set nd-mode basic + set ip6-address ::/0 + unset ip6-allowaccess + set icmp6-send-redirect enable + set ip6-reachable-time 0 + set ip6-retrans-time 0 + set ip6-hop-limit 0 + set vrrp-virtual-mac6 disable + set vrip6_link_local :: + set ip6-send-adv disable + set autoconf disable + end + set defaultgw enable + set dns-server-override enable + set speed auto + set trust-ip-1 0.0.0.0 0.0.0.0 + set trust-ip-2 0.0.0.0 0.0.0.0 + set trust-ip-3 0.0.0.0 0.0.0.0 + set trust-ip6-1 ::/0 + set trust-ip6-2 ::/0 + set trust-ip6-3 ::/0 + set mtu-override disable + set wccp disable + set drop-overlapped-fragment disable + set drop-fragment disable + next + edit "ha" + set vdom "root" + set vrf 0 + set fortilink disable + set mode static + set dhcp-relay-service disable + set management-ip 0.0.0.0 0.0.0.0 + set ip 0.0.0.0 0.0.0.0 + unset allowaccess + set fail-detect disable + set arpforward enable + set broadcast-forward disable + set bfd global + set l2forward disable + set icmp-send-redirect enable + set icmp-accept-redirect enable + set vlanforward disable + set stpforward disable + set ips-sniffer-mode disable + set ident-accept disable + set ipmac disable + set subst disable + set substitute-dst-mac 00:00:00:00:00:00 + set status up + set netbios-forward disable + set wins-ip 0.0.0.0 + set type physical + set netflow-sampler disable + set sflow-sampler disable + set src-check enable + set sample-rate 2000 + set polling-interval 20 + set sample-direction both + set explicit-web-proxy disable + set explicit-ftp-proxy disable + set proxy-captive-portal disable + set tcp-mss 0 + set inbandwidth 0 + set outbandwidth 0 + set egress-shaping-profile '' + set ingress-shaping-profile '' + set disconnect-threshold 0 + set spillover-threshold 0 + set ingress-spillover-threshold 0 + set weight 0 + set external disable + set description '' + set alias '' + set security-mode none + set device-identification disable + set lldp-reception vdom + set lldp-transmission vdom + set estimated-upstream-bandwidth 0 + set estimated-downstream-bandwidth 0 + set measured-upstream-bandwidth 0 + set measured-downstream-bandwidth 0 + set bandwidth-measure-time 0 + set monitor-bandwidth disable + set vrrp-virtual-mac disable + set role undefined + set snmp-index 2 + set secondary-IP disable + set preserve-session-route disable + set auto-auth-extension-device disable + set ap-discover enable + set ip-managed-by-fortiipam disable + set switch-controller-mgmt-vlan 4094 + set switch-controller-igmp-snooping-proxy disable + set switch-controller-igmp-snooping-fast-leave disable + set swc-first-create 0 + config ipv6 + set ip6-mode static + set nd-mode basic + set ip6-address ::/0 + unset ip6-allowaccess + set icmp6-send-redirect enable + set ip6-reachable-time 0 + set ip6-retrans-time 0 + set ip6-hop-limit 0 + set dhcp6-prefix-delegation disable + set dhcp6-information-request disable + set vrrp-virtual-mac6 disable + set vrip6_link_local :: + set ip6-send-adv disable + set autoconf disable + set dhcp6-relay-service disable + end + set speed auto + set mtu-override disable + set wccp disable + set drop-overlapped-fragment disable + set drop-fragment disable + next +end +config system virtual-switch + edit "lan" + set physical-switch "sw0" + set span disable + config port + edit "port1" + set speed auto + set status up + set alias '' + next + edit "port2" + set speed auto + set status up + set alias '' + next + edit "port3" + set speed auto + set status up + set alias '' + next + edit "port4" + set speed auto + set status up + set alias '' + next + edit "port5" + set speed auto + set status up + set alias '' + next + edit "port6" + set speed auto + set status up + set alias '' + next + edit "port7" + set speed auto + set status up + set alias '' + next + edit "port8" + set speed auto + set status up + set alias '' + next + edit "port9" + set speed auto + set status up + set alias '' + next + edit "port10" + set speed auto + set status up + set alias '' + next + edit "port11" + set speed auto + set status up + set alias '' + next + edit "port12" + set speed auto + set status up + set alias '' + next + edit "port13" + set speed auto + set status up + set alias '' + next + edit "port14" + set speed auto + set status up + set alias '' + next + edit "port15" + set speed auto + set status up + set alias '' + next + edit "port16" + set speed auto + set status up + set alias '' + next + end + next +end \ No newline at end of file diff --git a/tests/unit/mock/config/compliance/compliance/fortinet_fortios/fortios_received.json b/tests/unit/mock/config/compliance/compliance/fortinet_fortios/fortios_received.json new file mode 100644 index 00000000..4badfeb6 --- /dev/null +++ b/tests/unit/mock/config/compliance/compliance/fortinet_fortios/fortios_received.json @@ -0,0 +1,22 @@ +{ + "global": { + "actual": "config system global\n set admin-concurrent enable\n set admin-console-timeout 0\n set admin-hsts-max-age 15552000\n set admin-https-pki-required disable\n set admin-https-redirect enable\n set admin-https-ssl-versions tlsv1-1 tlsv1-2 tlsv1-3\n set admin-lockout-duration 60\n set admin-lockout-threshold 3\n set admin-login-max 100", + "cannot_parse": true, + "compliant": true, + "extra": "", + "intended": "config system global\n set admin-concurrent enable\n set admin-console-timeout 0\n set admin-hsts-max-age 15552000\n set admin-https-pki-required disable\n set admin-https-redirect enable\n set admin-https-ssl-versions tlsv1-1 tlsv1-2 tlsv1-3\n set admin-lockout-duration 60\n set admin-lockout-threshold 3\n set admin-login-max 100", + "missing": "", + "ordered_compliant": true, + "unordered_compliant": true + }, + "interfaces": { + "actual": "config system virtual-switch\n edit \"lan\"\n set physical-switch \"sw0\"\n set span disable\n config port\n edit \"port1\"\n set speed auto\n set status up\n set alias ''\n edit \"port2\"\n set speed auto\n set status up\n set alias ''\n edit \"port3\"\n set speed auto\n set status up\n set alias ''\n edit \"port4\"\n set speed auto\n set status up\n set alias ''\n edit \"port5\"\n set speed auto\n set status up\n set alias ''\n edit \"port6\"\n set speed auto\n set status up\n set alias ''\n edit \"port7\"\n set speed auto\n set status up\n set alias ''\n edit \"port8\"\n set speed auto\n set status up\n set alias ''\n edit \"port9\"\n set speed auto\n set status up\n set alias ''\n edit \"port10\"\n set speed auto\n set status up\n set alias ''\n edit \"port11\"\n set speed auto\n set status up\n set alias ''\n edit \"port12\"\n set speed auto\n set status up\n set alias ''\n edit \"port13\"\n set speed auto\n set status up\n set alias ''\n edit \"port14\"\n set speed auto\n set status up\n set alias ''\n edit \"port15\"\n set speed auto\n set status up\n set alias ''\n edit \"port16\"\n set speed auto\n set status up\n set alias ''", + "cannot_parse": true, + "compliant": true, + "extra": "", + "intended": "config system virtual-switch\n edit \"lan\"\n set physical-switch \"sw0\"\n set span disable\n config port\n edit \"port1\"\n set speed auto\n set status up\n set alias ''\n edit \"port2\"\n set speed auto\n set status up\n set alias ''\n edit \"port3\"\n set speed auto\n set status up\n set alias ''\n edit \"port4\"\n set speed auto\n set status up\n set alias ''\n edit \"port5\"\n set speed auto\n set status up\n set alias ''\n edit \"port6\"\n set speed auto\n set status up\n set alias ''\n edit \"port7\"\n set speed auto\n set status up\n set alias ''\n edit \"port8\"\n set speed auto\n set status up\n set alias ''\n edit \"port9\"\n set speed auto\n set status up\n set alias ''\n edit \"port10\"\n set speed auto\n set status up\n set alias ''\n edit \"port11\"\n set speed auto\n set status up\n set alias ''\n edit \"port12\"\n set speed auto\n set status up\n set alias ''\n edit \"port13\"\n set speed auto\n set status up\n set alias ''\n edit \"port14\"\n set speed auto\n set status up\n set alias ''\n edit \"port15\"\n set speed auto\n set status up\n set alias ''\n edit \"port16\"\n set speed auto\n set status up\n set alias ''", + "missing": "", + "ordered_compliant": true, + "unordered_compliant": true + } +} diff --git a/tests/unit/mock/config/parser/fortinet_fortios/fortios_full_received.py b/tests/unit/mock/config/parser/fortinet_fortios/fortios_full_received.py new file mode 100644 index 00000000..89622750 --- /dev/null +++ b/tests/unit/mock/config/parser/fortinet_fortios/fortios_full_received.py @@ -0,0 +1,716 @@ +from netutils.config.parser import ConfigLine + +data = [ + ConfigLine(config_line="config system global", parents=()), + ConfigLine(config_line=" set admin-concurrent enable", parents=("config system global",)), + ConfigLine(config_line=" set admin-console-timeout 0", parents=("config system global",)), + ConfigLine(config_line=" set admin-hsts-max-age 15552000", parents=("config system global",)), + ConfigLine(config_line=" set admin-https-pki-required disable", parents=("config system global",)), + ConfigLine(config_line=" set admin-https-redirect enable", parents=("config system global",)), + ConfigLine( + config_line=" set admin-https-ssl-versions tlsv1-1 tlsv1-2 tlsv1-3", parents=("config system global",) + ), + ConfigLine(config_line=" set admin-lockout-duration 60", parents=("config system global",)), + ConfigLine(config_line=" set admin-lockout-threshold 3", parents=("config system global",)), + ConfigLine(config_line=" set admin-login-max 100", parents=("config system global",)), + ConfigLine(config_line="config wireless-controller global", parents=()), + ConfigLine(config_line=" set name ''", parents=("config wireless-controller global",)), + ConfigLine(config_line=" set location ''", parents=("config wireless-controller global",)), + ConfigLine(config_line=" set image-download enable", parents=("config wireless-controller global",)), + ConfigLine(config_line=" set max-retransmit 3", parents=("config wireless-controller global",)), + ConfigLine( + config_line=" set control-message-offload ebp-frame aeroscout-tag ap-list sta-list sta-cap-list stats aeroscout-mu sta-health spectral-analysis", + parents=("config wireless-controller global",), + ), + ConfigLine(config_line=" set data-ethernet-II enable", parents=("config wireless-controller global",)), + ConfigLine(config_line=" set link-aggregation disable", parents=("config wireless-controller global",)), + ConfigLine(config_line=" set mesh-eth-type 8755", parents=("config wireless-controller global",)), + ConfigLine(config_line=" set fiapp-eth-type 5252", parents=("config wireless-controller global",)), + ConfigLine(config_line=" set discovery-mc-addr 224.0.1.140", parents=("config wireless-controller global",)), + ConfigLine(config_line=" set max-clients 0", parents=("config wireless-controller global",)), + ConfigLine(config_line=" set rogue-scan-mac-adjacency 7", parents=("config wireless-controller global",)), + ConfigLine(config_line=" set ipsec-base-ip 169.254.0.1", parents=("config wireless-controller global",)), + ConfigLine(config_line=" set wtp-share disable", parents=("config wireless-controller global",)), + ConfigLine(config_line=" set ap-log-server disable", parents=("config wireless-controller global",)), + ConfigLine(config_line="config system switch-interface", parents=()), + ConfigLine(config_line="config system lte-modem", parents=()), + ConfigLine(config_line=" set status disable", parents=("config system lte-modem",)), + ConfigLine(config_line=" set extra-init ''", parents=("config system lte-modem",)), + ConfigLine(config_line=" set authtype none", parents=("config system lte-modem",)), + ConfigLine(config_line=" set apn ''", parents=("config system lte-modem",)), + ConfigLine(config_line=" set modem-port 255", parents=("config system lte-modem",)), + ConfigLine(config_line="config system interface", parents=()), + ConfigLine(config_line=' edit "mgmt"', parents=("config system interface",)), + ConfigLine(config_line=" set vrf 0", parents=("config system interface", ' edit "mgmt"')), + ConfigLine(config_line=" set distance 5", parents=("config system interface", ' edit "mgmt"')), + ConfigLine(config_line=" set priority 0", parents=("config system interface", ' edit "mgmt"')), + ConfigLine( + config_line=" set dhcp-relay-service disable", parents=("config system interface", ' edit "mgmt"') + ), + ConfigLine( + config_line=" set ip 10.0.0.41 255.255.252.0", parents=("config system interface", ' edit "mgmt"') + ), + ConfigLine( + config_line=" set allowaccess ping https ssh snmp http fgfm", + parents=("config system interface", ' edit "mgmt"'), + ), + ConfigLine(config_line=" set fail-detect disable", parents=("config system interface", ' edit "mgmt"')), + ConfigLine(config_line=" set arpforward enable", parents=("config system interface", ' edit "mgmt"')), + ConfigLine( + config_line=" set broadcast-forward disable", parents=("config system interface", ' edit "mgmt"') + ), + ConfigLine(config_line=" set bfd global", parents=("config system interface", ' edit "mgmt"')), + ConfigLine(config_line=" set l2forward disable", parents=("config system interface", ' edit "mgmt"')), + ConfigLine( + config_line=" set icmp-send-redirect enable", parents=("config system interface", ' edit "mgmt"') + ), + ConfigLine( + config_line=" set icmp-accept-redirect enable", parents=("config system interface", ' edit "mgmt"') + ), + ConfigLine(config_line=" set vlanforward disable", parents=("config system interface", ' edit "mgmt"')), + ConfigLine(config_line=" set stpforward disable", parents=("config system interface", ' edit "mgmt"')), + ConfigLine( + config_line=" set ips-sniffer-mode disable", parents=("config system interface", ' edit "mgmt"') + ), + ConfigLine(config_line=" set ident-accept disable", parents=("config system interface", ' edit "mgmt"')), + ConfigLine(config_line=" set ipmac disable", parents=("config system interface", ' edit "mgmt"')), + ConfigLine(config_line=" set subst disable", parents=("config system interface", ' edit "mgmt"')), + ConfigLine( + config_line=" set substitute-dst-mac 00:00:00:00:00:00", + parents=("config system interface", ' edit "mgmt"'), + ), + ConfigLine(config_line=" set status up", parents=("config system interface", ' edit "mgmt"')), + ConfigLine( + config_line=" set netbios-forward disable", parents=("config system interface", ' edit "mgmt"') + ), + ConfigLine(config_line=" set wins-ip 0.0.0.0", parents=("config system interface", ' edit "mgmt"')), + ConfigLine(config_line=" set type physical", parents=("config system interface", ' edit "mgmt"')), + ConfigLine( + config_line=" set dedicated-to management", parents=("config system interface", ' edit "mgmt"') + ), + ConfigLine( + config_line=" set netflow-sampler disable", parents=("config system interface", ' edit "mgmt"') + ), + ConfigLine(config_line=" set sflow-sampler disable", parents=("config system interface", ' edit "mgmt"')), + ConfigLine(config_line=" set src-check enable", parents=("config system interface", ' edit "mgmt"')), + ConfigLine(config_line=" set sample-rate 2000", parents=("config system interface", ' edit "mgmt"')), + ConfigLine(config_line=" set polling-interval 20", parents=("config system interface", ' edit "mgmt"')), + ConfigLine(config_line=" set sample-direction both", parents=("config system interface", ' edit "mgmt"')), + ConfigLine( + config_line=" set explicit-web-proxy disable", parents=("config system interface", ' edit "mgmt"') + ), + ConfigLine( + config_line=" set explicit-ftp-proxy disable", parents=("config system interface", ' edit "mgmt"') + ), + ConfigLine( + config_line=" set proxy-captive-portal disable", parents=("config system interface", ' edit "mgmt"') + ), + ConfigLine(config_line=" set tcp-mss 0", parents=("config system interface", ' edit "mgmt"')), + ConfigLine(config_line=" set inbandwidth 0", parents=("config system interface", ' edit "mgmt"')), + ConfigLine(config_line=" set outbandwidth 0", parents=("config system interface", ' edit "mgmt"')), + ConfigLine( + config_line=" set egress-shaping-profile ''", parents=("config system interface", ' edit "mgmt"') + ), + ConfigLine( + config_line=" set ingress-shaping-profile ''", parents=("config system interface", ' edit "mgmt"') + ), + ConfigLine( + config_line=" set disconnect-threshold 0", parents=("config system interface", ' edit "mgmt"') + ), + ConfigLine(config_line=" set spillover-threshold 0", parents=("config system interface", ' edit "mgmt"')), + ConfigLine( + config_line=" set ingress-spillover-threshold 0", parents=("config system interface", ' edit "mgmt"') + ), + ConfigLine(config_line=" set weight 0", parents=("config system interface", ' edit "mgmt"')), + ConfigLine(config_line=" set external disable", parents=("config system interface", ' edit "mgmt"')), + ConfigLine(config_line=" set description ''", parents=("config system interface", ' edit "mgmt"')), + ConfigLine(config_line=" set alias ''", parents=("config system interface", ' edit "mgmt"')), + ConfigLine( + config_line=" set device-identification disable", parents=("config system interface", ' edit "mgmt"') + ), + ConfigLine(config_line=" set lldp-reception vdom", parents=("config system interface", ' edit "mgmt"')), + ConfigLine( + config_line=" set lldp-transmission vdom", parents=("config system interface", ' edit "mgmt"') + ), + ConfigLine( + config_line=" set estimated-upstream-bandwidth 0", parents=("config system interface", ' edit "mgmt"') + ), + ConfigLine( + config_line=" set estimated-downstream-bandwidth 0", + parents=("config system interface", ' edit "mgmt"'), + ), + ConfigLine( + config_line=" set measured-upstream-bandwidth 0", parents=("config system interface", ' edit "mgmt"') + ), + ConfigLine( + config_line=" set measured-downstream-bandwidth 0", + parents=("config system interface", ' edit "mgmt"'), + ), + ConfigLine( + config_line=" set bandwidth-measure-time 0", parents=("config system interface", ' edit "mgmt"') + ), + ConfigLine( + config_line=" set monitor-bandwidth disable", parents=("config system interface", ' edit "mgmt"') + ), + ConfigLine( + config_line=" set vrrp-virtual-mac disable", parents=("config system interface", ' edit "mgmt"') + ), + ConfigLine(config_line=" set role lan", parents=("config system interface", ' edit "mgmt"')), + ConfigLine(config_line=" set snmp-index 1", parents=("config system interface", ' edit "mgmt"')), + ConfigLine(config_line=" set secondary-IP disable", parents=("config system interface", ' edit "mgmt"')), + ConfigLine( + config_line=" set preserve-session-route disable", parents=("config system interface", ' edit "mgmt"') + ), + ConfigLine( + config_line=" set auto-auth-extension-device disable", + parents=("config system interface", ' edit "mgmt"'), + ), + ConfigLine(config_line=" set ap-discover enable", parents=("config system interface", ' edit "mgmt"')), + ConfigLine( + config_line=" set switch-controller-igmp-snooping-proxy disable", + parents=("config system interface", ' edit "mgmt"'), + ), + ConfigLine( + config_line=" set switch-controller-igmp-snooping-fast-leave disable", + parents=("config system interface", ' edit "mgmt"'), + ), + ConfigLine(config_line=" config ipv6", parents=("config system interface", ' edit "mgmt"')), + ConfigLine( + config_line=" set ip6-mode static", + parents=("config system interface", ' edit "mgmt"', " config ipv6"), + ), + ConfigLine( + config_line=" set nd-mode basic", + parents=("config system interface", ' edit "mgmt"', " config ipv6"), + ), + ConfigLine( + config_line=" set ip6-address ::/0", + parents=("config system interface", ' edit "mgmt"', " config ipv6"), + ), + ConfigLine( + config_line=" unset ip6-allowaccess", + parents=("config system interface", ' edit "mgmt"', " config ipv6"), + ), + ConfigLine( + config_line=" set icmp6-send-redirect enable", + parents=("config system interface", ' edit "mgmt"', " config ipv6"), + ), + ConfigLine( + config_line=" set ip6-reachable-time 0", + parents=("config system interface", ' edit "mgmt"', " config ipv6"), + ), + ConfigLine( + config_line=" set ip6-retrans-time 0", + parents=("config system interface", ' edit "mgmt"', " config ipv6"), + ), + ConfigLine( + config_line=" set ip6-hop-limit 0", + parents=("config system interface", ' edit "mgmt"', " config ipv6"), + ), + ConfigLine( + config_line=" set vrrp-virtual-mac6 disable", + parents=("config system interface", ' edit "mgmt"', " config ipv6"), + ), + ConfigLine( + config_line=" set vrip6_link_local ::", + parents=("config system interface", ' edit "mgmt"', " config ipv6"), + ), + ConfigLine( + config_line=" set ip6-send-adv disable", + parents=("config system interface", ' edit "mgmt"', " config ipv6"), + ), + ConfigLine( + config_line=" set autoconf disable", + parents=("config system interface", ' edit "mgmt"', " config ipv6"), + ), + ConfigLine(config_line=" set defaultgw enable", parents=("config system interface", ' edit "mgmt"')), + ConfigLine( + config_line=" set dns-server-override enable", parents=("config system interface", ' edit "mgmt"') + ), + ConfigLine(config_line=" set speed auto", parents=("config system interface", ' edit "mgmt"')), + ConfigLine( + config_line=" set trust-ip-1 0.0.0.0 0.0.0.0", parents=("config system interface", ' edit "mgmt"') + ), + ConfigLine( + config_line=" set trust-ip-2 0.0.0.0 0.0.0.0", parents=("config system interface", ' edit "mgmt"') + ), + ConfigLine( + config_line=" set trust-ip-3 0.0.0.0 0.0.0.0", parents=("config system interface", ' edit "mgmt"') + ), + ConfigLine(config_line=" set trust-ip6-1 ::/0", parents=("config system interface", ' edit "mgmt"')), + ConfigLine(config_line=" set trust-ip6-2 ::/0", parents=("config system interface", ' edit "mgmt"')), + ConfigLine(config_line=" set trust-ip6-3 ::/0", parents=("config system interface", ' edit "mgmt"')), + ConfigLine(config_line=" set mtu-override disable", parents=("config system interface", ' edit "mgmt"')), + ConfigLine(config_line=" set wccp disable", parents=("config system interface", ' edit "mgmt"')), + ConfigLine( + config_line=" set drop-overlapped-fragment disable", + parents=("config system interface", ' edit "mgmt"'), + ), + ConfigLine(config_line=" set drop-fragment disable", parents=("config system interface", ' edit "mgmt"')), + ConfigLine(config_line=' edit "ha"', parents=("config system interface",)), + ConfigLine(config_line=' set vdom "root"', parents=("config system interface", ' edit "ha"')), + ConfigLine(config_line=" set vrf 0", parents=("config system interface", ' edit "ha"')), + ConfigLine(config_line=" set fortilink disable", parents=("config system interface", ' edit "ha"')), + ConfigLine(config_line=" set mode static", parents=("config system interface", ' edit "ha"')), + ConfigLine( + config_line=" set dhcp-relay-service disable", parents=("config system interface", ' edit "ha"') + ), + ConfigLine( + config_line=" set management-ip 0.0.0.0 0.0.0.0", parents=("config system interface", ' edit "ha"') + ), + ConfigLine(config_line=" set ip 0.0.0.0 0.0.0.0", parents=("config system interface", ' edit "ha"')), + ConfigLine(config_line=" unset allowaccess", parents=("config system interface", ' edit "ha"')), + ConfigLine(config_line=" set fail-detect disable", parents=("config system interface", ' edit "ha"')), + ConfigLine(config_line=" set arpforward enable", parents=("config system interface", ' edit "ha"')), + ConfigLine( + config_line=" set broadcast-forward disable", parents=("config system interface", ' edit "ha"') + ), + ConfigLine(config_line=" set bfd global", parents=("config system interface", ' edit "ha"')), + ConfigLine(config_line=" set l2forward disable", parents=("config system interface", ' edit "ha"')), + ConfigLine( + config_line=" set icmp-send-redirect enable", parents=("config system interface", ' edit "ha"') + ), + ConfigLine( + config_line=" set icmp-accept-redirect enable", parents=("config system interface", ' edit "ha"') + ), + ConfigLine(config_line=" set vlanforward disable", parents=("config system interface", ' edit "ha"')), + ConfigLine(config_line=" set stpforward disable", parents=("config system interface", ' edit "ha"')), + ConfigLine( + config_line=" set ips-sniffer-mode disable", parents=("config system interface", ' edit "ha"') + ), + ConfigLine(config_line=" set ident-accept disable", parents=("config system interface", ' edit "ha"')), + ConfigLine(config_line=" set ipmac disable", parents=("config system interface", ' edit "ha"')), + ConfigLine(config_line=" set subst disable", parents=("config system interface", ' edit "ha"')), + ConfigLine( + config_line=" set substitute-dst-mac 00:00:00:00:00:00", + parents=("config system interface", ' edit "ha"'), + ), + ConfigLine(config_line=" set status up", parents=("config system interface", ' edit "ha"')), + ConfigLine(config_line=" set netbios-forward disable", parents=("config system interface", ' edit "ha"')), + ConfigLine(config_line=" set wins-ip 0.0.0.0", parents=("config system interface", ' edit "ha"')), + ConfigLine(config_line=" set type physical", parents=("config system interface", ' edit "ha"')), + ConfigLine(config_line=" set netflow-sampler disable", parents=("config system interface", ' edit "ha"')), + ConfigLine(config_line=" set sflow-sampler disable", parents=("config system interface", ' edit "ha"')), + ConfigLine(config_line=" set src-check enable", parents=("config system interface", ' edit "ha"')), + ConfigLine(config_line=" set sample-rate 2000", parents=("config system interface", ' edit "ha"')), + ConfigLine(config_line=" set polling-interval 20", parents=("config system interface", ' edit "ha"')), + ConfigLine(config_line=" set sample-direction both", parents=("config system interface", ' edit "ha"')), + ConfigLine( + config_line=" set explicit-web-proxy disable", parents=("config system interface", ' edit "ha"') + ), + ConfigLine( + config_line=" set explicit-ftp-proxy disable", parents=("config system interface", ' edit "ha"') + ), + ConfigLine( + config_line=" set proxy-captive-portal disable", parents=("config system interface", ' edit "ha"') + ), + ConfigLine(config_line=" set tcp-mss 0", parents=("config system interface", ' edit "ha"')), + ConfigLine(config_line=" set inbandwidth 0", parents=("config system interface", ' edit "ha"')), + ConfigLine(config_line=" set outbandwidth 0", parents=("config system interface", ' edit "ha"')), + ConfigLine( + config_line=" set egress-shaping-profile ''", parents=("config system interface", ' edit "ha"') + ), + ConfigLine( + config_line=" set ingress-shaping-profile ''", parents=("config system interface", ' edit "ha"') + ), + ConfigLine(config_line=" set disconnect-threshold 0", parents=("config system interface", ' edit "ha"')), + ConfigLine(config_line=" set spillover-threshold 0", parents=("config system interface", ' edit "ha"')), + ConfigLine( + config_line=" set ingress-spillover-threshold 0", parents=("config system interface", ' edit "ha"') + ), + ConfigLine(config_line=" set weight 0", parents=("config system interface", ' edit "ha"')), + ConfigLine(config_line=" set external disable", parents=("config system interface", ' edit "ha"')), + ConfigLine(config_line=" set description ''", parents=("config system interface", ' edit "ha"')), + ConfigLine(config_line=" set alias ''", parents=("config system interface", ' edit "ha"')), + ConfigLine(config_line=" set security-mode none", parents=("config system interface", ' edit "ha"')), + ConfigLine( + config_line=" set device-identification disable", parents=("config system interface", ' edit "ha"') + ), + ConfigLine(config_line=" set lldp-reception vdom", parents=("config system interface", ' edit "ha"')), + ConfigLine(config_line=" set lldp-transmission vdom", parents=("config system interface", ' edit "ha"')), + ConfigLine( + config_line=" set estimated-upstream-bandwidth 0", parents=("config system interface", ' edit "ha"') + ), + ConfigLine( + config_line=" set estimated-downstream-bandwidth 0", parents=("config system interface", ' edit "ha"') + ), + ConfigLine( + config_line=" set measured-upstream-bandwidth 0", parents=("config system interface", ' edit "ha"') + ), + ConfigLine( + config_line=" set measured-downstream-bandwidth 0", parents=("config system interface", ' edit "ha"') + ), + ConfigLine( + config_line=" set bandwidth-measure-time 0", parents=("config system interface", ' edit "ha"') + ), + ConfigLine( + config_line=" set monitor-bandwidth disable", parents=("config system interface", ' edit "ha"') + ), + ConfigLine( + config_line=" set vrrp-virtual-mac disable", parents=("config system interface", ' edit "ha"') + ), + ConfigLine(config_line=" set role undefined", parents=("config system interface", ' edit "ha"')), + ConfigLine(config_line=" set snmp-index 2", parents=("config system interface", ' edit "ha"')), + ConfigLine(config_line=" set secondary-IP disable", parents=("config system interface", ' edit "ha"')), + ConfigLine( + config_line=" set preserve-session-route disable", parents=("config system interface", ' edit "ha"') + ), + ConfigLine( + config_line=" set auto-auth-extension-device disable", + parents=("config system interface", ' edit "ha"'), + ), + ConfigLine(config_line=" set ap-discover enable", parents=("config system interface", ' edit "ha"')), + ConfigLine( + config_line=" set ip-managed-by-fortiipam disable", parents=("config system interface", ' edit "ha"') + ), + ConfigLine( + config_line=" set switch-controller-mgmt-vlan 4094", parents=("config system interface", ' edit "ha"') + ), + ConfigLine( + config_line=" set switch-controller-igmp-snooping-proxy disable", + parents=("config system interface", ' edit "ha"'), + ), + ConfigLine( + config_line=" set switch-controller-igmp-snooping-fast-leave disable", + parents=("config system interface", ' edit "ha"'), + ), + ConfigLine(config_line=" set swc-first-create 0", parents=("config system interface", ' edit "ha"')), + ConfigLine(config_line=" config ipv6", parents=("config system interface", ' edit "ha"')), + ConfigLine( + config_line=" set ip6-mode static", + parents=("config system interface", ' edit "ha"', " config ipv6"), + ), + ConfigLine( + config_line=" set nd-mode basic", + parents=("config system interface", ' edit "ha"', " config ipv6"), + ), + ConfigLine( + config_line=" set ip6-address ::/0", + parents=("config system interface", ' edit "ha"', " config ipv6"), + ), + ConfigLine( + config_line=" unset ip6-allowaccess", + parents=("config system interface", ' edit "ha"', " config ipv6"), + ), + ConfigLine( + config_line=" set icmp6-send-redirect enable", + parents=("config system interface", ' edit "ha"', " config ipv6"), + ), + ConfigLine( + config_line=" set ip6-reachable-time 0", + parents=("config system interface", ' edit "ha"', " config ipv6"), + ), + ConfigLine( + config_line=" set ip6-retrans-time 0", + parents=("config system interface", ' edit "ha"', " config ipv6"), + ), + ConfigLine( + config_line=" set ip6-hop-limit 0", + parents=("config system interface", ' edit "ha"', " config ipv6"), + ), + ConfigLine( + config_line=" set dhcp6-prefix-delegation disable", + parents=("config system interface", ' edit "ha"', " config ipv6"), + ), + ConfigLine( + config_line=" set dhcp6-information-request disable", + parents=("config system interface", ' edit "ha"', " config ipv6"), + ), + ConfigLine( + config_line=" set vrrp-virtual-mac6 disable", + parents=("config system interface", ' edit "ha"', " config ipv6"), + ), + ConfigLine( + config_line=" set vrip6_link_local ::", + parents=("config system interface", ' edit "ha"', " config ipv6"), + ), + ConfigLine( + config_line=" set ip6-send-adv disable", + parents=("config system interface", ' edit "ha"', " config ipv6"), + ), + ConfigLine( + config_line=" set autoconf disable", + parents=("config system interface", ' edit "ha"', " config ipv6"), + ), + ConfigLine( + config_line=" set dhcp6-relay-service disable", + parents=("config system interface", ' edit "ha"', " config ipv6"), + ), + ConfigLine(config_line=" set speed auto", parents=("config system interface", ' edit "ha"')), + ConfigLine(config_line=" set mtu-override disable", parents=("config system interface", ' edit "ha"')), + ConfigLine(config_line=" set wccp disable", parents=("config system interface", ' edit "ha"')), + ConfigLine( + config_line=" set drop-overlapped-fragment disable", parents=("config system interface", ' edit "ha"') + ), + ConfigLine(config_line=" set drop-fragment disable", parents=("config system interface", ' edit "ha"')), + ConfigLine(config_line="config system virtual-switch", parents=()), + ConfigLine(config_line=' edit "lan"', parents=("config system virtual-switch",)), + ConfigLine( + config_line=' set physical-switch "sw0"', parents=("config system virtual-switch", ' edit "lan"') + ), + ConfigLine(config_line=" set span disable", parents=("config system virtual-switch", ' edit "lan"')), + ConfigLine(config_line=" config port", parents=("config system virtual-switch", ' edit "lan"')), + ConfigLine( + config_line=' edit "port1"', + parents=("config system virtual-switch", ' edit "lan"', " config port"), + ), + ConfigLine( + config_line=" set speed auto", + parents=("config system virtual-switch", ' edit "lan"', " config port", ' edit "port1"'), + ), + ConfigLine( + config_line=" set status up", + parents=("config system virtual-switch", ' edit "lan"', " config port", ' edit "port1"'), + ), + ConfigLine( + config_line=" set alias ''", + parents=("config system virtual-switch", ' edit "lan"', " config port", ' edit "port1"'), + ), + ConfigLine( + config_line=' edit "port2"', + parents=("config system virtual-switch", ' edit "lan"', " config port"), + ), + ConfigLine( + config_line=" set speed auto", + parents=("config system virtual-switch", ' edit "lan"', " config port", ' edit "port2"'), + ), + ConfigLine( + config_line=" set status up", + parents=("config system virtual-switch", ' edit "lan"', " config port", ' edit "port2"'), + ), + ConfigLine( + config_line=" set alias ''", + parents=("config system virtual-switch", ' edit "lan"', " config port", ' edit "port2"'), + ), + ConfigLine( + config_line=' edit "port3"', + parents=("config system virtual-switch", ' edit "lan"', " config port"), + ), + ConfigLine( + config_line=" set speed auto", + parents=("config system virtual-switch", ' edit "lan"', " config port", ' edit "port3"'), + ), + ConfigLine( + config_line=" set status up", + parents=("config system virtual-switch", ' edit "lan"', " config port", ' edit "port3"'), + ), + ConfigLine( + config_line=" set alias ''", + parents=("config system virtual-switch", ' edit "lan"', " config port", ' edit "port3"'), + ), + ConfigLine( + config_line=' edit "port4"', + parents=("config system virtual-switch", ' edit "lan"', " config port"), + ), + ConfigLine( + config_line=" set speed auto", + parents=("config system virtual-switch", ' edit "lan"', " config port", ' edit "port4"'), + ), + ConfigLine( + config_line=" set status up", + parents=("config system virtual-switch", ' edit "lan"', " config port", ' edit "port4"'), + ), + ConfigLine( + config_line=" set alias ''", + parents=("config system virtual-switch", ' edit "lan"', " config port", ' edit "port4"'), + ), + ConfigLine( + config_line=' edit "port5"', + parents=("config system virtual-switch", ' edit "lan"', " config port"), + ), + ConfigLine( + config_line=" set speed auto", + parents=("config system virtual-switch", ' edit "lan"', " config port", ' edit "port5"'), + ), + ConfigLine( + config_line=" set status up", + parents=("config system virtual-switch", ' edit "lan"', " config port", ' edit "port5"'), + ), + ConfigLine( + config_line=" set alias ''", + parents=("config system virtual-switch", ' edit "lan"', " config port", ' edit "port5"'), + ), + ConfigLine( + config_line=' edit "port6"', + parents=("config system virtual-switch", ' edit "lan"', " config port"), + ), + ConfigLine( + config_line=" set speed auto", + parents=("config system virtual-switch", ' edit "lan"', " config port", ' edit "port6"'), + ), + ConfigLine( + config_line=" set status up", + parents=("config system virtual-switch", ' edit "lan"', " config port", ' edit "port6"'), + ), + ConfigLine( + config_line=" set alias ''", + parents=("config system virtual-switch", ' edit "lan"', " config port", ' edit "port6"'), + ), + ConfigLine( + config_line=' edit "port7"', + parents=("config system virtual-switch", ' edit "lan"', " config port"), + ), + ConfigLine( + config_line=" set speed auto", + parents=("config system virtual-switch", ' edit "lan"', " config port", ' edit "port7"'), + ), + ConfigLine( + config_line=" set status up", + parents=("config system virtual-switch", ' edit "lan"', " config port", ' edit "port7"'), + ), + ConfigLine( + config_line=" set alias ''", + parents=("config system virtual-switch", ' edit "lan"', " config port", ' edit "port7"'), + ), + ConfigLine( + config_line=' edit "port8"', + parents=("config system virtual-switch", ' edit "lan"', " config port"), + ), + ConfigLine( + config_line=" set speed auto", + parents=("config system virtual-switch", ' edit "lan"', " config port", ' edit "port8"'), + ), + ConfigLine( + config_line=" set status up", + parents=("config system virtual-switch", ' edit "lan"', " config port", ' edit "port8"'), + ), + ConfigLine( + config_line=" set alias ''", + parents=("config system virtual-switch", ' edit "lan"', " config port", ' edit "port8"'), + ), + ConfigLine( + config_line=' edit "port9"', + parents=("config system virtual-switch", ' edit "lan"', " config port"), + ), + ConfigLine( + config_line=" set speed auto", + parents=("config system virtual-switch", ' edit "lan"', " config port", ' edit "port9"'), + ), + ConfigLine( + config_line=" set status up", + parents=("config system virtual-switch", ' edit "lan"', " config port", ' edit "port9"'), + ), + ConfigLine( + config_line=" set alias ''", + parents=("config system virtual-switch", ' edit "lan"', " config port", ' edit "port9"'), + ), + ConfigLine( + config_line=' edit "port10"', + parents=("config system virtual-switch", ' edit "lan"', " config port"), + ), + ConfigLine( + config_line=" set speed auto", + parents=("config system virtual-switch", ' edit "lan"', " config port", ' edit "port10"'), + ), + ConfigLine( + config_line=" set status up", + parents=("config system virtual-switch", ' edit "lan"', " config port", ' edit "port10"'), + ), + ConfigLine( + config_line=" set alias ''", + parents=("config system virtual-switch", ' edit "lan"', " config port", ' edit "port10"'), + ), + ConfigLine( + config_line=' edit "port11"', + parents=("config system virtual-switch", ' edit "lan"', " config port"), + ), + ConfigLine( + config_line=" set speed auto", + parents=("config system virtual-switch", ' edit "lan"', " config port", ' edit "port11"'), + ), + ConfigLine( + config_line=" set status up", + parents=("config system virtual-switch", ' edit "lan"', " config port", ' edit "port11"'), + ), + ConfigLine( + config_line=" set alias ''", + parents=("config system virtual-switch", ' edit "lan"', " config port", ' edit "port11"'), + ), + ConfigLine( + config_line=' edit "port12"', + parents=("config system virtual-switch", ' edit "lan"', " config port"), + ), + ConfigLine( + config_line=" set speed auto", + parents=("config system virtual-switch", ' edit "lan"', " config port", ' edit "port12"'), + ), + ConfigLine( + config_line=" set status up", + parents=("config system virtual-switch", ' edit "lan"', " config port", ' edit "port12"'), + ), + ConfigLine( + config_line=" set alias ''", + parents=("config system virtual-switch", ' edit "lan"', " config port", ' edit "port12"'), + ), + ConfigLine( + config_line=' edit "port13"', + parents=("config system virtual-switch", ' edit "lan"', " config port"), + ), + ConfigLine( + config_line=" set speed auto", + parents=("config system virtual-switch", ' edit "lan"', " config port", ' edit "port13"'), + ), + ConfigLine( + config_line=" set status up", + parents=("config system virtual-switch", ' edit "lan"', " config port", ' edit "port13"'), + ), + ConfigLine( + config_line=" set alias ''", + parents=("config system virtual-switch", ' edit "lan"', " config port", ' edit "port13"'), + ), + ConfigLine( + config_line=' edit "port14"', + parents=("config system virtual-switch", ' edit "lan"', " config port"), + ), + ConfigLine( + config_line=" set speed auto", + parents=("config system virtual-switch", ' edit "lan"', " config port", ' edit "port14"'), + ), + ConfigLine( + config_line=" set status up", + parents=("config system virtual-switch", ' edit "lan"', " config port", ' edit "port14"'), + ), + ConfigLine( + config_line=" set alias ''", + parents=("config system virtual-switch", ' edit "lan"', " config port", ' edit "port14"'), + ), + ConfigLine( + config_line=' edit "port15"', + parents=("config system virtual-switch", ' edit "lan"', " config port"), + ), + ConfigLine( + config_line=" set speed auto", + parents=("config system virtual-switch", ' edit "lan"', " config port", ' edit "port15"'), + ), + ConfigLine( + config_line=" set status up", + parents=("config system virtual-switch", ' edit "lan"', " config port", ' edit "port15"'), + ), + ConfigLine( + config_line=" set alias ''", + parents=("config system virtual-switch", ' edit "lan"', " config port", ' edit "port15"'), + ), + ConfigLine( + config_line=' edit "port16"', + parents=("config system virtual-switch", ' edit "lan"', " config port"), + ), + ConfigLine( + config_line=" set speed auto", + parents=("config system virtual-switch", ' edit "lan"', " config port", ' edit "port16"'), + ), + ConfigLine( + config_line=" set status up", + parents=("config system virtual-switch", ' edit "lan"', " config port", ' edit "port16"'), + ), + ConfigLine( + config_line=" set alias ''", + parents=("config system virtual-switch", ' edit "lan"', " config port", ' edit "port16"'), + ), + ConfigLine(config_line='config system replacemsg auth "auth-sms-token-page"', parents=()), + ConfigLine( + config_line=' set buffer "\n\n \n \n \n \n \n \n Firewall Authentication\n \n
\n
\n

SMS Token Code Required

\n
\n \n \n \n \n

%%QUESTION%%

\n
\n \n
\n \n
\n
\n

%%EXTRAINFO%%

\n
\n \n
\n
\n
\n\n"\n', + parents=('config system replacemsg auth "auth-sms-token-page"',), + ), + ConfigLine(config_line=" set header http", parents=('config system replacemsg auth "auth-sms-token-page"',)), + ConfigLine(config_line=" set format html", parents=('config system replacemsg auth "auth-sms-token-page"',)), +] diff --git a/tests/unit/mock/config/parser/fortinet_fortios/fortios_full_sent.txt b/tests/unit/mock/config/parser/fortinet_fortios/fortios_full_sent.txt new file mode 100644 index 00000000..873f75ad --- /dev/null +++ b/tests/unit/mock/config/parser/fortinet_fortios/fortios_full_sent.txt @@ -0,0 +1,466 @@ +config system global + set admin-concurrent enable + set admin-console-timeout 0 + set admin-hsts-max-age 15552000 + set admin-https-pki-required disable + set admin-https-redirect enable + set admin-https-ssl-versions tlsv1-1 tlsv1-2 tlsv1-3 + set admin-lockout-duration 60 + set admin-lockout-threshold 3 + set admin-login-max 100 +end +config wireless-controller global + set name '' + set location '' + set image-download enable + set max-retransmit 3 + set control-message-offload ebp-frame aeroscout-tag ap-list sta-list sta-cap-list stats aeroscout-mu sta-health spectral-analysis + set data-ethernet-II enable + set link-aggregation disable + set mesh-eth-type 8755 + set fiapp-eth-type 5252 + set discovery-mc-addr 224.0.1.140 + set max-clients 0 + set rogue-scan-mac-adjacency 7 + set ipsec-base-ip 169.254.0.1 + set wtp-share disable + set ap-log-server disable +end +config system switch-interface +end +config system lte-modem + set status disable + set extra-init '' + set authtype none + set apn '' + set modem-port 255 +end +config system interface + edit "mgmt" + set vrf 0 + set distance 5 + set priority 0 + set dhcp-relay-service disable + set ip 10.0.0.41 255.255.252.0 + set allowaccess ping https ssh snmp http fgfm + set fail-detect disable + set arpforward enable + set broadcast-forward disable + set bfd global + set l2forward disable + set icmp-send-redirect enable + set icmp-accept-redirect enable + set vlanforward disable + set stpforward disable + set ips-sniffer-mode disable + set ident-accept disable + set ipmac disable + set subst disable + set substitute-dst-mac 00:00:00:00:00:00 + set status up + set netbios-forward disable + set wins-ip 0.0.0.0 + set type physical + set dedicated-to management + set netflow-sampler disable + set sflow-sampler disable + set src-check enable + set sample-rate 2000 + set polling-interval 20 + set sample-direction both + set explicit-web-proxy disable + set explicit-ftp-proxy disable + set proxy-captive-portal disable + set tcp-mss 0 + set inbandwidth 0 + set outbandwidth 0 + set egress-shaping-profile '' + set ingress-shaping-profile '' + set disconnect-threshold 0 + set spillover-threshold 0 + set ingress-spillover-threshold 0 + set weight 0 + set external disable + set description '' + set alias '' + set device-identification disable + set lldp-reception vdom + set lldp-transmission vdom + set estimated-upstream-bandwidth 0 + set estimated-downstream-bandwidth 0 + set measured-upstream-bandwidth 0 + set measured-downstream-bandwidth 0 + set bandwidth-measure-time 0 + set monitor-bandwidth disable + set vrrp-virtual-mac disable + set role lan + set snmp-index 1 + set secondary-IP disable + set preserve-session-route disable + set auto-auth-extension-device disable + set ap-discover enable + set switch-controller-igmp-snooping-proxy disable + set switch-controller-igmp-snooping-fast-leave disable + config ipv6 + set ip6-mode static + set nd-mode basic + set ip6-address ::/0 + unset ip6-allowaccess + set icmp6-send-redirect enable + set ip6-reachable-time 0 + set ip6-retrans-time 0 + set ip6-hop-limit 0 + set vrrp-virtual-mac6 disable + set vrip6_link_local :: + set ip6-send-adv disable + set autoconf disable + end + set defaultgw enable + set dns-server-override enable + set speed auto + set trust-ip-1 0.0.0.0 0.0.0.0 + set trust-ip-2 0.0.0.0 0.0.0.0 + set trust-ip-3 0.0.0.0 0.0.0.0 + set trust-ip6-1 ::/0 + set trust-ip6-2 ::/0 + set trust-ip6-3 ::/0 + set mtu-override disable + set wccp disable + set drop-overlapped-fragment disable + set drop-fragment disable + next + edit "ha" + set vdom "root" + set vrf 0 + set fortilink disable + set mode static + set dhcp-relay-service disable + set management-ip 0.0.0.0 0.0.0.0 + set ip 0.0.0.0 0.0.0.0 + unset allowaccess + set fail-detect disable + set arpforward enable + set broadcast-forward disable + set bfd global + set l2forward disable + set icmp-send-redirect enable + set icmp-accept-redirect enable + set vlanforward disable + set stpforward disable + set ips-sniffer-mode disable + set ident-accept disable + set ipmac disable + set subst disable + set substitute-dst-mac 00:00:00:00:00:00 + set status up + set netbios-forward disable + set wins-ip 0.0.0.0 + set type physical + set netflow-sampler disable + set sflow-sampler disable + set src-check enable + set sample-rate 2000 + set polling-interval 20 + set sample-direction both + set explicit-web-proxy disable + set explicit-ftp-proxy disable + set proxy-captive-portal disable + set tcp-mss 0 + set inbandwidth 0 + set outbandwidth 0 + set egress-shaping-profile '' + set ingress-shaping-profile '' + set disconnect-threshold 0 + set spillover-threshold 0 + set ingress-spillover-threshold 0 + set weight 0 + set external disable + set description '' + set alias '' + set security-mode none + set device-identification disable + set lldp-reception vdom + set lldp-transmission vdom + set estimated-upstream-bandwidth 0 + set estimated-downstream-bandwidth 0 + set measured-upstream-bandwidth 0 + set measured-downstream-bandwidth 0 + set bandwidth-measure-time 0 + set monitor-bandwidth disable + set vrrp-virtual-mac disable + set role undefined + set snmp-index 2 + set secondary-IP disable + set preserve-session-route disable + set auto-auth-extension-device disable + set ap-discover enable + set ip-managed-by-fortiipam disable + set switch-controller-mgmt-vlan 4094 + set switch-controller-igmp-snooping-proxy disable + set switch-controller-igmp-snooping-fast-leave disable + set swc-first-create 0 + config ipv6 + set ip6-mode static + set nd-mode basic + set ip6-address ::/0 + unset ip6-allowaccess + set icmp6-send-redirect enable + set ip6-reachable-time 0 + set ip6-retrans-time 0 + set ip6-hop-limit 0 + set dhcp6-prefix-delegation disable + set dhcp6-information-request disable + set vrrp-virtual-mac6 disable + set vrip6_link_local :: + set ip6-send-adv disable + set autoconf disable + set dhcp6-relay-service disable + end + set speed auto + set mtu-override disable + set wccp disable + set drop-overlapped-fragment disable + set drop-fragment disable + next +end +config system virtual-switch + edit "lan" + set physical-switch "sw0" + set span disable + config port + edit "port1" + set speed auto + set status up + set alias '' + next + edit "port2" + set speed auto + set status up + set alias '' + next + edit "port3" + set speed auto + set status up + set alias '' + next + edit "port4" + set speed auto + set status up + set alias '' + next + edit "port5" + set speed auto + set status up + set alias '' + next + edit "port6" + set speed auto + set status up + set alias '' + next + edit "port7" + set speed auto + set status up + set alias '' + next + edit "port8" + set speed auto + set status up + set alias '' + next + edit "port9" + set speed auto + set status up + set alias '' + next + edit "port10" + set speed auto + set status up + set alias '' + next + edit "port11" + set speed auto + set status up + set alias '' + next + edit "port12" + set speed auto + set status up + set alias '' + next + edit "port13" + set speed auto + set status up + set alias '' + next + edit "port14" + set speed auto + set status up + set alias '' + next + edit "port15" + set speed auto + set status up + set alias '' + next + edit "port16" + set speed auto + set status up + set alias '' + next + end + next +end +config system replacemsg auth "auth-sms-token-page" + set buffer " + + + + + + + + Firewall Authentication + +
+
+

SMS Token Code Required

+
+ + + + +

%%QUESTION%%

+
+ +
+ +
+
+

%%EXTRAINFO%%

+
+ +
+
+
+ +" + set header http + set format html +end \ No newline at end of file diff --git a/tests/unit/test_compliance.py b/tests/unit/test_compliance.py index a03940a4..c4d7d362 100644 --- a/tests/unit/test_compliance.py +++ b/tests/unit/test_compliance.py @@ -1,9 +1,8 @@ """Test for the config compliance functions.""" -import os import glob +import os import pytest - from netutils.config import compliance MOCK_DIR = os.path.join(os.path.dirname(os.path.realpath(__file__)), "mock", "config", "compliance") diff --git a/tests/unit/test_parser.py b/tests/unit/test_parser.py index c4a323a1..862f643b 100644 --- a/tests/unit/test_parser.py +++ b/tests/unit/test_parser.py @@ -1,9 +1,8 @@ """Test for the network os parser functions.""" -import os import glob +import os import pytest - from netutils.config import compliance MOCK_DIR = os.path.join(os.path.dirname(os.path.realpath(__file__)), "mock", "config", "parser") From 8663a4ae64ea9c42e6bce6cb9d339a18cf2fa65d Mon Sep 17 00:00:00 2001 From: Adam Byczkowski <38091261+qduk@users.noreply.github.com> Date: Sat, 13 Nov 2021 09:34:53 -0600 Subject: [PATCH 11/12] Docs contribution update (#71) * Updated README Co-authored-by: Adam Byczkowski Co-authored-by: Jeff Kala <48843785+jeffkala@users.noreply.github.com> --- README.md | 33 +++++++++++++++++++++++++++++- docs/source/contributing/index.rst | 2 +- tests/unit/test_docs.py | 2 +- 3 files changed, 34 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index f3993fbb..52656817 100644 --- a/README.md +++ b/README.md @@ -59,7 +59,7 @@ The following function will help in deploying list of VLANs and match the config >>> vlan_cfg = vlanlist_to_config([1, 2, 3, 5, 6, 1000, 1002, 1004, 1006, 1008, 1010, 1012, 1014, 1016, 1018]) >>> >>> vlan_cfg -['1-3,5,6,1000,1002,1004,1006,1008,1010,1012,1014', '1016,1018'] +["1-3,5,6,1000,1002,1004,1006,1008,1010,1012,1014", "1016,1018"] >>> >>> for index, line in enumerate(vlan_cfg): ... if index == 0: @@ -150,6 +150,37 @@ The project is following Network to Code software development guidelines and are - Black, Pylint, Bandit, flake8, and pydocstyle for Python linting and formatting. - pytest, coverage, and unittest for unit tests. +There are a number of things that are required in order to have a successfull PR. + +- All new functions must contain at least 1 example in their docstrings. +- Docstrings must conform to the google docstring [convention](https://google.github.io/styleguide/pyguide.html#381-docstrings). +- Unit test for newly added functions are required. +- If applicable, tests related to config parsing and compliuance must be added. +- Update the jinja2 filter for any new functions (see below for details). +- If you create a new file in the `netutils` folder, you must create a new folder and `index.rst` in the docs folde r(see below for details). +- Your PR must not introduce any required dependencies. You can introduce optional or development dependencies. + +## Adding to the jinja2 filter function + +To add a new function to the jinja2 filter, add a new entry to the `_JINJA2_FUNCTION_MAPPINGS` located in the `utils.py` file. When adding an entry, the key corresponds with the name to call the function and the value to the path to find the function. + +## Adding docs for a new python file + +If adding a new python file, the docs must be updated to account for the new file. + +1. Create a new folder in `docs/source/netutils` matching the name of your new file. +2. Create an `index.rst` file in that folder. +3. Add the following to the newly created file. + +```python +############################# +# ENTER THE TITLE OF THE PAGE +############################## + +.. automodule:: netutils.newfile + :members: +``` + ## CLI Helper Commands The project features a CLI helper based on [invoke](http://www.pyinvoke.org/) to help setup the development environment. The commands are listed below in 3 categories: diff --git a/docs/source/contributing/index.rst b/docs/source/contributing/index.rst index 3a618164..59337f0e 100644 --- a/docs/source/contributing/index.rst +++ b/docs/source/contributing/index.rst @@ -4,4 +4,4 @@ Contributing .. mdinclude:: ../../../README.md :start-line: 144 - :end-line: 197 \ No newline at end of file + :end-line: 228 \ No newline at end of file diff --git a/tests/unit/test_docs.py b/tests/unit/test_docs.py index 52b86b9e..e2f90808 100644 --- a/tests/unit/test_docs.py +++ b/tests/unit/test_docs.py @@ -48,7 +48,7 @@ "name": "contribution", "start_line": 145, "start_value": "Pull requests are welcomed and automatically built and tested against multiple versions of Python through TravisCI.\n", - "end_line": 197, + "end_line": 228, "end_value": "Sign up [here](http://slack.networktocode.com/)\n", }, ] From 8ea1182b10327a945b22c6f02c809d52fbd5c974 Mon Sep 17 00:00:00 2001 From: itdependsnetworks Date: Thu, 11 Nov 2021 23:07:58 -0500 Subject: [PATCH 12/12] Bump version, fix ip padding on ip to hex conversion Bump version, fix ip padding on ip to hex conversion, update changelog, modify ci --- .github/workflows/ci.yml | 42 ++++++++++++++++++++--------------- CHANGELOG.md | 22 ++++++++++++++++++ docs/source/sphinxext/exec.py | 2 +- netutils/__init__.py | 2 +- netutils/config/compliance.py | 8 +++---- netutils/config/parser.py | 2 +- netutils/constants.py | 4 ++-- netutils/interface.py | 5 +++-- netutils/ip.py | 9 ++++---- netutils/password.py | 9 ++++---- netutils/vlan.py | 4 ++-- pyproject.toml | 3 ++- tests/unit/conftest.py | 4 ++-- tests/unit/test_docs.py | 2 +- tests/unit/test_ip.py | 2 +- 15 files changed, 76 insertions(+), 44 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0a89c8bb..f1709971 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -117,16 +117,18 @@ jobs: uses: "actions/checkout@v2" - name: "Setup environment" uses: "networktocode/gh-action-setup-poetry-environment@v2" - - name: "Cache Docker images" - uses: "actions/cache@v2" - id: "cached-docker-images" - with: - path: "/tmp/docker" - key: "${{ runner.os }}-docker-${{ matrix.python-version }}-${{ hashFiles('./Dockerfile') }}" - - name: "Load docker image" - run: "docker load < /tmp/docker/netutils-py${{ matrix.python-version }}.tar" - - name: "Show docker images" - run: "docker image ls" + # - name: "Cache Docker images" + # uses: "actions/cache@v2" + # id: "cached-docker-images" + # with: + # path: "/tmp/docker" + # key: "${{ runner.os }}-docker-${{ matrix.python-version }}-${{ hashFiles('./Dockerfile') }}" + # - name: "Load docker image" + # run: "docker load < /tmp/docker/netutils-py${{ matrix.python-version }}.tar" + # - name: "Show docker images" + # run: "docker image ls" + - name: "Build Container" + run: "poetry run invoke build" - name: "Linting: Pylint" run: "poetry run invoke pylint" needs: @@ -144,14 +146,18 @@ jobs: uses: "actions/checkout@v2" - name: "Setup environment" uses: "networktocode/gh-action-setup-poetry-environment@v2" - - name: "Cache Docker images" - uses: "actions/cache@v2" - id: "cached-docker-images" - with: - path: "/tmp/docker" - key: "${{ runner.os }}-docker-${{ matrix.python-version }}-${{ hashFiles('./Dockerfile') }}" - - name: "Load docker image" - run: "docker load < /tmp/docker/netutils-py${{ matrix.python-version }}.tar" + # - name: "Cache Docker images" + # uses: "actions/cache@v2" + # id: "cached-docker-images" + # with: + # path: "/tmp/docker" + # key: "${{ runner.os }}-docker-${{ matrix.python-version }}-${{ hashFiles('./Dockerfile') }}" + # - name: "Load docker image" + # run: "docker load < /tmp/docker/netutils-py${{ matrix.python-version }}.tar" + - name: "Build Container" + run: "poetry run invoke build" + - name: "Linting: Pylint" + run: "poetry run invoke pylint" - name: "Run Tests" run: "poetry run invoke pytest" needs: diff --git a/CHANGELOG.md b/CHANGELOG.md index 551293b0..419327bc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,27 @@ # Changelog +## v0.2.4 - 2021-11 + +### Added + +- #33 Add interface range compress function +- #53 Add get peer address function +- #59 Add bandwidth converting function +- #65 Added Docker caching +- #68 Add Fortinet Fortios Parser support + +### Changed + +- #64 CI implementation on GitHub actions + +### Fixed + +- #52 Update pyproject.toml build-server +- #55 update version in toml and init files +- #63 Fix lack of zero padding on ip to binary conversion +- #70 Fix lack of zero padding on ip to hex conversion +- #68 Update Black pinning + ## v0.2.3 - 2021-09 ### Added diff --git a/docs/source/sphinxext/exec.py b/docs/source/sphinxext/exec.py index f6929b15..6cdf5b26 100644 --- a/docs/source/sphinxext/exec.py +++ b/docs/source/sphinxext/exec.py @@ -33,7 +33,7 @@ def run(self): return [ nodes.error( None, - nodes.paragraph(text="Unable to execute python code at %s:%d:" % (basename(source), self.lineno)), + nodes.paragraph(text=f"Unable to execute python code at {basename(source)}:{self.lineno}:"), nodes.paragraph(text=str(sys.exc_info()[1])), ) ] diff --git a/netutils/__init__.py b/netutils/__init__.py index 2c1b8acd..41c1aec3 100644 --- a/netutils/__init__.py +++ b/netutils/__init__.py @@ -1,3 +1,3 @@ """Initialization file for library.""" -__version__ = "0.2.3" +__version__ = "0.2.4" diff --git a/netutils/config/compliance.py b/netutils/config/compliance.py index 84bac72d..5a0257fe 100644 --- a/netutils/config/compliance.py +++ b/netutils/config/compliance.py @@ -99,7 +99,7 @@ def _is_feature_ordered_compliant(feature_intended_cfg, feature_actual_cfg): def _open_file_config(cfg_path): """Open config file from local disk.""" try: - with open(cfg_path) as filehandler: + with open(cfg_path, encoding="utf-8") as filehandler: device_cfg = filehandler.read() except IOError: return False @@ -168,7 +168,7 @@ def compliance(features, backup, intended, network_os, cfg_type="file"): backup_cfg = backup intended_cfg = intended - compliance_results = dict() + compliance_results = {} for feature in features: backup_str = section_config(feature, backup_cfg, network_os) @@ -207,7 +207,7 @@ def config_section_not_parsed(features, device_cfg, network_os): {'remaining_cfg': '!\naccess-list 1 permit 10.10.10.10\naccess-list 1 permit 10.10.10.11', 'section_not_found': []} """ remaining_cfg = device_cfg - section_not_found = list() + section_not_found = [] for feature in features: feature_cfg = section_config(feature, device_cfg, network_os) if not feature_cfg: @@ -357,7 +357,7 @@ def find_unordered_cfg_lines(intended_cfg, actual_cfg): """ intended_lines = intended_cfg.splitlines() actual_lines = actual_cfg.splitlines() - unordered_lines = list() + unordered_lines = [] if len(intended_lines) == len(actual_lines): # Process to find actual lines that are misordered unordered_lines = [(e1, e2) for e1, e2 in zip(intended_lines, actual_lines) if e1 != e2] diff --git a/netutils/config/parser.py b/netutils/config/parser.py index ef466b61..cfa7990f 100644 --- a/netutils/config/parser.py +++ b/netutils/config/parser.py @@ -172,7 +172,7 @@ def _remove_parents(self, line, current_spaces): previous_parent = self._current_parents[-deindent_level] previous_indent = self.get_leading_space_count(previous_parent) except IndexError: - raise IndexError("\nValidate the first line does not begin with a space" "\n{}\n".format(line)) + raise IndexError(f"\nValidate the first line does not begin with a space\n{line}\n") parents = self._current_parents[:-deindent_level] or (self._current_parents[0],) return parents diff --git a/netutils/constants.py b/netutils/constants.py index f49cfec9..3089369f 100644 --- a/netutils/constants.py +++ b/netutils/constants.py @@ -4,8 +4,8 @@ from netutils import __file__ as netutils_file # Load the PROTOCOLS json file. -with open("/".join([dirname(netutils_file), "protocols.json"])) as f: - PROTOCOLS = json.loads(f.read()) +with open("/".join([dirname(netutils_file), "protocols.json"]), encoding="utf-8") as fh: + PROTOCOLS = json.loads(fh.read()) # This variable provides mapping for known interface variants, to the associated long form. BASE_INTERFACES = { diff --git a/netutils/interface.py b/netutils/interface.py index 9e2e33b2..d0579261 100644 --- a/netutils/interface.py +++ b/netutils/interface.py @@ -2,7 +2,7 @@ import itertools import re import typing as t -from abc import ABC, abstractmethod, abstractproperty +from abc import ABC, abstractmethod from functools import total_ordering from operator import itemgetter from .constants import BASE_INTERFACES, REVERSE_MAPPING @@ -253,7 +253,8 @@ def __lt__(self, other) -> bool: # noqa: D105 def __eq__(self, other) -> bool: # noqa: D105 return self.weight == other.weight and self.val == other.val - @abstractproperty + @property + @abstractmethod def weight(self) -> int: """Weight property.""" ... diff --git a/netutils/ip.py b/netutils/ip.py index 95de4ff6..b7ecaa5b 100644 --- a/netutils/ip.py +++ b/netutils/ip.py @@ -15,10 +15,11 @@ def ip_to_hex(ip): Example: >>> from netutils.ip import ip_to_hex >>> ip_to_hex("10.100.100.100") - 'a646464' + '0a646464' >>> """ - return str(hex(int(ipaddress.ip_address(ip))))[2:] + ip_obj = ipaddress.ip_address(ip) + return str(hex(int(ip_obj)))[2:].zfill(int(ip_obj.max_prefixlen / 4)) def ip_addition(ip, val): @@ -243,7 +244,7 @@ def get_first_usable(ip_network): >>> """ net = ipaddress.ip_network(ip_network) - if net.prefixlen == 31 or net.prefixlen == 127: + if net.prefixlen in [31, 127]: return str(net[0]) return str(net[1]) @@ -302,7 +303,7 @@ def get_usable_range(ip_network): >>> """ net = ipaddress.ip_network(ip_network) - if net.prefixlen == 31 or net.prefixlen == 127: + if net.prefixlen in [31, 127]: lower_bound = str(net[0]) upper_bound = str(net[1]) else: diff --git a/netutils/password.py b/netutils/password.py index 5ee58285..814eaf6c 100644 --- a/netutils/password.py +++ b/netutils/password.py @@ -185,7 +185,7 @@ def encrypt_type5(unencrypted_password, salt=None, salt_len=4): if not salt: salt = "".join(secrets.choice(ALPHABET) for i in range(salt_len)) elif not set(salt) <= set(ALPHABET): - raise ValueError("type5_pw salt used inproper characters, must be one of %s" % (ALPHABET)) + raise ValueError(f"type5_pw salt used inproper characters, must be one of {ALPHABET}") return crypt.crypt(unencrypted_password, f"$1${salt}$") @@ -207,9 +207,10 @@ def encrypt_type7(unencrypted_password, salt=None): """ if not salt: salt = random.randrange(0, 15) # nosec - encrypted_password = "%02x" % salt + encrypted_password = "%02x" % salt # pylint: disable=consider-using-f-string for i, _ in enumerate(unencrypted_password): - encrypted_password += "%02x" % (ord(unencrypted_password[i]) ^ XLAT[salt]) + hex_password = "%02x" % (ord(unencrypted_password[i]) ^ XLAT[salt]) # pylint: disable=consider-using-f-string + encrypted_password += hex_password salt += 1 if salt == 51: salt = 0 @@ -233,5 +234,5 @@ def get_hash_salt(encrypted_password): """ split_password = encrypted_password.split("$") if len(split_password) != 4: - raise ValueError("Could not parse salt out password correctly from {0}".format(encrypted_password)) + raise ValueError(f"Could not parse salt out password correctly from {encrypted_password}") return split_password[2] diff --git a/netutils/vlan.py b/netutils/vlan.py index fec89a27..a937ca6a 100644 --- a/netutils/vlan.py +++ b/netutils/vlan.py @@ -31,12 +31,12 @@ def vlanlist_to_config(vlan_list, first_line_len=48, other_line_len=44): raise ValueError("Valid VLAN range is 1-4094") # Group consecutive VLANs - vlan_groups = list() + vlan_groups = [] for _, vlan in groupby(enumerate(clean_vlan_list), lambda vlan: vlan[0] - vlan[1]): vlan_groups.append(list(map(itemgetter(1), vlan))) # Create VLAN portion of config - vlan_strings = list() + vlan_strings = [] for group in vlan_groups: if len(group) == 1: vlan_strings.append(f"{group[0]}") diff --git a/pyproject.toml b/pyproject.toml index f50f26f4..2acfb75d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "netutils" -version = "0.2.3" +version = "0.2.4" description = "Common helper functions useful in network automation." authors = ["Network to Code, LLC "] license = "Apache-2.0" @@ -78,6 +78,7 @@ good-names="i,ip,j,k,ex,Run,_" disable = """, line-too-long, bad-continuation, + consider-iterating-dictionary, """ [tool.pylint.miscellaneous] diff --git a/tests/unit/conftest.py b/tests/unit/conftest.py index d9a11011..059e4710 100644 --- a/tests/unit/conftest.py +++ b/tests/unit/conftest.py @@ -19,7 +19,7 @@ def _method(_file): Returns: dict: The data structure from the JSON file. """ - with open(_file) as file: + with open(_file, encoding="utf-8") as file: data = json.load(file) return data @@ -39,7 +39,7 @@ def _method(_file): Returns: str: The data structure from the text file. """ - with open(_file) as file: + with open(_file, encoding="utf-8") as file: data = file.read() return data diff --git a/tests/unit/test_docs.py b/tests/unit/test_docs.py index e2f90808..3d5a7ffa 100644 --- a/tests/unit/test_docs.py +++ b/tests/unit/test_docs.py @@ -54,7 +54,7 @@ ] -with open("README.md", "r") as file: +with open("README.md", "r", encoding="utf-8") as file: README_LIST = file.readlines() README_LIST.insert(0, "") diff --git a/tests/unit/test_ip.py b/tests/unit/test_ip.py index a1f69080..c6b8b49b 100644 --- a/tests/unit/test_ip.py +++ b/tests/unit/test_ip.py @@ -6,7 +6,7 @@ IP_TO_HEX = [ { "sent": {"ip": "10.1.1.1"}, - "received": "a010101", + "received": "0a010101", }, { "sent": {"ip": "2001:db8:3333:4444:5555:6666:7777:8888"},