summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--haircontrol/discovery.py57
-rw-r--r--haircontrol/inspectors.py114
-rw-r--r--test-data/input/stg2-lldpctl.out (renamed from test-data/input/stg2-lldp.out)0
-rwxr-xr-xtests/test_discovery.py22
4 files changed, 131 insertions, 62 deletions
diff --git a/haircontrol/discovery.py b/haircontrol/discovery.py
index 680eb4c..243e3b6 100644
--- a/haircontrol/discovery.py
+++ b/haircontrol/discovery.py
@@ -1,56 +1,41 @@
-import re
-import xml.etree.ElementTree
from haircontrol.data import *
+from haircontrol.inspectors import *
class Discovery:
- 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
+ def __init__(self): #, inspector):
+ self.linux_inspector = LinuxInspector()
+ self.ubnt_inspector = UbntInspector()
self.net = EtherDomain()
def discover_hinting_from_lldp(self, e_lldp):
self.net.add_equipment(e_lldp)
- self.inspector.connect(e_lldp)
+ self.linux_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()
+ result = self.linux_inspector.command('ip-link')
+ for (_, ifname, mac) in result:
+ e_lldp.ifaces[ifname] = Interface(ifname, mac)
# 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'):
- local_ifname = iface.get('name')
- local_mac = e_lldp.ifaces[local_ifname].mac
- chassis = iface.find('chassis')
- remote_name = chassis.find('name').text
- remote_ipmgmt = chassis.find('mgmt-ip').text
+ result = self.linux_inspector.command('lldpctl')
+ for (local_ifname, local_mac, remote_name, remote_ipmgmt, ports) in result:
e = Equipment(remote_name, remote_ipmgmt)
self.net.add_equipment(e)
- for port in iface.findall('port'):
- remote_ifname = port.find('id').text
+ for remote_ifname in ports:
e.add_seen_mac(remote_ifname, local_mac)
- fd.close()
- self.inspector.disconnect()
+
+ self.linux_inspector.disconnect()
def discover_from_root(self, e_root):
self.net.add_equipment(e_root)
- self.inspector.connect(e_root)
- fd = self.inspector.command('ip-neigh')
- for line in fd:
- 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)
- e_root.add_seen_mac(ifname, mac)
- fd.close()
- self.inspector.disconnect()
+ self.linux_inspector.connect(e_root)
+
+ result = self.linux_inspector.command('ip-neigh')
+ for (ip, ifname, mac) in result:
+ self.net.index_mac_ip(mac, ip)
+ e_root.add_seen_mac(ifname, mac)
+
+ self.linux_inspector.disconnect()
diff --git a/haircontrol/inspectors.py b/haircontrol/inspectors.py
index 9b797fe..33bf360 100644
--- a/haircontrol/inspectors.py
+++ b/haircontrol/inspectors.py
@@ -1,21 +1,123 @@
import re
+import xml.etree.ElementTree
class Inspector():
cmds = {}
- fd = None
- def parse(self):
- return None #XXX Implement
+ def __init__(self):
+ #XXX in a mockup class ?
+ self.testDataPath = '../test-data/input'
+ self.e = None
+
+ def connect(self, e):
+ self.e = e
+
+ def disconnect(self):
+ self.e = None
+
+ def command(self, cmdname):
+ cmddef = self.cmds.get(cmdname)
+ if not cmddef:
+ return None
+ fd = self._exec(cmdname, cmddef['cmd'])
+ result = []
+ re = cmddef.get('re')
+ func = cmddef.get('func')
+ if re:
+ for line in fd:
+ matches = re.match(line)
+ if matches:
+ result.append(matches.groups())
+ elif func:
+ result = func(self, fd)
+ fd.close()
+ return result
+
+ def _exec(self, cmdname, cmd):
+ #XXX in a mockup class ?
+ mockfile = self.testDataPath + '/' + self.e.name + '-' + cmdname + '.out'
+ return open(mockfile)
+
+
class LinuxInspector(Inspector):
+
+ def parse_lldpctl_xml(self, fd):
+ result = []
+ root = xml.etree.ElementTree.parse(fd).getroot()
+ for iface in root.iter('interface'):
+ local_ifname = iface.get('name')
+ local_mac = self.e.ifaces[local_ifname].mac
+ chassis = iface.find('chassis')
+ remote_name = chassis.find('name').text
+ remote_ipmgmt = chassis.find('mgmt-ip').text
+ ports = []
+ for port in iface.findall('port'):
+ remote_ifname = port.find('id').text
+ ports.append(remote_ifname)
+ result.append( (local_ifname, local_mac, remote_name, remote_ipmgmt, ports) )
+ return result
+
cmds = {
'ip-neigh': {
'cmd': 'ip neigh',
+ 're': 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
+ },
+ 'ip-link': {
+ 'cmd': 'ip -o link',
'kind': 'text',
- 'fields': ['ip','ifname','mac'],
- 're': re.compile("(?P<ip>[a-f0-9:.]+) dev (?P<ifname>.*) lladdr (?P<mac>[a-f0-9:]*)")
- }
+ 're': re.compile("(?P<id>\d+):\s+(?P<ifname>[^:]*):.+\s+(?:link/ether\s+(?P<mac>[a-f0-9:]*)\s+brd|link/none)")
+ # 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 8c:89:a5:c7:be:88 brd ff:ff:ff:ff:ff:ff
+ },
+ 'lldpctl': {
+ 'cmd': 'lldpctl -f xml',
+ 'func': parse_lldpctl_xml
+ #<?xml version="1.0" encoding="UTF-8"?>
+ #<lldp label="LLDP neighbors">
+ # <interface label="Interface" name="eth0" via="CDPv1" rid="2" age="0 day, 21:41:18">
+ # <chassis label="Chassis">
+ # <id label="ChassisID" type="local">Robert_VILO</id>
+ # <name label="SysName">Robert_VILO</name>
+ # <descr label="SysDescr">LM5 running on
+ #XM.v5.5.10</descr>
+ # <mgmt-ip label="MgmtIP">172.16.10.26</mgmt-ip>
+ # <mgmt-ip label="MgmtIP">169.254.227.212</mgmt-ip>
+ # <capability label="Capability" type="Bridge" enabled="on"/>
+ # </chassis>
+ # <port label="Port">
+ # <id label="PortID" type="ifname">br0</id>
+ # <descr label="PortDescr">br0</descr>
+ # </port>
+ # </interface>
+ # <interface label="Interface" name="eth0" via="CDPv1" rid="4" age="0 day, 21:41:18">
+ # ...
+ # </interface>
+ #</lldp>
+ },
}
+
+
+class UbntInspector(Inspector):
+
+ def parse_status_json(self, fd):
+ return 'parse_status_json'
+
+ def parse_brmacs_json(self, fd):
+ return 'parse_brmacs_json'
+
+ cmds = {
+ 'status.cgi': {
+ 'cmd':'..',
+ 'kind': 'cgi-json',
+ 'func': parse_status_json
+ },
+ 'brmacs.cgi': {
+ 'cmd':'..',
+ 'kind': 'cgi-json',
+ 'func': parse_brmacs_json
+ }
+ }
diff --git a/test-data/input/stg2-lldp.out b/test-data/input/stg2-lldpctl.out
index 2200b95..2200b95 100644
--- a/test-data/input/stg2-lldp.out
+++ b/test-data/input/stg2-lldpctl.out
diff --git a/tests/test_discovery.py b/tests/test_discovery.py
index 7fb0bd2..06c61e3 100755
--- a/tests/test_discovery.py
+++ b/tests/test_discovery.py
@@ -3,30 +3,12 @@ import unittest
#import pudb; pudb.set_trace()
from context import haircontrol
-from haircontrol import discovery, data, inspectors
-
-class MockInspector(inspectors.Inspector):
- def __init__(self, testDataPath):
- self.testDataPath = testDataPath
- self.e = None
-
- def connect(self, e):
- self.e = e
-
- def disconnect(self):
- self.e = None
-
- def command(self, command):
- if not self.e:
- return None
- mockfile = self.testDataPath + '/' + self.e.name + '-' + command + '.out'
- return open(mockfile) #XXX use Inspector.parse()
-
+from haircontrol import discovery, data #, inspectors
class TestDiscovery(unittest.TestCase):
def setUp(self):
- self.discovery = discovery.Discovery(MockInspector('../test-data/input'))
+ self.discovery = discovery.Discovery() #MockInspector('../test-data/input'))
self.maxDiff=None
def test_wire_graph(self):