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)