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