summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--haircontrol/data.py9
-rw-r--r--haircontrol/discovery.py31
-rw-r--r--haircontrol/inspectors.py21
-rw-r--r--test-data/input/stg2-ip-link.out2
-rwxr-xr-xtests/test_discovery.py14
5 files changed, 58 insertions, 19 deletions
diff --git a/haircontrol/data.py b/haircontrol/data.py
index d8195c7..6f5733d 100644
--- a/haircontrol/data.py
+++ b/haircontrol/data.py
@@ -5,9 +5,12 @@ class EtherDomain:
self.mac2ip = {}
def __repr__(self):
+ return '([\n%s\n],\n%s\n)'%(',\n'.join(' %s'%repr(e) for e in get_equipment_list_sorted()), repr(self.ip2mac))
+
+ def get_equipment_list_sorted(self):
e_list = list(self.equipments.values())
e_list.sort(key=lambda x: x.name)
- return '([\n%s\n],\n%s\n)'%(',\n'.join(' %s'%repr(e) for e in e_list), repr(self.ip2mac))
+ return e_list
def add_equipment(self,e):
old = self.equipments.get(e.mgmtip)
@@ -37,6 +40,7 @@ class Equipment:
def add_seen_mac(self, ifname, mac):
iface = self.ifaces.get(ifname)
if not iface:
+ print("Warn : add_seen_mac(%s, %s) auto-create iface on %s"%(ifname, mac, self.name))
iface = Interface(ifname)
self.ifaces[ifname] = iface
iface.mac_seen.append(mac)
@@ -48,5 +52,6 @@ class Interface:
self.mac_seen = []
def __repr__(self):
- return repr( ( self.mac, self.name, '[ %i mac_seen ]'%len(self.mac_seen) ) )
+ return repr( ( self.mac, self.name, self.mac_seen ) )
+ #return repr( ( self.mac, self.name, '[ %i mac_seen ]'%len(self.mac_seen) ) )
diff --git a/haircontrol/discovery.py b/haircontrol/discovery.py
index 2b7ad83..680eb4c 100644
--- a/haircontrol/discovery.py
+++ b/haircontrol/discovery.py
@@ -4,9 +4,8 @@ import xml.etree.ElementTree
from haircontrol.data import *
class Discovery:
- IPNEIGH = re.compile("^(?P<ip>[a-f0-9:.]+) dev (?P<ifname>.*) lladdr (?P<mac>[a-f0-9:]*)")
- # fe80::8300 dev eth1 lladdr 10:fe:ed:f1:e1:f3 router STALE
- # 172.16.20.210 dev eth1 lladdr c0:4a:00:fe:1f:87 REACHABLE
+ IPNEIGH = re.compile("(?P<ip>[a-f0-9:.]+) dev (?P<ifname>.*) lladdr (?P<mac>[a-f0-9:]*)")
+ IPLINKSHOW = re.compile("(?P<id>\d+):\s+(?P<ifname>[^:]*):.+\s+(?:link/ether\s+(?P<mac>[a-f0-9:]*)\s+brd|link/none)")
def __init__(self, inspector):
self.inspector = inspector
@@ -15,18 +14,30 @@ class Discovery:
def discover_hinting_from_lldp(self, e_lldp):
self.net.add_equipment(e_lldp)
self.inspector.connect(e_lldp)
+
+ # Learn local interfaces of e_lldp
+ fd = self.inspector.command('ip-link')
+ for line in fd:
+ matches = Discovery.IPLINKSHOW.match(line)
+ if matches:
+ ifname, mac = [ matches.group(k) for k in ['ifname','mac'] ]
+ e_lldp.ifaces[ifname] = Interface(ifname, mac)
+ fd.close()
+
+ # Create equipments and ifaces from LLDP neighbour discovery
fd = self.inspector.command('lldp')
root = xml.etree.ElementTree.parse(fd).getroot()
for iface in root.iter('interface'):
- ifname = iface.get('name')
+ local_ifname = iface.get('name')
+ local_mac = e_lldp.ifaces[local_ifname].mac
chassis = iface.find('chassis')
- e = Equipment()
- e.name = chassis.find('name').text
- e.mgmtip = chassis.find('mgmt-ip').text
+ remote_name = chassis.find('name').text
+ remote_ipmgmt = chassis.find('mgmt-ip').text
+ e = Equipment(remote_name, remote_ipmgmt)
self.net.add_equipment(e)
for port in iface.findall('port'):
- #remote_ifname = port.find('id').text
- e.add_seen_mac(ifname, 'lldp') #XXX
+ remote_ifname = port.find('id').text
+ e.add_seen_mac(remote_ifname, local_mac)
fd.close()
self.inspector.disconnect()
@@ -35,7 +46,7 @@ class Discovery:
self.inspector.connect(e_root)
fd = self.inspector.command('ip-neigh')
for line in fd:
- matches = Discovery.IPNEIGH.search(line)
+ matches = Discovery.IPNEIGH.match(line)
if matches:
ip, ifname, mac = [ matches.group(k) for k in ['ip','ifname','mac'] ]
self.net.index_mac_ip(mac, ip)
diff --git a/haircontrol/inspectors.py b/haircontrol/inspectors.py
new file mode 100644
index 0000000..9b797fe
--- /dev/null
+++ b/haircontrol/inspectors.py
@@ -0,0 +1,21 @@
+import re
+
+class Inspector():
+ cmds = {}
+ fd = None
+
+ def parse(self):
+ return None #XXX Implement
+
+class LinuxInspector(Inspector):
+ cmds = {
+ 'ip-neigh': {
+ 'cmd': 'ip neigh',
+ # fe80::8300 dev eth1 lladdr 10:fe:ed:f1:e1:f3 router STALE
+ # 172.16.20.210 dev eth1 lladdr c0:4a:00:fe:1f:87 REACHABLE
+ 'kind': 'text',
+ 'fields': ['ip','ifname','mac'],
+ 're': re.compile("(?P<ip>[a-f0-9:.]+) dev (?P<ifname>.*) lladdr (?P<mac>[a-f0-9:]*)")
+ }
+ }
+
diff --git a/test-data/input/stg2-ip-link.out b/test-data/input/stg2-ip-link.out
new file mode 100644
index 0000000..58c4fb4
--- /dev/null
+++ b/test-data/input/stg2-ip-link.out
@@ -0,0 +1,2 @@
+1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default \ link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
+2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP mode DEFAULT group default qlen 1000\ link/ether 52:54:00:75:c6:1d brd ff:ff:ff:ff:ff:ff
diff --git a/tests/test_discovery.py b/tests/test_discovery.py
index b0de19b..7fb0bd2 100755
--- a/tests/test_discovery.py
+++ b/tests/test_discovery.py
@@ -3,9 +3,9 @@ import unittest
#import pudb; pudb.set_trace()
from context import haircontrol
-from haircontrol import discovery, data
+from haircontrol import discovery, data, inspectors
-class MockInspector(object):
+class MockInspector(inspectors.Inspector):
def __init__(self, testDataPath):
self.testDataPath = testDataPath
self.e = None
@@ -20,7 +20,7 @@ class MockInspector(object):
if not self.e:
return None
mockfile = self.testDataPath + '/' + self.e.name + '-' + command + '.out'
- return open(mockfile)
+ return open(mockfile) #XXX use Inspector.parse()
class TestDiscovery(unittest.TestCase):
@@ -30,11 +30,11 @@ class TestDiscovery(unittest.TestCase):
self.maxDiff=None
def test_wire_graph(self):
- ref_equipments = { 'todo': data.Equipment() } #json.load('../test-data/ref-output/equipments.json')
+ ref_net = data.EtherDomain() #json.load('../test-data/ref-output/equipments.json')
- self.discovery.discover_hinting_from_lldp(data.Equipment('stg2', '172.16.0.254'))
- self.discovery.discover_from_root(data.Equipment('stg', '172.16.0.253'))
- self.assertDictEqual(self.discovery.net.equipments, ref_equipments)
+ self.discovery.discover_hinting_from_lldp(data.Equipment('stg2', '172.16.0.253'))
+ self.discovery.discover_from_root(data.Equipment('stg', '172.16.0.254'))
+ self.assertEqual(ref_net.get_equipment_list_sorted(), list(self.discovery.net.get_equipment_list_sorted()))
if __name__ == '__main__':
unittest.main()