]>
git.ipfire.org Git - thirdparty/lldpd.git/blob - tests/integration/test_lldpcli.py
add740b6112c1830ac3e4ad400d46c6e7e42f67a
7 import xml
.etree
.ElementTree
as ET
10 @pytest.fixture(scope
='session')
12 return "{} {} {} {}".format(
18 @pytest.mark
.parametrize("command, expected", [
20 """-------------------------------------------------------------------------------
22 -------------------------------------------------------------------------------
23 Interface: eth0, via: LLDP, RID: 1, Time: 0 day, 00:00:{seconds}
25 ChassisID: mac 00:00:00:00:00:02
26 SysName: ns-2.example.com
27 SysDescr: Spectacular GNU/Linux 2016 {uname}
28 MgmtIP: fe80::200:ff:fe00:2
29 Capability: Bridge, off
30 Capability: Router, {router}
32 Capability: Station, {station}
34 PortID: mac 00:00:00:00:00:02
37 -------------------------------------------------------------------------------
40 """-------------------------------------------------------------------------------
42 -------------------------------------------------------------------------------
43 Interface: eth0, via: unknown, Time: {time}
45 ChassisID: mac 00:00:00:00:00:01
46 SysName: ns-1.example.com
47 SysDescr: Spectacular GNU/Linux 2016 {uname}
48 MgmtIP: fe80::200:ff:fe00:1
49 Capability: Bridge, off
50 Capability: Router, {router}
52 Capability: Station, {station}
54 PortID: mac 00:00:00:00:00:01
57 -------------------------------------------------------------------------------
58 """)], ids
=["neighbors", "interfaces"])
59 def test_text_output(lldpd1
, lldpd
, lldpcli
, namespaces
, uname
, command
,
65 *shlex
.split("show {} details".format(command
)))
66 assert result
.returncode
== 0
67 out
= result
.stdout
.decode('ascii')
69 if 'Dot3' in pytest
.config
.lldpd
.features
:
71 PMD autoneg: supported: no, enabled: no
72 MAU oper type: 10GigBaseCX4 - X copper over 8 pair 100-Ohm balanced cable"""
76 out
= result
.stdout
.decode('ascii')
77 time
= re
.search(r
'^Interface: .*Time: (.*)$',
79 re
.MULTILINE
).group(1)
80 seconds
= re
.search(r
'^Interface: .*(\d\d)$',
82 re
.MULTILINE
).group(1)
83 router
= re
.search(r
'^ Capability: Router, (.*)$',
85 re
.MULTILINE
).group(1)
86 station
= re
.search(r
'^ Capability: Station, (.*)$',
88 re
.MULTILINE
).group(1)
89 out
= re
.sub(r
' *$', '', out
, flags
=re
.MULTILINE
)
90 assert out
== expected
.format(seconds
=seconds
,
97 @pytest.mark
.skipif('JSON' not in pytest
.config
.lldpcli
.outputs
,
98 reason
="JSON not supported")
99 @pytest.mark
.parametrize("command, expected", [
107 "ns-2.example.com": {
110 "value": "00:00:00:00:00:02"},
111 "descr": "Spectacular GNU/Linux 2016 {}".format(uname
),
112 "mgmt-ip": "fe80::200:ff:fe00:2",
114 {"type": "Bridge", "enabled": False},
115 {"type": "Wlan", "enabled": False},]}},
119 "value": "00:00:00:00:00:02"},
128 "ns-1.example.com": {
131 "value": "00:00:00:00:00:01"},
132 "descr": "Spectacular GNU/Linux 2016 {}".format(uname
),
133 "mgmt-ip": "fe80::200:ff:fe00:1",
135 {"type": "Bridge", "enabled": False},
136 {"type": "Wlan", "enabled": False},]}},
140 "value": "00:00:00:00:00:01"},
143 "ttl": "120"}}}}})], ids
=["neighbors", "interfaces"])
144 def test_json_output(lldpd1
, lldpd
, lldpcli
, namespaces
, uname
, command
,
150 *shlex
.split("-f json show {} details".format(command
)))
151 assert result
.returncode
== 0
152 out
= result
.stdout
.decode('ascii')
155 eth0
= j
['lldp']['interface']['eth0']
156 name
= next(k
for k
,v
in eth0
['chassis'].items() if k
.startswith('ns'))
158 del eth0
['chassis'][name
]['capability'][3]
159 del eth0
['chassis'][name
]['capability'][1]
161 descr
= "Spectacular GNU/Linux 2016 {}".format(uname
)
162 expected
['lldp']['interface']['eth0']['chassis'][name
]["descr"] = descr
164 if 'Dot3' in pytest
.config
.lldpd
.features
:
165 expected
['lldp']['interface']['eth0']['port']['auto-negotiation'] = {
168 "current": "10GigBaseCX4 - X copper over 8 pair 100-Ohm balanced cable"
173 @pytest.mark
.skipif('JSON' not in pytest
.config
.lldpcli
.outputs
,
174 reason
="JSON not supported")
175 @pytest.mark
.parametrize("command, expected", [
185 "value": "00:00:00:00:00:02"
187 "name": [{"value": "ns-2.example.com"}],
188 "descr": [{"value": "Spectacular GNU/Linux 2016 {}".format(uname
)}],
189 "mgmt-ip": [{"value": "fe80::200:ff:fe00:2"}],
191 {"type": "Bridge", "enabled": False},
192 {"type": "Wlan", "enabled": False},
198 "value": "00:00:00:00:00:02"
200 "descr": [{"value": "eth1"}],
201 "ttl": [{"value": "120"}]
213 "value": "00:00:00:00:00:01"
215 "name": [{"value": "ns-1.example.com"}],
216 "descr": [{"value": "Spectacular GNU/Linux 2016 {}".format(uname
)}],
217 "mgmt-ip": [{"value": "fe80::200:ff:fe00:1"}],
219 {"type": "Bridge", "enabled": False},
220 {"type": "Wlan", "enabled": False},
226 "value": "00:00:00:00:00:01"
228 "descr": [{"value": "eth0"}]
230 "ttl": [{"ttl": "120"}]
232 ]})], ids
=["neighbors", "interfaces"])
233 def test_json0_output(lldpd1
, lldpd
, lldpcli
, namespaces
, uname
, command
,
239 *shlex
.split("-f json0 show {} details".format(command
)))
240 assert result
.returncode
== 0
241 out
= result
.stdout
.decode('ascii')
244 eth0
= j
['lldp'][0]['interface'][0]
246 del eth0
['chassis'][0]['capability'][3]
247 del eth0
['chassis'][0]['capability'][1]
249 descr
= "Spectacular GNU/Linux 2016 {}".format(uname
)
250 expected
['lldp'][0]['interface'][0]['chassis'][0]["descr"][0]['value'] = descr
252 if 'Dot3' in pytest
.config
.lldpd
.features
:
253 expected
['lldp'][0]['interface'][0]['port'][0]['auto-negotiation'] = [{
256 "current": [{"value":
257 "10GigBaseCX4 - X copper over 8 pair 100-Ohm balanced cable"}]
262 @pytest.mark
.skipif('XML' not in pytest
.config
.lldpcli
.outputs
,
263 reason
="XML not supported")
264 @pytest.mark
.parametrize("command, expected", [
266 """<?xml version="1.0" encoding="UTF-8"?>
267 <lldp label="LLDP neighbors">
268 <interface label="Interface" name="eth0" via="LLDP" rid="1" age="{age}">
269 <chassis label="Chassis">
270 <id label="ChassisID" type="mac">00:00:00:00:00:02</id>
271 <name label="SysName">ns-2.example.com</name>
272 <descr label="SysDescr">Spectacular GNU/Linux 2016 {uname}</descr>
273 <mgmt-ip label="MgmtIP">fe80::200:ff:fe00:2</mgmt-ip>
274 <capability label="Capability" type="Bridge" enabled="off"/>
275 <capability label="Capability" type="Router" enabled="{router}"/>
276 <capability label="Capability" type="Wlan" enabled="off"/>
277 <capability label="Capability" type="Station" enabled="{station}"/>
280 <id label="PortID" type="mac">00:00:00:00:00:02</id>
281 <descr label="PortDescr">eth1</descr>
282 <ttl label="TTL">120</ttl>{dot3}
288 """<?xml version="1.0" encoding="UTF-8"?>
289 <lldp label="LLDP interfaces">
290 <interface label="Interface" name="eth0" via="unknown" age="{age}">
291 <chassis label="Chassis">
292 <id label="ChassisID" type="mac">00:00:00:00:00:01</id>
293 <name label="SysName">ns-1.example.com</name>
294 <descr label="SysDescr">Spectacular GNU/Linux 2016 {uname}</descr>
295 <mgmt-ip label="MgmtIP">fe80::200:ff:fe00:1</mgmt-ip>
296 <capability label="Capability" type="Bridge" enabled="off"/>
297 <capability label="Capability" type="Router" enabled="{router}"/>
298 <capability label="Capability" type="Wlan" enabled="off"/>
299 <capability label="Capability" type="Station" enabled="{station}"/>
302 <id label="PortID" type="mac">00:00:00:00:00:01</id>
303 <descr label="PortDescr">eth0</descr>{dot3}
305 <ttl label="TTL" ttl="120"/>
308 """)], ids
=["neighbors", "interfaces"])
309 def test_xml_output(lldpd1
, lldpd
, lldpcli
, namespaces
, uname
, command
,
315 *shlex
.split("-f xml show {} details".format(command
)))
316 assert result
.returncode
== 0
317 out
= result
.stdout
.decode('ascii')
318 xml
= ET
.fromstring(out
)
320 age
= xml
.findall('./interface[1]')[0].attrib
['age']
321 router
= xml
.findall("./interface[1]/chassis/"
322 "capability[@type='Router']")[0].attrib
['enabled']
323 station
= xml
.findall("./interface[1]/chassis/"
324 "capability[@type='Station']")[0].attrib
['enabled']
325 if 'Dot3' in pytest
.config
.lldpd
.features
:
327 <auto-negotiation enabled="no" label="PMD autoneg" supported="no">
328 <current label="MAU oper type">10GigBaseCX4 - X copper over 8 pair 100-Ohm balanced cable</current>
329 </auto-negotiation>"""
332 expected
= ET
.fromstring(expected
.format(age
=age
,
337 assert ET
.tostring(xml
) == ET
.tostring(expected
)
340 @pytest.mark
.skipif('Dot3' not in pytest
.config
.lldpd
.features
,
341 reason
="Dot3 not supported")
342 def test_configure_one_port(lldpd1
, lldpd
, lldpcli
, namespaces
, links
):
343 links(namespaces(1), namespaces(2))
346 result
= lldpcli(*("configure ports eth3 dot3 power "
347 "pse supported enabled paircontrol powerpairs "
348 "spare class class-3").split())
349 assert result
.returncode
== 0
351 out
= lldpcli("-f", "keyvalue", "show", "interfaces", "details")
352 assert 'lldp.eth1.port.power.device-type' not in out
353 assert out
['lldp.eth3.port.power.device-type'] == 'PSE'
355 out
= lldpcli("-f", "keyvalue", "show", "neighbors", "details")
356 assert out
['lldp.eth0.port.descr'] == 'eth1'
357 assert 'lldp.eth0.port.power.device-type' not in out
358 assert out
['lldp.eth2.port.descr'] == 'eth3'
359 assert out
['lldp.eth2.port.power.device-type'] == 'PSE'
362 @pytest.mark
.skipif('Dot3' not in pytest
.config
.lldpd
.features
,
363 reason
="Dot3 not supported")
364 def test_new_port_take_default(lldpd1
, lldpd
, lldpcli
, namespaces
, links
):
367 result
= lldpcli(*("configure dot3 power "
368 "pse supported enabled paircontrol powerpairs "
369 "spare class class-3").split())
370 assert result
.returncode
== 0
372 out
= lldpcli("-f", "keyvalue", "show", "interfaces", "details")
373 assert out
['lldp.eth1.port.power.device-type'] == 'PSE'
376 out
= lldpcli("-f", "keyvalue", "show", "neighbors", "details")
377 assert out
['lldp.eth0.port.descr'] == 'eth1'
378 assert out
['lldp.eth0.port.power.device-type'] == 'PSE'
379 links(namespaces(1), namespaces(2), 4)
382 out
= lldpcli("-f", "keyvalue", "show", "neighbors", "details")
383 assert out
['lldp.eth2.port.descr'] == 'eth3'
384 assert out
['lldp.eth2.port.power.device-type'] == 'PSE'
386 out
= lldpcli("-f", "keyvalue", "show", "interfaces", "details")
387 assert out
['lldp.eth3.port.power.device-type'] == 'PSE'
390 @pytest.mark
.skipif('Dot3' not in pytest
.config
.lldpd
.features
,
391 reason
="Dot3 not supported")
392 def test_port_keep_configuration_when_down(lldpd
, lldpcli
, namespaces
, links
):
396 result
= lldpcli(*("configure ports eth3 dot3 power "
397 "pse supported enabled paircontrol powerpairs "
398 "spare class class-3").split())
399 assert result
.returncode
== 0
403 # eth3 configuration is kept because the port still exists.
404 out
= lldpcli("-f", "keyvalue", "show", "interfaces", "details")
405 assert out
['lldp.eth3.port.power.device-type'] == 'PSE'
409 # eth3 configuration is unchanged
410 out
= lldpcli("-f", "keyvalue", "show", "interfaces", "details")
411 assert out
['lldp.eth3.port.power.device-type'] == 'PSE'
414 @pytest.mark
.skipif('Dot3' not in pytest
.config
.lldpd
.features
,
415 reason
="Dot3 not supported")
416 def test_port_forget_configuration(lldpd
, lldpcli
,
421 result
= lldpcli(*("configure dot3 power "
422 "pse supported enabled paircontrol powerpairs "
423 "spare class class-3").split())
424 assert result
.returncode
== 0
428 # eth3 configuration was forgotten because it disappeared.
429 out
= lldpcli("-f", "keyvalue", "show", "interfaces", "details")
430 assert 'lldp.eth3.port.power.device-type' not in out
433 def test_watch(lldpd1
, lldpd
, lldpcli
, namespaces
, links
):
437 result
= lldpcli("show", "neighbors")
438 assert result
.returncode
== 0
439 out
= result
.stdout
.decode('ascii')
440 assert "ns-2.example.com" in out
442 # Put a link down and immediately watch for a change
444 result
= lldpcli("watch", "limit", "1")
445 assert result
.returncode
== 0
446 expected
= out
.replace('LLDP neighbors:', 'LLDP neighbor deleted:')
447 expected
= re
.sub(r
', Time: 0 day, 00:.*$', '', expected
,
449 got
= result
.stdout
.decode('ascii')
450 got
= re
.sub(r
', Time: 0 day, 00:.*$', '', got
,
452 assert got
== expected
455 @pytest.mark
.skipif('XML' not in pytest
.config
.lldpcli
.outputs
,
456 reason
="XML not supported")
457 def test_watch_xml(lldpd1
, lldpd
, lldpcli
, namespaces
, links
):
461 result
= lldpcli("-f", "xml", "show", "neighbors")
462 assert result
.returncode
== 0
463 expected
= result
.stdout
.decode('ascii')
464 expected
= ET
.fromstring(expected
)
466 for x
in expected
.findall("./interface/chassis/name")] == \
469 # Put a link down and immediately watch for a change
471 result
= lldpcli("-f", "xml", "watch", "limit", "1")
472 assert result
.returncode
== 0
473 expected
.tag
= 'lldp-deleted'
474 expected
.set('label', 'LLDP neighbor deleted')
475 expected
.find('./interface').set('age', '')
476 got
= result
.stdout
.decode('ascii')
477 got
= ET
.fromstring(got
)
478 got
.find('./interface').set('age', '')
479 assert ET
.tostring(got
) == ET
.tostring(expected
)
482 @pytest.mark
.skipif('JSON' not in pytest
.config
.lldpcli
.outputs
,
483 reason
="JSON not supported")
484 def test_watch_json(lldpd1
, lldpd
, lldpcli
, namespaces
, links
):
488 result
= lldpcli("-f", "json", "show", "neighbors")
489 assert result
.returncode
== 0
490 expected
= result
.stdout
.decode('ascii')
491 expected
= json
.loads(expected
)
492 assert 'ns-2.example.com' in \
493 expected
['lldp']['interface']['eth0']['chassis']
495 # Put a link down and immediately watch for a change
497 result
= lldpcli("-f", "json", "watch", "limit", "1")
498 assert result
.returncode
== 0
499 got
= result
.stdout
.decode('ascii')
500 got
= json
.loads(got
)
501 expected
['lldp-deleted'] = expected
['lldp']
503 del expected
['lldp-deleted']['interface']['eth0']['age']
504 del got
['lldp-deleted']['interface']['eth0']['age']
505 assert got
== expected
508 def test_return_code(lldpd1
, lldpcli
, namespaces
):
510 result
= lldpcli("show", "neighbors")
511 assert result
.returncode
== 0
512 result
= lldpcli("show", "interfaces")
513 assert result
.returncode
== 0
514 result
= lldpcli("unknown", "command")
515 assert result
.returncode
== 1