blob: 70bc980e954003774d44ffd6137718e40ea6026d [file] [log] [blame]
Tomáš Pecka7acf3922021-08-10 11:16:57 +02001/*
2 * Copyright (C) 2020 CESNET, https://photonics.cesnet.cz/
3 *
4 * Written by Tomáš Pecka <tomas.pecka@fit.cvut.cz>
5 *
6 */
7#include <netinet/ether.h>
Tomáš Pecka070f60d2021-10-13 21:48:14 +02008#include <nlohmann/json.hpp>
Tomáš Pecka7acf3922021-08-10 11:16:57 +02009#include <spdlog/spdlog.h>
10#include "LLDP.h"
Tomáš Pecka070f60d2021-10-13 21:48:14 +020011#include "system_vars.h"
12#include "utils/exec.h"
Tomáš Pecka7acf3922021-08-10 11:16:57 +020013#include "utils/log.h"
14
15namespace velia::system {
16
Tomáš Pecka36427a12021-08-10 11:27:56 +020017namespace {
Tomáš Pecka7acf3922021-08-10 11:16:57 +020018
Tomáš Pecka7acf3922021-08-10 11:16:57 +020019/** @brief LLDP capabilities identifiers ordered by their appearence in YANG schema 'czechlight-lldp' */
Tomáš Pecka070f60d2021-10-13 21:48:14 +020020std::map<char, std::string> SYSTEM_CAPABILITIES = {
21 {'o', "other"},
22 {'p', "repeater"},
23 {'b', "bridge"},
24 {'w', "wlan-access-point"},
25 {'r', "router"},
26 {'t', "telephone"},
27 {'d', "docsis-cable-device"},
28 {'a', "station-only"},
29 {'c', "cvlan-component"},
30 {'s', "svlan-component"},
31 {'m', "two-port-mac-relay"},
Tomáš Pecka7acf3922021-08-10 11:16:57 +020032};
33
34/** @brief Converts systemd's capabilities bitset to YANG's (named) bits.
35 *
36 * Apparently, libyang's parser requires the bits to be specified as string of names separated by whitespace.
37 * See libyang's src/parser.c (function lyp_parse_value, switch-case LY_TYPE_BITS) and tests/test_sec9_7.c
38 *
39 * The names of individual bits should appear in the order they are defined in the YANG schema. At least that is how
40 * I understand libyang's comment 'identifiers appear ordered by their position' in src/parser.c.
41 * Systemd and our YANG model czechlight-lldp define the bits in the same order so this function does not have to care
42 * about it.
43 */
Tomáš Pecka070f60d2021-10-13 21:48:14 +020044std::string toBitsYANG(const std::string& caps)
Tomáš Pecka7acf3922021-08-10 11:16:57 +020045{
46 std::string res;
47
Tomáš Pecka070f60d2021-10-13 21:48:14 +020048 for (const auto& [bit, capability] : SYSTEM_CAPABILITIES) {
49 if (std::find(caps.begin(), caps.end(), bit) != caps.end()) {
Tomáš Pecka7acf3922021-08-10 11:16:57 +020050 if (!res.empty()) {
51 res += " ";
52 }
Tomáš Pecka7acf3922021-08-10 11:16:57 +020053
Tomáš Pecka070f60d2021-10-13 21:48:14 +020054 res += capability;
55 }
Tomáš Pecka7acf3922021-08-10 11:16:57 +020056 }
57
58 return res;
59}
Tomáš Pecka7acf3922021-08-10 11:16:57 +020060}
61
Tomáš Pecka070f60d2021-10-13 21:48:14 +020062LLDPDataProvider::LLDPDataProvider(std::function<std::string()> dataCallback)
Tomáš Pecka7acf3922021-08-10 11:16:57 +020063 : m_log(spdlog::get("system"))
Tomáš Pecka070f60d2021-10-13 21:48:14 +020064 , m_dataCallback(std::move(dataCallback))
Tomáš Pecka7acf3922021-08-10 11:16:57 +020065{
66}
67
68std::vector<NeighborEntry> LLDPDataProvider::getNeighbors() const
69{
70 std::vector<NeighborEntry> res;
71
Tomáš Pecka070f60d2021-10-13 21:48:14 +020072 auto json = nlohmann::json::parse(m_dataCallback());
Tomáš Pecka7acf3922021-08-10 11:16:57 +020073
Tomáš Pecka070f60d2021-10-13 21:48:14 +020074 for (const auto& [linkName, neighbors] : json.items()) {
75 for (const auto& n_ : neighbors) {
76 [[maybe_unused]] const auto& parameters = n_["neighbor"];
Tomáš Pecka7acf3922021-08-10 11:16:57 +020077 NeighborEntry ne;
78 ne.m_portId = linkName;
79
Tomáš Pecka070f60d2021-10-13 21:48:14 +020080 if (auto it = parameters.find("chassisId"); it != parameters.end()) {
81 ne.m_properties["remoteChassisId"] = *it;
Tomáš Pecka7acf3922021-08-10 11:16:57 +020082 }
Tomáš Pecka070f60d2021-10-13 21:48:14 +020083 if (auto it = parameters.find("portId"); it != parameters.end()) {
84 ne.m_properties["remotePortId"] = *it;
Tomáš Pecka7acf3922021-08-10 11:16:57 +020085 }
Tomáš Pecka070f60d2021-10-13 21:48:14 +020086 if (auto it = parameters.find("systemName"); it != parameters.end()) {
87 ne.m_properties["remoteSysName"] = *it;
Tomáš Pecka7acf3922021-08-10 11:16:57 +020088 }
Tomáš Pecka070f60d2021-10-13 21:48:14 +020089 if (auto it = parameters.find("enabledCapabilities"); it != parameters.end()) {
90 ne.m_properties["systemCapabilitiesEnabled"] = toBitsYANG(*it);
Tomáš Pecka7acf3922021-08-10 11:16:57 +020091 }
92
Tomáš Pecka070f60d2021-10-13 21:48:14 +020093 m_log->trace("Found LLDP neighbor {}", ne);
Tomáš Pecka7acf3922021-08-10 11:16:57 +020094 res.push_back(ne);
95 }
96 }
97
98 return res;
99}
100
101std::ostream& operator<<(std::ostream& os, const NeighborEntry& entry)
102{
103 os << "NeighborEntry(" << entry.m_portId << ": {";
104
105 for (auto it = entry.m_properties.begin(); it != entry.m_properties.end(); ++it) {
106 if (it != entry.m_properties.begin()) {
107 os << ", ";
108 }
109
110 os << it->first << ": " << it->second;
111 }
112
Tomáš Pecka070f60d2021-10-13 21:48:14 +0200113 return os << "})";
Tomáš Pecka7acf3922021-08-10 11:16:57 +0200114}
115
116}