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