]> git.ipfire.org Git - thirdparty/lldpd.git/blob - tests/integration/test_lldpcli.py
tests: add test of `lldpcli watch`
[thirdparty/lldpd.git] / tests / integration / test_lldpcli.py
1 import pytest
2 import time
3 import re
4 import platform
5 import json
6 import xml.etree.ElementTree as ET
7
8
9 @pytest.fixture(scope='session')
10 def uname():
11 return "{} {} {} {}".format(
12 platform.system(),
13 platform.release(),
14 platform.version(),
15 platform.machine())
16
17
18 def test_text_output(lldpd1, lldpd, lldpcli, namespaces, uname):
19 with namespaces(2):
20 lldpd()
21 with namespaces(1):
22 result = lldpcli("show", "neighbors", "details")
23 assert result.returncode == 0
24 expected = """-------------------------------------------------------------------------------
25 LLDP neighbors:
26 -------------------------------------------------------------------------------
27 Interface: eth0, via: LLDP, RID: 1, Time: 0 day, 00:00:{seconds}
28 Chassis:
29 ChassisID: mac 00:00:00:00:00:02
30 SysName: ns-2.example.com
31 SysDescr: Spectacular GNU/Linux 2016 {uname}
32 MgmtIP: fe80::200:ff:fe00:2
33 Capability: Bridge, off
34 Capability: Router, {router}
35 Capability: Wlan, off
36 Capability: Station, {station}
37 Port:
38 PortID: mac 00:00:00:00:00:02
39 PortDescr: eth1
40 -------------------------------------------------------------------------------
41 """
42 out = result.stdout.decode('ascii')
43 seconds = re.search(r'^Interface: .*(\d\d)$',
44 out,
45 re.MULTILINE).group(1)
46 router = re.search(r'^ Capability: Router, (.*)$',
47 out,
48 re.MULTILINE).group(1)
49 station = re.search(r'^ Capability: Station, (.*)$',
50 out,
51 re.MULTILINE).group(1)
52 out = re.sub(r' *$', '', out, flags=re.MULTILINE)
53 assert out == expected.format(seconds=seconds,
54 router=router,
55 station=station,
56 uname=uname)
57
58
59 @pytest.mark.skipif('JSON' not in pytest.config.lldpcli.outputs,
60 reason="JSON not supported")
61 def test_json_output(lldpd1, lldpd, lldpcli, namespaces, uname):
62 with namespaces(2):
63 lldpd()
64 with namespaces(1):
65 result = lldpcli("-f", "json", "show", "neighbors", "details")
66 assert result.returncode == 0
67 out = result.stdout.decode('ascii')
68 j = json.loads(out)
69
70 eth0 = j['lldp']['interface']['eth0']
71 del eth0['age']
72 del eth0['chassis']['ns-2.example.com']['capability'][3]
73 del eth0['chassis']['ns-2.example.com']['capability'][1]
74 expected = {"lldp": {
75 "interface": {"eth0": {
76 "via": "LLDP",
77 "rid": "1",
78 "chassis": {
79 "ns-2.example.com": {
80 "id": {
81 "type": "mac",
82 "value": "00:00:00:00:00:02"
83 },
84 "descr": "Spectacular GNU/Linux 2016 {}".format(uname),
85 "mgmt-ip": "fe80::200:ff:fe00:2",
86 "capability": [
87 {"type": "Bridge", "enabled": False},
88 {"type": "Wlan", "enabled": False},
89 ]
90 }
91 },
92 "port": {
93 "id": {
94 "type": "mac",
95 "value": "00:00:00:00:00:02"
96 },
97 "descr": "eth1"
98 }
99 }}
100 }}
101
102 assert j == expected
103
104
105 @pytest.mark.skipif('XML' not in pytest.config.lldpcli.outputs,
106 reason="XML not supported")
107 def test_xml_output(lldpd1, lldpd, lldpcli, namespaces, uname):
108 with namespaces(2):
109 lldpd()
110 with namespaces(1):
111 result = lldpcli("-f", "xml", "show", "neighbors", "details")
112 assert result.returncode == 0
113 out = result.stdout.decode('ascii')
114 xml = ET.fromstring(out)
115
116 age = xml.findall('./interface[1]')[0].attrib['age']
117 router = xml.findall("./interface[1]/chassis/"
118 "capability[@type='Router']")[0].attrib['enabled']
119 station = xml.findall("./interface[1]/chassis/"
120 "capability[@type='Station']")[0].attrib['enabled']
121 expected = ET.fromstring("""<?xml version="1.0" encoding="UTF-8"?>
122 <lldp label="LLDP neighbors">
123 <interface label="Interface" name="eth0" via="LLDP" rid="1" age="{age}">
124 <chassis label="Chassis">
125 <id label="ChassisID" type="mac">00:00:00:00:00:02</id>
126 <name label="SysName">ns-2.example.com</name>
127 <descr label="SysDescr">Spectacular GNU/Linux 2016 {uname}</descr>
128 <mgmt-ip label="MgmtIP">fe80::200:ff:fe00:2</mgmt-ip>
129 <capability label="Capability" type="Bridge" enabled="off"/>
130 <capability label="Capability" type="Router" enabled="{router}"/>
131 <capability label="Capability" type="Wlan" enabled="off"/>
132 <capability label="Capability" type="Station" enabled="{station}"/>
133 </chassis>
134 <port label="Port">
135 <id label="PortID" type="mac">00:00:00:00:00:02</id>
136 <descr label="PortDescr">eth1</descr>
137 </port>
138 </interface>
139 </lldp>
140 """.format(age=age,
141 router=router,
142 station=station,
143 uname=uname))
144 assert ET.tostring(xml) == ET.tostring(expected)
145
146
147 @pytest.mark.skipif('Dot3' not in pytest.config.lldpd.features,
148 reason="Dot3 not supported")
149 def test_configure_one_port(lldpd1, lldpd, lldpcli, namespaces, links):
150 links(namespaces(1), namespaces(2))
151 with namespaces(2):
152 lldpd()
153 result = lldpcli(*("configure ports eth3 dot3 power "
154 "pse supported enabled paircontrol powerpairs "
155 "spare class class-3").split())
156 assert result.returncode == 0
157 time.sleep(3)
158 with namespaces(1):
159 out = lldpcli("-f", "keyvalue", "show", "neighbors", "details")
160 assert out['lldp.eth0.port.descr'] == 'eth1'
161 assert 'lldp.eth0.port.power.device-type' not in out
162 assert out['lldp.eth2.port.descr'] == 'eth3'
163 assert out['lldp.eth2.port.power.device-type'] == 'PSE'
164
165
166 @pytest.mark.skipif('Dot3' not in pytest.config.lldpd.features,
167 reason="Dot3 not supported")
168 def test_new_port_take_default(lldpd1, lldpd, lldpcli, namespaces, links):
169 with namespaces(2):
170 lldpd()
171 result = lldpcli(*("configure dot3 power "
172 "pse supported enabled paircontrol powerpairs "
173 "spare class class-3").split())
174 assert result.returncode == 0
175 time.sleep(3)
176 with namespaces(1):
177 # Check this worked
178 out = lldpcli("-f", "keyvalue", "show", "neighbors", "details")
179 assert out['lldp.eth0.port.descr'] == 'eth1'
180 assert out['lldp.eth0.port.power.device-type'] == 'PSE'
181 links(namespaces(1), namespaces(2))
182 time.sleep(6)
183 with namespaces(1):
184 out = lldpcli("-f", "keyvalue", "show", "neighbors", "details")
185 assert out['lldp.eth2.port.descr'] == 'eth3'
186 assert out['lldp.eth2.port.power.device-type'] == 'PSE'
187
188
189 @pytest.mark.skipif('Dot3' not in pytest.config.lldpd.features,
190 reason="Dot3 not supported")
191 def test_port_keep_configuration(lldpd1, lldpd, lldpcli, namespaces, links):
192 links(namespaces(1), namespaces(2))
193 with namespaces(2):
194 lldpd()
195 result = lldpcli(*("configure ports eth3 dot3 power "
196 "pse supported enabled paircontrol powerpairs "
197 "spare class class-3").split())
198 assert result.returncode == 0
199 time.sleep(3)
200 links.down('eth3')
201 time.sleep(4)
202 links.up('eth3')
203 time.sleep(4)
204 with namespaces(1):
205 out = lldpcli("-f", "keyvalue", "show", "neighbors", "details")
206 assert out['lldp.eth2.port.descr'] == 'eth3'
207 assert out['lldp.eth2.port.power.device-type'] == 'PSE'
208
209
210 def test_watch(lldpd1, lldpd, lldpcli, namespaces, links):
211 with namespaces(2):
212 lldpd()
213 with namespaces(1):
214 result = lldpcli("show", "neighbors")
215 assert result.returncode == 0
216 out = result.stdout.decode('ascii')
217 assert "ns-2.example.com" in out
218
219 # Put a link down and immediately watch for a change
220 links.down('eth0')
221 result = lldpcli("watch", "limit", "1")
222 assert result.returncode == 0
223 expected = out.replace('LLDP neighbors:', 'LLDP neighbor deleted:')
224 expected = re.sub(r', Time: 0 day, 00:.*$', '', expected,
225 flags=re.MULTILINE)
226 got = result.stdout.decode('ascii')
227 got = re.sub(r', Time: 0 day, 00:.*$', '', got,
228 flags=re.MULTILINE)
229 assert got == expected
230
231
232 @pytest.mark.skipif('XML' not in pytest.config.lldpcli.outputs,
233 reason="XML not supported")
234 def test_watch_xml(lldpd1, lldpd, lldpcli, namespaces, links):
235 with namespaces(2):
236 lldpd()
237 with namespaces(1):
238 result = lldpcli("-f", "xml", "show", "neighbors")
239 assert result.returncode == 0
240 expected = result.stdexpected.decode('ascii')
241 expected = ET.fromstring(expected)
242 assert [x.text
243 for x in expected.findall("./interface/chassis/name")] == \
244 ["ns-2.example.com"]
245
246 # Put a link down and immediately watch for a change
247 links.down('eth0')
248 result = lldpcli("-f", "xml", "watch", "limit", "1")
249 assert result.returncode == 0
250 expected.tag = 'lldp-deleted'
251 expected.set('label', 'LLDP neighbor deleted')
252 expected.find('./interface').set('age', '')
253 got = result.stdout.decode('ascii')
254 got = ET.fromstring(got)
255 got.find('./interface').set('age', '')
256 assert ET.tostring(got) == ET.tostring(expected)
257
258
259 @pytest.mark.skipif('JSON' not in pytest.config.lldpcli.outputs,
260 reason="JSON not supported")
261 def test_watch_json(lldpd1, lldpd, lldpcli, namespaces, links):
262 with namespaces(2):
263 lldpd()
264 with namespaces(1):
265 result = lldpcli("-f", "json", "show", "neighbors")
266 assert result.returncode == 0
267 expected = result.stdout.decode('ascii')
268 expected = json.loads(expected)
269 assert 'ns-2.example.com' in \
270 expected['lldp']['interface']['eth0']['chassis']
271
272 # Put a link down and immediately watch for a change
273 links.down('eth0')
274 result = lldpcli("-f", "json", "watch", "limit", "1")
275 assert result.returncode == 0
276 got = result.stdout.decode('ascii')
277 got = json.loads(got)
278 expected['lldp-deleted'] = expected['lldp']
279 del expected['lldp']
280 del expected['lldp-deleted']['interface']['eth0']['age']
281 del got['lldp-deleted']['interface']['eth0']['age']
282 assert got == expected