tag_datatag(w, "descr", "PortDescr",
lldpctl_atom_get_str(port, lldpctl_k_port_descr));
- if (details)
+ if (details &&
+ lldpctl_atom_get_int(port, lldpctl_k_port_ttl) > 0)
tag_datatag(w, "ttl", "TTL",
lldpctl_atom_get_str(port, lldpctl_k_port_ttl));
tag_end(w);
}
+static void
+display_local_ttl(struct writer *w, lldpctl_conn_t *conn, int details)
+{
+ char *ttl;
+ long int tx_hold;
+ long int tx_interval;
+
+ lldpctl_atom_t *configuration;
+ configuration = lldpctl_get_configuration(conn);
+ if (!configuration) {
+ log_warnx("lldpctl", "not able to get configuration. %s",
+ lldpctl_last_strerror(conn));
+ return;
+ }
+
+ tx_hold = lldpctl_atom_get_int(configuration, lldpctl_k_config_tx_hold);
+ tx_interval = lldpctl_atom_get_int(configuration, lldpctl_k_config_tx_interval);
+
+ if (asprintf(&ttl, "%lu", tx_hold*tx_interval) == -1) {
+ log_warnx("lldpctl", "not enough memory to build TTL.");
+ goto end;
+ }
+
+ tag_start(w, "ttl", "TTL");
+ tag_attr(w, "ttl", "", ttl);
+ tag_end(w);
+ free(ttl);
+end:
+ lldpctl_atom_dec_ref(configuration);
+ return;
+}
+
static void
display_vlans(struct writer *w, lldpctl_atom_t *port)
{
void
display_interface(lldpctl_conn_t *conn, struct writer *w, int hidden,
- lldpctl_atom_t *iface, lldpctl_atom_t *neighbor, int details, int protocol)
+ lldpctl_atom_t *iface, lldpctl_atom_t *port, int details, int protocol)
{
+ int local = 0;
+
if (!hidden &&
- lldpctl_atom_get_int(neighbor, lldpctl_k_port_hidden))
+ lldpctl_atom_get_int(port, lldpctl_k_port_hidden))
return;
/* user might have specified protocol to filter on display */
if ((protocol != LLDPD_MODE_MAX) &&
- (protocol != lldpctl_atom_get_int(neighbor, lldpctl_k_port_protocol)))
+ (protocol != lldpctl_atom_get_int(port, lldpctl_k_port_protocol)))
return;
- lldpctl_atom_t *chassis = lldpctl_atom_get(neighbor, lldpctl_k_port_chassis);
+ /* Infer local / remote port from the port index (remote == 0) */
+ local = lldpctl_atom_get_int(port, lldpctl_k_port_index)>0?1:0;
+
+ lldpctl_atom_t *chassis = lldpctl_atom_get(port, lldpctl_k_port_chassis);
tag_start(w, "interface", "Interface");
tag_attr(w, "name", "",
lldpctl_atom_get_str(iface, lldpctl_k_interface_name));
tag_attr(w, "via" , "via",
- lldpctl_atom_get_str(neighbor, lldpctl_k_port_protocol));
+ lldpctl_atom_get_str(port, lldpctl_k_port_protocol));
if (details > DISPLAY_BRIEF) {
- tag_attr(w, "rid" , "RID",
- lldpctl_atom_get_str(chassis, lldpctl_k_chassis_index));
+ if (!local)
+ tag_attr(w, "rid" , "RID",
+ lldpctl_atom_get_str(chassis, lldpctl_k_chassis_index));
tag_attr(w, "age" , "Time",
- display_age(lldpctl_atom_get_int(neighbor, lldpctl_k_port_age)));
+ display_age(lldpctl_atom_get_int(port, lldpctl_k_port_age)));
}
display_chassis(w, chassis, details);
- display_port(w, neighbor, details);
+ display_port(w, port, details);
+ if (details && local)
+ display_local_ttl(w, conn, details);
if (details == DISPLAY_DETAILS) {
- display_vlans(w, neighbor);
- display_ppvids(w, neighbor);
- display_pids(w, neighbor);
- display_med(w, neighbor, chassis);
+ display_vlans(w, port);
+ display_ppvids(w, port);
+ display_pids(w, port);
+ display_med(w, port, chassis);
}
lldpctl_atom_dec_ref(chassis);
- display_custom_tlvs(w, neighbor);
+ display_custom_tlvs(w, port);
tag_end(w);
}
tag_end(w);
}
+
+/**
+ * Display information about local interfaces.
+ *
+ * @param conn Connection to lldpd.
+ * @param w Writer.
+ * @param hidden Whatever to show hidden ports.
+ * @param env Environment from which we may find the list of ports.
+ * @param details Level of details we need (DISPLAY_*).
+ */
+void
+display_local_interfaces(lldpctl_conn_t *conn, struct writer *w,
+ struct cmd_env *env,
+ int hidden, int details)
+{
+ lldpctl_atom_t *iface;
+ int protocol = LLDPD_MODE_MAX;
+
+ tag_start(w, "lldp", "LLDP interfaces");
+ while ((iface = cmd_iterate_on_interfaces(conn, env))) {
+ lldpctl_atom_t *port;
+ port = lldpctl_get_port(iface);
+ display_interface(conn, w, hidden, iface, port, details, protocol);
+ lldpctl_atom_dec_ref(port);
+ }
+ tag_end(w);
+ }
+
void
display_stat(struct writer *w, const char *tag, const char *descr,
long unsigned int cnt)
import pytest
+import shlex
import time
import re
import platform
platform.version(),
platform.machine())
-
-def test_text_output(lldpd1, lldpd, lldpcli, namespaces, uname):
- with namespaces(2):
- lldpd()
- with namespaces(1):
- result = lldpcli("show", "neighbors", "details")
- assert result.returncode == 0
- if 'Dot3' in pytest.config.lldpd.features:
- dot3 = """
- PMD autoneg: supported: no, enabled: no
- MAU oper type: 10GigBaseCX4 - X copper over 8 pair 100-Ohm balanced cable"""
- else:
- dot3 = ""
- expected = """-------------------------------------------------------------------------------
+@pytest.mark.parametrize("command, expected", [
+ ("neighbors",
+ """-------------------------------------------------------------------------------
LLDP neighbors:
-------------------------------------------------------------------------------
Interface: eth0, via: LLDP, RID: 1, Time: 0 day, 00:00:{seconds}
PortDescr: eth1
TTL: 120{dot3}
-------------------------------------------------------------------------------
-"""
+"""),
+ ("interfaces",
+ """-------------------------------------------------------------------------------
+LLDP interfaces:
+-------------------------------------------------------------------------------
+Interface: eth0, via: unknown, Time: {time}
+ Chassis:
+ ChassisID: mac 00:00:00:00:00:01
+ SysName: ns-1.example.com
+ SysDescr: Spectacular GNU/Linux 2016 {uname}
+ MgmtIP: fe80::200:ff:fe00:1
+ Capability: Bridge, off
+ Capability: Router, {router}
+ Capability: Wlan, off
+ Capability: Station, {station}
+ Port:
+ PortID: mac 00:00:00:00:00:01
+ PortDescr: eth0{dot3}
+ TTL: 120
+-------------------------------------------------------------------------------
+""")], ids=["neighbors", "interfaces"])
+def test_text_output(lldpd1, lldpd, lldpcli, namespaces, uname, command,
+ expected):
+ with namespaces(2):
+ lldpd()
+ with namespaces(1):
+ result = lldpcli(
+ *shlex.split("show {} details".format(command)))
+ assert result.returncode == 0
out = result.stdout.decode('ascii')
+
+ if 'Dot3' in pytest.config.lldpd.features:
+ dot3 = """
+ PMD autoneg: supported: no, enabled: no
+ MAU oper type: 10GigBaseCX4 - X copper over 8 pair 100-Ohm balanced cable"""
+ else:
+ dot3 = ""
+
+ out = result.stdout.decode('ascii')
+ time = re.search(r'^Interface: .*Time: (.*)$',
+ out,
+ re.MULTILINE).group(1)
seconds = re.search(r'^Interface: .*(\d\d)$',
out,
re.MULTILINE).group(1)
re.MULTILINE).group(1)
out = re.sub(r' *$', '', out, flags=re.MULTILINE)
assert out == expected.format(seconds=seconds,
+ time=time,
router=router,
station=station,
uname=uname,
dot3=dot3)
-
@pytest.mark.skipif('JSON' not in pytest.config.lldpcli.outputs,
reason="JSON not supported")
-def test_json_output(lldpd1, lldpd, lldpcli, namespaces, uname):
+@pytest.mark.parametrize("command, expected", [
+ ("neighbors",
+ {"lldp": {
+ "interface": {
+ "eth0": {
+ "via": "LLDP",
+ "rid": "1",
+ "chassis": {
+ "ns-2.example.com": {
+ "id": {
+ "type": "mac",
+ "value": "00:00:00:00:00:02"},
+ "descr": "Spectacular GNU/Linux 2016 {}".format(uname),
+ "mgmt-ip": "fe80::200:ff:fe00:2",
+ "capability": [
+ {"type": "Bridge", "enabled": False},
+ {"type": "Wlan", "enabled": False},]}},
+ "port": {
+ "id": {
+ "type": "mac",
+ "value": "00:00:00:00:00:02"},
+ "descr": "eth1",
+ "ttl": "120"}}}}}),
+ ("interfaces",
+ {"lldp": {
+ "interface": {
+ "eth0": {
+ "via": "unknown",
+ "chassis": {
+ "ns-1.example.com": {
+ "id": {
+ "type": "mac",
+ "value": "00:00:00:00:00:01"},
+ "descr": "Spectacular GNU/Linux 2016 {}".format(uname),
+ "mgmt-ip": "fe80::200:ff:fe00:1",
+ "capability": [
+ {"type": "Bridge", "enabled": False},
+ {"type": "Wlan", "enabled": False},]}},
+ "port": {
+ "id": {
+ "type": "mac",
+ "value": "00:00:00:00:00:01"},
+ "descr": "eth0"},
+ "ttl": {
+ "ttl": "120"}}}}})], ids=["neighbors", "interfaces"])
+def test_json_output(lldpd1, lldpd, lldpcli, namespaces, uname, command,
+ expected):
with namespaces(2):
lldpd()
with namespaces(1):
- result = lldpcli("-f", "json", "show", "neighbors", "details")
+ result = lldpcli(
+ *shlex.split("-f json show {} details".format(command)))
assert result.returncode == 0
out = result.stdout.decode('ascii')
j = json.loads(out)
eth0 = j['lldp']['interface']['eth0']
+ name = next(k for k,v in eth0['chassis'].items() if k.startswith('ns'))
del eth0['age']
- del eth0['chassis']['ns-2.example.com']['capability'][3]
- del eth0['chassis']['ns-2.example.com']['capability'][1]
- expected = {"lldp": {
- "interface": {"eth0": {
- "via": "LLDP",
- "rid": "1",
- "chassis": {
- "ns-2.example.com": {
- "id": {
- "type": "mac",
- "value": "00:00:00:00:00:02"
- },
- "descr": "Spectacular GNU/Linux 2016 {}".format(uname),
- "mgmt-ip": "fe80::200:ff:fe00:2",
- "capability": [
- {"type": "Bridge", "enabled": False},
- {"type": "Wlan", "enabled": False},
- ]
- }
- },
- "port": {
- "id": {
- "type": "mac",
- "value": "00:00:00:00:00:02"
- },
- "descr": "eth1",
- "ttl": "120"
- }
- }}
- }}
+ del eth0['chassis'][name]['capability'][3]
+ del eth0['chassis'][name]['capability'][1]
+
+ descr = "Spectacular GNU/Linux 2016 {}".format(uname)
+ expected['lldp']['interface']['eth0']['chassis'][name]["descr"] = descr
if 'Dot3' in pytest.config.lldpd.features:
expected['lldp']['interface']['eth0']['port']['auto-negotiation'] = {
"supported": False,
"current": "10GigBaseCX4 - X copper over 8 pair 100-Ohm balanced cable"
}
- assert j == expected
+ assert j == expected
@pytest.mark.skipif('JSON' not in pytest.config.lldpcli.outputs,
reason="JSON not supported")
-def test_json0_output(lldpd1, lldpd, lldpcli, namespaces, uname):
- with namespaces(2):
- lldpd()
- with namespaces(1):
- result = lldpcli("-f", "json0", "show", "neighbors", "details")
- assert result.returncode == 0
- out = result.stdout.decode('ascii')
- j = json.loads(out)
-
- eth0 = j['lldp'][0]['interface'][0]
- del eth0['age']
- del eth0['chassis'][0]['capability'][3]
- del eth0['chassis'][0]['capability'][1]
- expected = {"lldp": [{
+@pytest.mark.parametrize("command, expected", [
+ ("neighbors",
+ {"lldp": [{
"interface": [{
"name": "eth0",
"via": "LLDP",
"ttl": [{"value": "120"}]
}]
}]}
- ]}
+ ]}),
+ ("interfaces",
+ {"lldp": [{
+ "interface": [{
+ "name": "eth0",
+ "via": "unknown",
+ "chassis": [{
+ "id": [{
+ "type": "mac",
+ "value": "00:00:00:00:00:01"
+ }],
+ "name": [{"value": "ns-1.example.com"}],
+ "descr": [{"value": "Spectacular GNU/Linux 2016 {}".format(uname)}],
+ "mgmt-ip": [{"value": "fe80::200:ff:fe00:1"}],
+ "capability": [
+ {"type": "Bridge", "enabled": False},
+ {"type": "Wlan", "enabled": False},
+ ]}
+ ],
+ "port": [{
+ "id": [{
+ "type": "mac",
+ "value": "00:00:00:00:00:01"
+ }],
+ "descr": [{"value": "eth0"}]
+ }],
+ "ttl": [{"ttl": "120"}]
+ }]}
+ ]})], ids=["neighbors", "interfaces"])
+def test_json0_output(lldpd1, lldpd, lldpcli, namespaces, uname, command,
+ expected):
+ with namespaces(2):
+ lldpd()
+ with namespaces(1):
+ result = lldpcli(
+ *shlex.split("-f json0 show {} details".format(command)))
+ assert result.returncode == 0
+ out = result.stdout.decode('ascii')
+ j = json.loads(out)
+
+ eth0 = j['lldp'][0]['interface'][0]
+ del eth0['age']
+ del eth0['chassis'][0]['capability'][3]
+ del eth0['chassis'][0]['capability'][1]
+
+ descr = "Spectacular GNU/Linux 2016 {}".format(uname)
+ expected['lldp'][0]['interface'][0]['chassis'][0]["descr"][0]['value'] = descr
if 'Dot3' in pytest.config.lldpd.features:
expected['lldp'][0]['interface'][0]['port'][0]['auto-negotiation'] = [{
@pytest.mark.skipif('XML' not in pytest.config.lldpcli.outputs,
reason="XML not supported")
-def test_xml_output(lldpd1, lldpd, lldpcli, namespaces, uname):
- with namespaces(2):
- lldpd()
- with namespaces(1):
- result = lldpcli("-f", "xml", "show", "neighbors", "details")
- assert result.returncode == 0
- out = result.stdout.decode('ascii')
- xml = ET.fromstring(out)
-
- age = xml.findall('./interface[1]')[0].attrib['age']
- router = xml.findall("./interface[1]/chassis/"
- "capability[@type='Router']")[0].attrib['enabled']
- station = xml.findall("./interface[1]/chassis/"
- "capability[@type='Station']")[0].attrib['enabled']
- if 'Dot3' in pytest.config.lldpd.features:
- dot3 = """
- <auto-negotiation enabled="no" label="PMD autoneg" supported="no">
- <current label="MAU oper type">10GigBaseCX4 - X copper over 8 pair 100-Ohm balanced cable</current>
- </auto-negotiation>"""
- else:
- dot3 = ""
- expected = ET.fromstring("""<?xml version="1.0" encoding="UTF-8"?>
+@pytest.mark.parametrize("command, expected", [
+ ("neighbors",
+"""<?xml version="1.0" encoding="UTF-8"?>
<lldp label="LLDP neighbors">
<interface label="Interface" name="eth0" via="LLDP" rid="1" age="{age}">
<chassis label="Chassis">
</port>
</interface>
</lldp>
- """.format(age=age,
- router=router,
- station=station,
- uname=uname,
- dot3=dot3))
+"""),
+("interfaces",
+"""<?xml version="1.0" encoding="UTF-8"?>
+<lldp label="LLDP interfaces">
+ <interface label="Interface" name="eth0" via="unknown" age="{age}">
+ <chassis label="Chassis">
+ <id label="ChassisID" type="mac">00:00:00:00:00:01</id>
+ <name label="SysName">ns-1.example.com</name>
+ <descr label="SysDescr">Spectacular GNU/Linux 2016 {uname}</descr>
+ <mgmt-ip label="MgmtIP">fe80::200:ff:fe00:1</mgmt-ip>
+ <capability label="Capability" type="Bridge" enabled="off"/>
+ <capability label="Capability" type="Router" enabled="{router}"/>
+ <capability label="Capability" type="Wlan" enabled="off"/>
+ <capability label="Capability" type="Station" enabled="{station}"/>
+ </chassis>
+ <port label="Port">
+ <id label="PortID" type="mac">00:00:00:00:00:01</id>
+ <descr label="PortDescr">eth0</descr>{dot3}
+ </port>
+ <ttl label="TTL" ttl="120"/>
+ </interface>
+</lldp>
+""")], ids=["neighbors", "interfaces"])
+def test_xml_output(lldpd1, lldpd, lldpcli, namespaces, uname, command,
+ expected):
+ with namespaces(2):
+ lldpd()
+ with namespaces(1):
+ result = lldpcli(
+ *shlex.split("-f xml show {} details".format(command)))
+ assert result.returncode == 0
+ out = result.stdout.decode('ascii')
+ xml = ET.fromstring(out)
+
+ age = xml.findall('./interface[1]')[0].attrib['age']
+ router = xml.findall("./interface[1]/chassis/"
+ "capability[@type='Router']")[0].attrib['enabled']
+ station = xml.findall("./interface[1]/chassis/"
+ "capability[@type='Station']")[0].attrib['enabled']
+ if 'Dot3' in pytest.config.lldpd.features:
+ dot3 = """
+ <auto-negotiation enabled="no" label="PMD autoneg" supported="no">
+ <current label="MAU oper type">10GigBaseCX4 - X copper over 8 pair 100-Ohm balanced cable</current>
+ </auto-negotiation>"""
+ else:
+ dot3 = ""
+ expected = ET.fromstring(expected.format(age=age,
+ router=router,
+ station=station,
+ uname=uname,
+ dot3=dot3))
assert ET.tostring(xml) == ET.tostring(expected)
"spare class class-3").split())
assert result.returncode == 0
time.sleep(3)
+ out = lldpcli("-f", "keyvalue", "show", "interfaces", "details")
+ assert 'lldp.eth1.port.power.device-type' not in out
+ assert out['lldp.eth3.port.power.device-type'] == 'PSE'
with namespaces(1):
out = lldpcli("-f", "keyvalue", "show", "neighbors", "details")
assert out['lldp.eth0.port.descr'] == 'eth1'
"spare class class-3").split())
assert result.returncode == 0
time.sleep(3)
+ out = lldpcli("-f", "keyvalue", "show", "interfaces", "details")
+ assert out['lldp.eth1.port.power.device-type'] == 'PSE'
with namespaces(1):
# Check this worked
out = lldpcli("-f", "keyvalue", "show", "neighbors", "details")
out = lldpcli("-f", "keyvalue", "show", "neighbors", "details")
assert out['lldp.eth2.port.descr'] == 'eth3'
assert out['lldp.eth2.port.power.device-type'] == 'PSE'
+ with namespaces(2):
+ out = lldpcli("-f", "keyvalue", "show", "interfaces", "details")
+ assert out['lldp.eth3.port.power.device-type'] == 'PSE'
@pytest.mark.skipif('Dot3' not in pytest.config.lldpd.features,
time.sleep(4)
links.up('eth3')
time.sleep(4)
+ out = lldpcli("-f", "keyvalue", "show", "interfaces", "details")
+ assert out['lldp.eth3.port.power.device-type'] == 'PSE'
with namespaces(1):
out = lldpcli("-f", "keyvalue", "show", "neighbors", "details")
assert out['lldp.eth2.port.descr'] == 'eth3'
with namespaces(1):
result = lldpcli("show", "neighbors")
assert result.returncode == 0
+ result = lldpcli("show", "interfaces")
+ assert result.returncode == 0
result = lldpcli("unknown", "command")
assert result.returncode == 1