Skip to content

Commit 07614d4

Browse files
committed
addedd batman parser, applyied DRY and created generic Parser
1 parent e7b677f commit 07614d4

File tree

7 files changed

+316
-47
lines changed

7 files changed

+316
-47
lines changed

netdiff/batman.py

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
import json
2+
import networkx
3+
from netdiff.nxparser import Parser
4+
5+
6+
class BatmanParser(Parser):
7+
""" Batman Topology Parser """
8+
def _get_primary(self, mac, collection):
9+
for node in collection:
10+
for interface in node:
11+
if mac == interface:
12+
return node[0]
13+
return 0
14+
15+
def _get_ag_node_list(self, data):
16+
agn = []
17+
for node in data:
18+
agi = []
19+
agi.append(node['primary'])
20+
if('secondary'in node):
21+
for interface in node['secondary']:
22+
agi.append(interface)
23+
agn.append(agi)
24+
return agn
25+
26+
def _parse(self, data):
27+
"""
28+
Converts a topology in a NetworkX MultiGraph object.
29+
30+
:param str topology: The OLSR1 topology to be converted (JSON or dict)
31+
:return: the NetworkX MultiGraph object
32+
"""
33+
# if data is not a python dict it must be a json string
34+
if type(data) is not dict:
35+
data = json.loads(data)
36+
# initialize graph and list of aggregated nodes
37+
graph = networkx.MultiGraph()
38+
agn = self._get_ag_node_list(data['vis'])
39+
# loop over topology section and create networkx graph
40+
for node in data["vis"]:
41+
for neigh in node["neighbors"]:
42+
p_neigh = self._get_primary(neigh['neighbor'], agn)
43+
if not graph.has_edge(node['primary'], p_neigh):
44+
graph.add_edge(node['primary'],
45+
p_neigh,
46+
weight=neigh['metric'])
47+
return graph

netdiff/nxparser.py

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
import json
2+
import networkx
3+
4+
5+
class Parser(object):
6+
""" Generic Topology Parser """
7+
8+
def __init__(self, old, new):
9+
"""
10+
Initializes a new Parser
11+
12+
:param str old: a JSON or dict representing the old topology
13+
:param str new: a JSON or dict representing the new topology
14+
"""
15+
self.old_graph = self._parse(old)
16+
self.new_graph = self._parse(new)
17+
18+
def diff(self):
19+
"""
20+
Returns netdiff in a python dictionary
21+
"""
22+
return {
23+
"added": self._make_diff(self.new_graph, self.old_graph),
24+
"removed": self._make_diff(self.old_graph, self.new_graph)
25+
}
26+
27+
def diff_json(self, **kwargs):
28+
"""
29+
Returns netdiff in a JSON string
30+
"""
31+
return json.dumps(self.diff(), **kwargs)
32+
33+
# --- private methods --- #
34+
35+
def _make_diff(self, old, new):
36+
"""
37+
calculates differences between topologies 'old' and 'new'
38+
returns a list of links
39+
"""
40+
# make a copy of old topology to avoid tampering with it
41+
diff = old.copy()
42+
not_different = []
43+
# loop over all links
44+
for oedge in old.edges():
45+
# if link is also in new topology add it to the list
46+
for nedge in new.edges():
47+
if ((oedge[0] == nedge[0]) and (oedge[1] == nedge[1])) or ((oedge[1] == nedge[0]) and (oedge[0] == nedge[1])):
48+
not_different.append(oedge)
49+
# keep only differences
50+
diff.remove_edges_from(not_different)
51+
# return list of links
52+
return diff.edges()

netdiff/olsr1.py

Lines changed: 2 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -1,37 +1,10 @@
11
import json
22
import networkx
3+
from netdiff.nxparser import Parser
34

45

5-
class Olsr1Parser(object):
6+
class Olsr1Parser(Parser):
67
""" OLSR v1 Topology Parser """
7-
8-
def __init__(self, old, new):
9-
"""
10-
Initializes a new Olsr1Parser
11-
12-
:param str old: a JSON or dict representing the old OLSR1 topology
13-
:param str new: a JSON or dict representing the new OLSR1 topology
14-
"""
15-
self.old_graph = self._parse(old)
16-
self.new_graph = self._parse(new)
17-
18-
def diff(self):
19-
"""
20-
Returns netdiff in a python dictionary
21-
"""
22-
return {
23-
"added": self._make_diff(self.new_graph, self.old_graph),
24-
"removed": self._make_diff(self.old_graph, self.new_graph)
25-
}
26-
27-
def diff_json(self, **kwargs):
28-
"""
29-
Returns netdiff in a JSON string
30-
"""
31-
return json.dumps(self.diff(), **kwargs)
32-
33-
# --- private methods --- #
34-
358
def _parse(self, topology):
369
"""
3710
Converts a topology in a NetworkX MultiGraph object.
@@ -50,21 +23,3 @@ def _parse(self, topology):
5023
link["destinationIP"],
5124
weight=link["tcEdgeCost"])
5225
return graph
53-
54-
def _make_diff(self, old, new):
55-
"""
56-
calculates differences between topologies 'old' and 'new'
57-
returns a list of links
58-
"""
59-
# make a copy of old topology to avoid tampering with it
60-
diff = old.copy()
61-
not_different = []
62-
# loop over all links
63-
for edge in old.edges():
64-
# if link is also in new topology add it to the list
65-
if edge in new.edges():
66-
not_different.append(edge)
67-
# keep only differences
68-
diff.remove_edges_from(not_different)
69-
# return list of links
70-
return diff.edges()

tests/batman/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
from .tests import TestBatmanParser

tests/batman/batman-1+1.json

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
{
2+
"source_version" : "2014.3.0",
3+
"algorithm" : 4,
4+
"vis" : [
5+
{ "primary" : "a0:f3:c1:96:94:10",
6+
"neighbors" : [
7+
{ "router" : "a0:f3:c1:96:94:10",
8+
"neighbor" : "90:f6:52:f2:8c:2d",
9+
"metric" : "1.000" }
10+
],
11+
"clients" : [
12+
"86:a6:e8:ff:fd:6c",
13+
"64:76:ba:7e:06:44",
14+
"68:72:51:05:79:10",
15+
"86:a6:e8:ff:fd:6c",
16+
"a0:f3:c1:96:94:05"
17+
]
18+
},
19+
{ "primary" : "a0:f3:c1:ac:6c:44",
20+
"neighbors" : [
21+
{ "router" : "a0:f3:c1:ac:6c:44",
22+
"neighbor" : "10:fe:ed:37:3a:39",
23+
"metric" : "1.000" }
24+
],
25+
"clients" : [
26+
"a0:f3:c1:ac:6c:4c",
27+
"dc:9f:db:f1:d7:a9",
28+
"46:1f:b2:8b:c2:a1",
29+
"46:1f:b2:8b:c2:a1"
30+
]
31+
},
32+
{ "primary" : "90:f6:52:f2:8c:2c",
33+
"secondary" : [ "90:f6:52:f2:8c:2b","90:f6:52:f2:8c:2d"
34+
],
35+
"neighbors" : [
36+
{ "router" : "90:f6:52:f2:8c:2c",
37+
"neighbor" : "00:05:1c:06:35:8e",
38+
"metric" : "1.000" },
39+
{ "router" : "90:f6:52:f2:8c:2b",
40+
"neighbor" : "10:fe:ed:37:3a:39",
41+
"metric" : "1.000" },
42+
{ "router" : "90:f6:52:f2:8c:2d",
43+
"neighbor" : "a0:f3:c1:96:94:10",
44+
"metric" : "1.000" }
45+
],
46+
"clients" : [
47+
"ee:74:50:85:fd:67",
48+
"dc:9f:db:f1:d8:aa",
49+
"68:72:51:05:7c:86",
50+
"90:f6:52:f2:8c:2a",
51+
"ee:74:50:85:fd:67",
52+
"24:a4:3c:6d:fd:81"
53+
]
54+
},
55+
{ "primary" : "00:05:1c:06:35:8e",
56+
"neighbors" : [
57+
{ "router" : "00:05:1c:06:35:8e",
58+
"neighbor" : "90:f6:52:f2:8c:2c",
59+
"metric" : "1.000" }
60+
],
61+
"clients" : [
62+
"00:ff:aa:00:00:01",
63+
"00:18:7d:0b:b3:31",
64+
"52:fe:31:1d:aa:4e",
65+
"24:a4:3c:47:53:9a",
66+
"dc:9f:db:0b:af:b9",
67+
"52:fe:31:1d:aa:4e",
68+
"dc:9f:db:f1:d8:a7"
69+
]
70+
},
71+
{ "primary" : "10:fe:ed:37:3a:39",
72+
"neighbors" : [
73+
{ "router" : "10:fe:ed:37:3a:39",
74+
"neighbor" : "90:f6:52:f2:8c:2b",
75+
"metric" : "1.000" },
76+
{ "router" : "10:fe:ed:37:3a:39",
77+
"neighbor" : "a0:f3:c1:ac:6c:44",
78+
"metric" : "1.016" }
79+
],
80+
"clients" : [
81+
"10:fe:ed:37:3a:38",
82+
"24:a4:3c:6d:fd:ce",
83+
"a6:86:18:b1:00:7b",
84+
"a6:86:18:b1:00:7b"
85+
]
86+
}
87+
]
88+
}

tests/batman/batman.json

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
{
2+
"source_version" : "2014.3.0",
3+
"algorithm" : 4,
4+
"vis" : [
5+
{ "primary" : "a0:f3:c1:96:94:06",
6+
"neighbors" : [
7+
{ "router" : "a0:f3:c1:96:94:06",
8+
"neighbor" : "90:f6:52:f2:8c:2d",
9+
"metric" : "1.000" }
10+
],
11+
"clients" : [
12+
"86:a6:e8:ff:fd:6c",
13+
"64:76:ba:7e:06:44",
14+
"68:72:51:05:79:10",
15+
"86:a6:e8:ff:fd:6c",
16+
"a0:f3:c1:96:94:05"
17+
]
18+
},
19+
{ "primary" : "a0:f3:c1:ac:6c:44",
20+
"neighbors" : [
21+
{ "router" : "a0:f3:c1:ac:6c:44",
22+
"neighbor" : "10:fe:ed:37:3a:39",
23+
"metric" : "1.000" }
24+
],
25+
"clients" : [
26+
"a0:f3:c1:ac:6c:4c",
27+
"dc:9f:db:f1:d7:a9",
28+
"46:1f:b2:8b:c2:a1",
29+
"46:1f:b2:8b:c2:a1"
30+
]
31+
},
32+
{ "primary" : "90:f6:52:f2:8c:2c",
33+
"secondary" : [ "90:f6:52:f2:8c:2b","90:f6:52:f2:8c:2d"
34+
],
35+
"neighbors" : [
36+
{ "router" : "90:f6:52:f2:8c:2c",
37+
"neighbor" : "00:05:1c:06:35:8e",
38+
"metric" : "1.000" },
39+
{ "router" : "90:f6:52:f2:8c:2b",
40+
"neighbor" : "10:fe:ed:37:3a:39",
41+
"metric" : "1.000" },
42+
{ "router" : "90:f6:52:f2:8c:2d",
43+
"neighbor" : "a0:f3:c1:96:94:06",
44+
"metric" : "1.000" }
45+
],
46+
"clients" : [
47+
"ee:74:50:85:fd:67",
48+
"dc:9f:db:f1:d8:aa",
49+
"68:72:51:05:7c:86",
50+
"90:f6:52:f2:8c:2a",
51+
"ee:74:50:85:fd:67",
52+
"24:a4:3c:6d:fd:81"
53+
]
54+
},
55+
{ "primary" : "00:05:1c:06:35:8e",
56+
"neighbors" : [
57+
{ "router" : "00:05:1c:06:35:8e",
58+
"neighbor" : "90:f6:52:f2:8c:2c",
59+
"metric" : "1.000" }
60+
],
61+
"clients" : [
62+
"00:ff:aa:00:00:01",
63+
"00:18:7d:0b:b3:31",
64+
"52:fe:31:1d:aa:4e",
65+
"24:a4:3c:47:53:9a",
66+
"dc:9f:db:0b:af:b9",
67+
"52:fe:31:1d:aa:4e",
68+
"dc:9f:db:f1:d8:a7"
69+
]
70+
},
71+
{ "primary" : "10:fe:ed:37:3a:39",
72+
"neighbors" : [
73+
{ "router" : "10:fe:ed:37:3a:39",
74+
"neighbor" : "90:f6:52:f2:8c:2b",
75+
"metric" : "1.000" },
76+
{ "router" : "10:fe:ed:37:3a:39",
77+
"neighbor" : "a0:f3:c1:ac:6c:44",
78+
"metric" : "1.016" }
79+
],
80+
"clients" : [
81+
"10:fe:ed:37:3a:38",
82+
"24:a4:3c:6d:fd:ce",
83+
"a6:86:18:b1:00:7b",
84+
"a6:86:18:b1:00:7b"
85+
]
86+
}
87+
]
88+
}
89+

tests/batman/tests.py

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import os
2+
import unittest
3+
import json
4+
from netdiff.batman import BatmanParser
5+
6+
7+
__all__ = ['TestBatmanParser']
8+
9+
10+
CURRENT_DIR = os.path.dirname(os.path.realpath(__file__))
11+
iulinet = open('{0}/batman.json'.format(CURRENT_DIR)).read()
12+
iulinet2 = open('{0}/batman-1+1.json'.format(CURRENT_DIR)).read()
13+
14+
15+
class TestBatmanParser(unittest.TestCase):
16+
17+
def test_added_removed_1_node(self):
18+
parser = BatmanParser(old=iulinet, new=iulinet2)
19+
result = parser.diff()
20+
self.assertTrue(type(result) is dict)
21+
self.assertTrue(type(result['added']) is list)
22+
self.assertTrue(type(result['removed']) is list)
23+
# ensure there are no differences
24+
self.assertEqual(len(result['added']), 1)
25+
self.assertEqual(len(result['removed']), 1)
26+
self.assertEqual(result['added'][0], ('a0:f3:c1:96:94:10', '90:f6:52:f2:8c:2c'))
27+
self.assertEqual(result['removed'][0], ('a0:f3:c1:96:94:06', '90:f6:52:f2:8c:2c'))
28+
29+
def test_no_changes(self):
30+
parser = BatmanParser(old=iulinet, new=iulinet)
31+
result = parser.diff()
32+
self.assertTrue(type(result) is dict)
33+
self.assertTrue(type(result['added']) is list)
34+
self.assertTrue(type(result['removed']) is list)
35+
# ensure there are no differences
36+
self.assertEqual(len(result['added']), 0)
37+
self.assertEqual(len(result['removed']), 0)

0 commit comments

Comments
 (0)