From c806817b9f78a21e18ff1df278e4506cb9170c2c Mon Sep 17 00:00:00 2001 From: Rohith Asrk Date: Tue, 12 Sep 2017 19:53:16 +0530 Subject: [PATCH 1/2] Added openvpn parser --- netdiff/__init__.py | 1 + netdiff/parsers/openvpn.py | 58 ++++++++++++++++++++++++++++++++ requirements.txt | 1 + tests/static/openvpn-2-links.txt | 12 +++++++ tests/test_openvpn.py | 33 ++++++++++++++++++ 5 files changed, 105 insertions(+) create mode 100644 netdiff/parsers/openvpn.py create mode 100644 tests/static/openvpn-2-links.txt create mode 100644 tests/test_openvpn.py diff --git a/netdiff/__init__.py b/netdiff/__init__.py index 9a1cd40..0b498a2 100644 --- a/netdiff/__init__.py +++ b/netdiff/__init__.py @@ -5,4 +5,5 @@ from .parsers.bmx6 import Bmx6Parser # noqa from .parsers.netjson import NetJsonParser # noqa from .parsers.cnml import CnmlParser # noqa +from .parsers.openvpn import OpenvpnParser #noqa from .utils import diff # noqa diff --git a/netdiff/parsers/openvpn.py b/netdiff/parsers/openvpn.py new file mode 100644 index 0000000..7ebdc29 --- /dev/null +++ b/netdiff/parsers/openvpn.py @@ -0,0 +1,58 @@ +import networkx +from openvpn_status import ParsingError, parse_status + +from ..exceptions import ConversionException, ParserError +from .base import BaseParser + + +class OpenvpnParser(BaseParser): + """ OpenVPN status log parser """ + protocol = 'openvpn' + server_address = '127.0.0.1' # Testing purposes + version = '1' + metric = '0' + + def to_python(self, data): + status = parse_status(data) + nodes = [] + for i in status.client_list: + node = status.client_list[i] + nodes.append({ + 'commonName': node.common_name, + 'bytesSent': node.bytes_sent, + 'bytesReceived': node.bytes_received, + 'connectedSince': node.connected_since, + 'realAddress': str(node.real_address) + }) + + return { + 'type': 'OpenVPN', + 'nodes': nodes, + 'updated_at': status.updated_at, + 'server': self.server_address + } + + def parse(self, data): + """ + Converts a OpenVPN JSON to a NetworkX Graph object + which is then returned. + """ + # initialize graph and list of aggregated nodes + graph = networkx.Graph() + if 'type' not in data or data['type'] != 'OpenVPN': + raise ParserError('Parse Error, not a OpenVPN object') + required_keys = ['nodes', 'server'] + for key in required_keys: + if key not in data: + raise ParserError('Parse Error, "%s" key not found' % key) + + graph.add_node(data['server']) + for link in data['nodes']: + try: + source = data['server'] + target = link['realAddress'] + except KeyError as e: + raise ParserError('Parse Error, "%s" key not found' % key) + graph.add_node(target) + graph.add_edge(source, target, weight=1) + return graph diff --git a/requirements.txt b/requirements.txt index 6b05f5a..c93eee0 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,3 +2,4 @@ networkx<2.0 requests<3.0 six libcnml<0.10.0 +openvpn-status<0.2 diff --git a/tests/static/openvpn-2-links.txt b/tests/static/openvpn-2-links.txt new file mode 100644 index 0000000..cdc3a09 --- /dev/null +++ b/tests/static/openvpn-2-links.txt @@ -0,0 +1,12 @@ +OpenVPN CLIENT LIST +Updated,Thu Jun 18 08:12:15 2015 +Common Name,Real Address,Bytes Received,Bytes Sent,Connected Since +foo@example.com,10.10.10.10:49502,334948,1973012,Thu Jun 18 04:23:03 2015 +bar@example.com,10.10.10.10:64169,1817262,28981224,Thu Jun 18 04:08:39 2015 +ROUTING TABLE +Virtual Address,Common Name,Real Address,Last Ref +192.168.255.134,foo@example.com,10.10.10.10:49502,Thu Jun 18 08:12:09 2015 +192.168.255.126,bar@example.com,10.10.10.10:64169,Thu Jun 18 08:11:55 2015 +GLOBAL STATS +Max bcast/mcast queue length,0 +END diff --git a/tests/test_openvpn.py b/tests/test_openvpn.py new file mode 100644 index 0000000..f5a3ef8 --- /dev/null +++ b/tests/test_openvpn.py @@ -0,0 +1,33 @@ +import os + +import networkx +import six +from netdiff import OpenvpnParser +from netdiff.exceptions import ParserError, TopologyRetrievalError +from netdiff.tests import TestCase + +CURRENT_DIR = os.path.dirname(os.path.realpath(__file__)) +links2 = open('{0}/static/openvpn-2-links.txt'.format(CURRENT_DIR)).read() + + +class TestOpenvpnParser(TestCase): + + def test_parse(self): + p = OpenvpnParser(links2) + self.assertIsInstance(p.graph, networkx.Graph) + self.assertEqual(p.version, '1') + self.assertEqual(p.metric, '0') + + def test_json_dict(self): + p = OpenvpnParser(links2) + data = p.json(dict=True) + self.assertIsInstance(data, dict) + self.assertEqual(data['type'], 'NetworkGraph') + self.assertEqual(data['protocol'], 'openvpn') + self.assertEqual(data['version'], '1') + self.assertEqual(data['revision'], None) + self.assertEqual(data['metric'], '0') + self.assertIsInstance(data['nodes'], list) + self.assertIsInstance(data['links'], list) + self.assertEqual(len(data['nodes']), 3) + self.assertEqual(len(data['links']), 2) From 4ebfb69cd6af931d78e49d7639804da66bfa858f Mon Sep 17 00:00:00 2001 From: Federico Capoano Date: Fri, 22 Sep 2017 12:31:49 +0200 Subject: [PATCH 2/2] [openvpn] Simplified and improved OpenVPN parser --- netdiff/parsers/openvpn.py | 65 +++++++++++++------------------- tests/static/openvpn-2-links.txt | 8 ++-- tests/test_openvpn.py | 6 +-- 3 files changed, 33 insertions(+), 46 deletions(-) diff --git a/netdiff/parsers/openvpn.py b/netdiff/parsers/openvpn.py index 7ebdc29..d1504c4 100644 --- a/netdiff/parsers/openvpn.py +++ b/netdiff/parsers/openvpn.py @@ -1,36 +1,19 @@ import networkx -from openvpn_status import ParsingError, parse_status +from openvpn_status import parse_status -from ..exceptions import ConversionException, ParserError from .base import BaseParser class OpenvpnParser(BaseParser): """ OpenVPN status log parser """ - protocol = 'openvpn' - server_address = '127.0.0.1' # Testing purposes + protocol = 'OpenVPN Status Log' version = '1' - metric = '0' + metric = 'static' + # for internal use only + _server_common_name = 'openvpn-server' def to_python(self, data): - status = parse_status(data) - nodes = [] - for i in status.client_list: - node = status.client_list[i] - nodes.append({ - 'commonName': node.common_name, - 'bytesSent': node.bytes_sent, - 'bytesReceived': node.bytes_received, - 'connectedSince': node.connected_since, - 'realAddress': str(node.real_address) - }) - - return { - 'type': 'OpenVPN', - 'nodes': nodes, - 'updated_at': status.updated_at, - 'server': self.server_address - } + return parse_status(data) def parse(self, data): """ @@ -39,20 +22,24 @@ def parse(self, data): """ # initialize graph and list of aggregated nodes graph = networkx.Graph() - if 'type' not in data or data['type'] != 'OpenVPN': - raise ParserError('Parse Error, not a OpenVPN object') - required_keys = ['nodes', 'server'] - for key in required_keys: - if key not in data: - raise ParserError('Parse Error, "%s" key not found' % key) - - graph.add_node(data['server']) - for link in data['nodes']: - try: - source = data['server'] - target = link['realAddress'] - except KeyError as e: - raise ParserError('Parse Error, "%s" key not found' % key) - graph.add_node(target) - graph.add_edge(source, target, weight=1) + server = self._server_common_name + # add server (central node) to graph + graph.add_node(server) + # add clients in graph as nodes + for common_name, client in data.client_list.items(): + client_properties = { + 'real_address': str(client.real_address.host), + 'port': client.real_address.port, + 'connected_since': client.connected_since.strftime('%Y-%m-%dT%H:%M:%SZ'), + 'bytes_received': client.bytes_received, + 'bytes_sent': client.bytes_sent + } + if common_name in data.routing_table: + client_properties['local_addresses'] = [ + str(data.routing_table.get(common_name).virtual_address) + ] + graph.add_node(common_name, **client_properties) + # add links in routing table to graph + for common_name, link in data.routing_table.items(): + graph.add_edge(server, common_name, weight=1) return graph diff --git a/tests/static/openvpn-2-links.txt b/tests/static/openvpn-2-links.txt index cdc3a09..6662f21 100644 --- a/tests/static/openvpn-2-links.txt +++ b/tests/static/openvpn-2-links.txt @@ -1,12 +1,12 @@ OpenVPN CLIENT LIST Updated,Thu Jun 18 08:12:15 2015 Common Name,Real Address,Bytes Received,Bytes Sent,Connected Since -foo@example.com,10.10.10.10:49502,334948,1973012,Thu Jun 18 04:23:03 2015 -bar@example.com,10.10.10.10:64169,1817262,28981224,Thu Jun 18 04:08:39 2015 +node1,87.18.10.87:49502,334948,1973012,Thu Jun 18 04:23:03 2015 +node2,93.40.230.50:64169,1817262,28981224,Thu Jun 18 04:08:39 2015 ROUTING TABLE Virtual Address,Common Name,Real Address,Last Ref -192.168.255.134,foo@example.com,10.10.10.10:49502,Thu Jun 18 08:12:09 2015 -192.168.255.126,bar@example.com,10.10.10.10:64169,Thu Jun 18 08:11:55 2015 +192.168.255.134,node1,87.18.10.87:49502,Thu Jun 18 08:12:09 2015 +192.168.255.126,node2,93.40.230.50:64169,Thu Jun 18 08:11:55 2015 GLOBAL STATS Max bcast/mcast queue length,0 END diff --git a/tests/test_openvpn.py b/tests/test_openvpn.py index f5a3ef8..57a3dfd 100644 --- a/tests/test_openvpn.py +++ b/tests/test_openvpn.py @@ -16,17 +16,17 @@ def test_parse(self): p = OpenvpnParser(links2) self.assertIsInstance(p.graph, networkx.Graph) self.assertEqual(p.version, '1') - self.assertEqual(p.metric, '0') + self.assertEqual(p.metric, 'static') def test_json_dict(self): p = OpenvpnParser(links2) data = p.json(dict=True) self.assertIsInstance(data, dict) self.assertEqual(data['type'], 'NetworkGraph') - self.assertEqual(data['protocol'], 'openvpn') + self.assertEqual(data['protocol'], 'OpenVPN Status Log') self.assertEqual(data['version'], '1') self.assertEqual(data['revision'], None) - self.assertEqual(data['metric'], '0') + self.assertEqual(data['metric'], 'static') self.assertIsInstance(data['nodes'], list) self.assertIsInstance(data['links'], list) self.assertEqual(len(data['nodes']), 3)