diff --git a/netdiff/__init__.py b/netdiff/__init__.py index 99b7287..48c9c02 100644 --- a/netdiff/__init__.py +++ b/netdiff/__init__.py @@ -6,4 +6,5 @@ from .parsers.olsr import OlsrParser # noqa from .parsers.openvpn import OpenvpnParser # noqa from .parsers.wireguard import WireguardParser # noqa +from .parsers.zerotier import ZeroTierParser # noqa from .utils import diff # noqa diff --git a/netdiff/parsers/zerotier.py b/netdiff/parsers/zerotier.py new file mode 100644 index 0000000..5b7e018 --- /dev/null +++ b/netdiff/parsers/zerotier.py @@ -0,0 +1,42 @@ +import json +from copy import deepcopy + +from netdiff.parsers.base import BaseParser + + +class ZeroTierParser(BaseParser): + protocol = 'ZeroTier Peers Information' + version = '1' + metric = 'static' + + def to_python(self, data): + if isinstance(data, list): + return data + return super().to_python(data) + + def parse(self, data): + graph = self._init_graph() + for peer in data: + if peer.get('role') != 'LEAF': + continue + for path in peer.get('paths'): + if ( + not path.get('expired') + and path.get('active') + and path.get('preferred') + ): + peer_address = peer.get('address') + peer_properties = dict( + label=peer_address, + address=peer_address, + role=peer.get('role'), + version=peer.get('version'), + tunneled=peer.get('tunneled'), + isBonded=peer.get('isBonded'), + ) + graph.add_node('controller', label='controller') + graph.add_node(peer_address, **peer_properties) + graph.add_edge( + 'controller', peer_address, weight=peer.get('latency'), **path + ) + return graph diff --git a/tests/static/zt-peers-diff.json b/tests/static/zt-peers-diff.json new file mode 100644 index 0000000..1989df7 --- /dev/null +++ b/tests/static/zt-peers-diff.json @@ -0,0 +1,300 @@ +[ + { + "address": "3504e2b2e2", + "isBonded": false, + "latency": 1, + "paths": [ + { + "active": true, + "address": "192.168.56.3/9993", + "expired": false, + "lastReceive": 1691493323678, + "lastSend": 1691493323677, + "localSocket": 94434869092208, + "preferred": false, + "trustedPathId": 0 + }, + { + "active": true, + "address": "192.168.56.3/9993", + "expired": false, + "lastReceive": 1691493323678, + "lastSend": 1691493323677, + "localSocket": 94434869109504, + "preferred": false, + "trustedPathId": 0 + }, + { + "active": true, + "address": "192.168.56.3/9993", + "expired": false, + "lastReceive": 1691493323678, + "lastSend": 1691493323677, + "localSocket": 94434869108304, + "preferred": false, + "trustedPathId": 0 + }, + { + "active": true, + "address": "192.168.56.1/44221", + "expired": false, + "lastReceive": 1691493323678, + "lastSend": 1691493323677, + "localSocket": 94434869108304, + "preferred": true, + "trustedPathId": 0 + }, + { + "active": true, + "address": "192.168.56.1/44221", + "expired": false, + "lastReceive": 1691493308664, + "lastSend": 1691493325843, + "localSocket": 94434869092208, + "preferred": false, + "trustedPathId": 0 + }, + { + "active": true, + "address": "192.168.56.1/44221", + "expired": false, + "lastReceive": 1691493293653, + "lastSend": 1691493310824, + "localSocket": 94434869109504, + "preferred": false, + "trustedPathId": 0 + }, + { + "active": true, + "address": "fd60:8d57:65e9::1/9993", + "expired": false, + "lastReceive": 1691493323678, + "lastSend": 1691493323677, + "localSocket": 94434869123504, + "preferred": false, + "trustedPathId": 0 + }, + { + "active": true, + "address": "fd60:8d57:65e9::1/9993", + "expired": false, + "lastReceive": 1691493323678, + "lastSend": 1691493323677, + "localSocket": 94434869095936, + "preferred": false, + "trustedPathId": 0 + }, + { + "active": true, + "address": "fd60:8d57:65e9::1/9993", + "expired": false, + "lastReceive": 1691493325843, + "lastSend": 1691493323677, + "localSocket": 94434869119488, + "preferred": false, + "trustedPathId": 0 + } + ], + "role": "LEAF", + "tunneled": false, + "version": "1.10.3", + "versionMajor": 1, + "versionMinor": 10, + "versionRev": 3 + }, + { + "address": "4a9e1c6f14", + "isBonded": false, + "latency": 0, + "paths": [ + { + "active": true, + "address": "192.168.56.2/9993", + "expired": false, + "lastReceive": 1691493318673, + "lastSend": 1691493318672, + "localSocket": 94434869092208, + "preferred": false, + "trustedPathId": 0 + }, + { + "active": true, + "address": "192.168.56.2/9993", + "expired": false, + "lastReceive": 1691493318673, + "lastSend": 1691493318672, + "localSocket": 94434869109504, + "preferred": false, + "trustedPathId": 0 + }, + { + "active": true, + "address": "192.168.56.2/9993", + "expired": false, + "lastReceive": 1691493323678, + "lastSend": 1691493323677, + "localSocket": 94434869108304, + "preferred": true, + "trustedPathId": 0 + }, + { + "active": true, + "address": "fdcf:5771:404::1/9993", + "expired": false, + "lastReceive": 1691493318673, + "lastSend": 1691493318672, + "localSocket": 94434869107632, + "preferred": false, + "trustedPathId": 0 + }, + { + "active": true, + "address": "fdcf:5771:404::1/9993", + "expired": false, + "lastReceive": 1691493318673, + "lastSend": 1691493318672, + "localSocket": 94434869092560, + "preferred": false, + "trustedPathId": 0 + }, + { + "active": true, + "address": "fdcf:5771:404::1/9993", + "expired": false, + "lastReceive": 1691493318673, + "lastSend": 1691493318672, + "localSocket": 94434869086096, + "preferred": false, + "trustedPathId": 0 + }, + { + "active": true, + "address": "192.168.56.1/53571", + "expired": false, + "lastReceive": 1691493318673, + "lastSend": 1691493318672, + "localSocket": 94434869092208, + "preferred": false, + "trustedPathId": 0 + }, + { + "active": true, + "address": "192.168.56.1/53571", + "expired": false, + "lastReceive": 1691493318673, + "lastSend": 1691493318672, + "localSocket": 94434869109504, + "preferred": false, + "trustedPathId": 0 + }, + { + "active": true, + "address": "192.168.56.1/53571", + "expired": false, + "lastReceive": 1691493318673, + "lastSend": 1691493318672, + "localSocket": 94434869108304, + "preferred": false, + "trustedPathId": 0 + } + ], + "role": "LEAF", + "tunneled": false, + "version": "1.10.3", + "versionMajor": 1, + "versionMinor": 10, + "versionRev": 3 + }, + { + "address": "62f865ae71", + "isBonded": false, + "latency": 297, + "paths": [ + { + "active": true, + "address": "50.7.252.138/9993", + "expired": false, + "lastReceive": 1691493263920, + "lastSend": 1691493323677, + "localSocket": 94434869079296, + "preferred": true, + "trustedPathId": 0 + } + ], + "role": "PLANET", + "tunneled": false, + "version": "-1.-1.-1", + "versionMajor": -1, + "versionMinor": -1, + "versionRev": -1 + }, + { + "address": "778cde7190", + "isBonded": false, + "latency": 276, + "paths": [ + { + "active": true, + "address": "103.195.103.66/9993", + "expired": false, + "lastReceive": 1691493263899, + "lastSend": 1691493323677, + "localSocket": 94434869085344, + "preferred": true, + "trustedPathId": 0 + } + ], + "role": "PLANET", + "tunneled": false, + "version": "-1.-1.-1", + "versionMajor": -1, + "versionMinor": -1, + "versionRev": -1 + }, + { + "address": "cafe04eba9", + "isBonded": false, + "latency": 163, + "paths": [ + { + "active": true, + "address": "84.17.53.155/9993", + "expired": false, + "lastReceive": 1691493263786, + "lastSend": 1691493327124, + "localSocket": 94434869086304, + "preferred": true, + "trustedPathId": 0 + } + ], + "role": "PLANET", + "tunneled": false, + "version": "-1.-1.-1", + "versionMajor": -1, + "versionMinor": -1, + "versionRev": -1 + }, + { + "address": "cafe9efeb9", + "isBonded": false, + "latency": 241, + "paths": [ + { + "active": true, + "address": "104.194.8.134/9993", + "expired": false, + "lastReceive": 1691493263864, + "lastSend": 1691493323677, + "localSocket": 94434869086304, + "preferred": true, + "trustedPathId": 0 + } + ], + "role": "PLANET", + "tunneled": false, + "version": "-1.-1.-1", + "versionMajor": -1, + "versionMinor": -1, + "versionRev": -1 + } + ] diff --git a/tests/static/zt-peers.json b/tests/static/zt-peers.json new file mode 100644 index 0000000..e052719 --- /dev/null +++ b/tests/static/zt-peers.json @@ -0,0 +1,202 @@ +[ + { + "address": "3504e2b2e2", + "isBonded": false, + "latency": 1, + "paths": [ + { + "active": true, + "address": "fd60:8d57:65e9::1/9993", + "expired": false, + "lastReceive": 1691413701307, + "lastSend": 1691413701307, + "localSocket": 94898166342576, + "preferred": true, + "trustedPathId": 0 + }, + { + "active": true, + "address": "192.168.56.1/46076", + "expired": false, + "lastReceive": 1691413695846, + "lastSend": 1691413695844, + "localSocket": 94898166354352, + "preferred": false, + "trustedPathId": 0 + }, + { + "active": true, + "address": "192.168.56.1/46076", + "expired": false, + "lastReceive": 1691413695846, + "lastSend": 1691413695844, + "localSocket": 94898166357312, + "preferred": false, + "trustedPathId": 0 + } + ], + "role": "LEAF", + "tunneled": false, + "version": "1.10.3", + "versionMajor": 1, + "versionMinor": 10, + "versionRev": 3 + }, + { + "address": "4a9e1c6f14", + "isBonded": false, + "latency": 1, + "paths": [ + { + "active": true, + "address": "192.168.56.2/9993", + "expired": false, + "lastReceive": 1691413695845, + "lastSend": 1691413695844, + "localSocket": 94898166237728, + "preferred": false, + "trustedPathId": 0 + }, + { + "active": true, + "address": "192.168.9.109/5216", + "expired": false, + "lastReceive": 1691413695845, + "lastSend": 1691413700597, + "localSocket": 94898166193600, + "preferred": true, + "trustedPathId": 0 + } + ], + "role": "LEAF", + "tunneled": false, + "version": "1.10.3", + "versionMajor": 1, + "versionMinor": 10, + "versionRev": 3 + }, + { + "address": "62f865ae71", + "isBonded": false, + "latency": 299, + "paths": [ + { + "active": true, + "address": "50.7.252.138/9993", + "expired": false, + "lastReceive": 1691413480922, + "lastSend": 1691413675822, + "localSocket": 94898166306960, + "preferred": true, + "trustedPathId": 0 + } + ], + "role": "PLANET", + "tunneled": false, + "version": "-1.-1.-1", + "versionMajor": -1, + "versionMinor": -1, + "versionRev": -1 + }, + { + "address": "778cde7190", + "isBonded": false, + "latency": 239, + "paths": [ + { + "active": true, + "address": "103.195.103.66/9993", + "expired": false, + "lastReceive": 1691413255618, + "lastSend": 1691413435572, + "localSocket": 94898166308768, + "preferred": false, + "trustedPathId": 0 + }, + { + "active": true, + "address": "103.195.103.66/9993", + "expired": false, + "lastReceive": 1691413480862, + "lastSend": 1691413675822, + "localSocket": 94898166309120, + "preferred": true, + "trustedPathId": 0 + } + ], + "role": "PLANET", + "tunneled": false, + "version": "-1.-1.-1", + "versionMajor": -1, + "versionMinor": -1, + "versionRev": -1 + }, + { + "address": "a7f77c3e11", + "isBonded": false, + "latency": -1, + "paths": [], + "role": "LEAF", + "tunneled": false, + "version": "1.10.6", + "versionMajor": 1, + "versionMinor": 10, + "versionRev": 6 + }, + { + "address": "cafe04eba9", + "isBonded": false, + "latency": 166, + "paths": [ + { + "active": true, + "address": "84.17.53.155/9993", + "expired": false, + "lastReceive": 1691413455702, + "lastSend": 1691413475617, + "localSocket": 94898166309120, + "preferred": false, + "trustedPathId": 0 + }, + { + "active": true, + "address": "84.17.53.155/9993", + "expired": false, + "lastReceive": 1691413695952, + "lastSend": 1691413700848, + "localSocket": 94898166306960, + "preferred": true, + "trustedPathId": 0 + } + ], + "role": "PLANET", + "tunneled": false, + "version": "-1.-1.-1", + "versionMajor": -1, + "versionMinor": -1, + "versionRev": -1 + }, + { + "address": "cafe9efeb9", + "isBonded": false, + "latency": 239, + "paths": [ + { + "active": true, + "address": "104.194.8.134/9993", + "expired": false, + "lastReceive": 1691413480862, + "lastSend": 1691413675822, + "localSocket": 94898166308768, + "preferred": true, + "trustedPathId": 0 + } + ], + "role": "PLANET", + "tunneled": false, + "version": "-1.-1.-1", + "versionMajor": -1, + "versionMinor": -1, + "versionRev": -1 + } +] diff --git a/tests/test_zerotier.py b/tests/test_zerotier.py new file mode 100644 index 0000000..03db774 --- /dev/null +++ b/tests/test_zerotier.py @@ -0,0 +1,19 @@ +import os +from json import loads + +import networkx + +from netdiff import ZeroTierParser, diff +from netdiff.exceptions import ConversionException +from netdiff.tests import TestCase + +CURRENT_DIR = os.path.dirname(os.path.realpath(__file__)) +zt_peers = loads(open(f'{CURRENT_DIR}/static/zt-peers.json', 'r').read()) +zt_peers_diff = loads(open(f'{CURRENT_DIR}/static/zt-peers-diff.json', 'r').read()) + + +class TestZeroTierParser(TestCase): + def test_parse(self): + ng1 = ZeroTierParser(zt_peers) + ng2 = ZeroTierParser(zt_peers_diff) + # print(ng2.json(indent=4))