Tomáš Pecka | 7acf392 | 2021-08-10 11:16:57 +0200 | [diff] [blame] | 1 | /* |
| 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áš Pecka | 070f60d | 2021-10-13 21:48:14 +0200 | [diff] [blame] | 8 | #include <nlohmann/json.hpp> |
Tomáš Pecka | 7acf392 | 2021-08-10 11:16:57 +0200 | [diff] [blame] | 9 | #include <spdlog/spdlog.h> |
| 10 | #include "LLDP.h" |
Tomáš Pecka | 070f60d | 2021-10-13 21:48:14 +0200 | [diff] [blame] | 11 | #include "system_vars.h" |
| 12 | #include "utils/exec.h" |
Tomáš Pecka | 7acf392 | 2021-08-10 11:16:57 +0200 | [diff] [blame] | 13 | #include "utils/log.h" |
| 14 | |
| 15 | namespace velia::system { |
| 16 | |
Tomáš Pecka | 36427a1 | 2021-08-10 11:27:56 +0200 | [diff] [blame] | 17 | namespace { |
Tomáš Pecka | 7acf392 | 2021-08-10 11:16:57 +0200 | [diff] [blame] | 18 | |
Tomáš Pecka | 7acf392 | 2021-08-10 11:16:57 +0200 | [diff] [blame] | 19 | /** @brief LLDP capabilities identifiers ordered by their appearence in YANG schema 'czechlight-lldp' */ |
Tomáš Pecka | 070f60d | 2021-10-13 21:48:14 +0200 | [diff] [blame] | 20 | std::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áš Pecka | 7acf392 | 2021-08-10 11:16:57 +0200 | [diff] [blame] | 32 | }; |
| 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áš Pecka | 070f60d | 2021-10-13 21:48:14 +0200 | [diff] [blame] | 44 | std::string toBitsYANG(const std::string& caps) |
Tomáš Pecka | 7acf392 | 2021-08-10 11:16:57 +0200 | [diff] [blame] | 45 | { |
| 46 | std::string res; |
| 47 | |
Tomáš Pecka | 070f60d | 2021-10-13 21:48:14 +0200 | [diff] [blame] | 48 | for (const auto& [bit, capability] : SYSTEM_CAPABILITIES) { |
| 49 | if (std::find(caps.begin(), caps.end(), bit) != caps.end()) { |
Tomáš Pecka | 7acf392 | 2021-08-10 11:16:57 +0200 | [diff] [blame] | 50 | if (!res.empty()) { |
| 51 | res += " "; |
| 52 | } |
Tomáš Pecka | 7acf392 | 2021-08-10 11:16:57 +0200 | [diff] [blame] | 53 | |
Tomáš Pecka | 070f60d | 2021-10-13 21:48:14 +0200 | [diff] [blame] | 54 | res += capability; |
| 55 | } |
Tomáš Pecka | 7acf392 | 2021-08-10 11:16:57 +0200 | [diff] [blame] | 56 | } |
| 57 | |
| 58 | return res; |
| 59 | } |
Tomáš Pecka | 7acf392 | 2021-08-10 11:16:57 +0200 | [diff] [blame] | 60 | } |
| 61 | |
Tomáš Pecka | 070f60d | 2021-10-13 21:48:14 +0200 | [diff] [blame] | 62 | LLDPDataProvider::LLDPDataProvider(std::function<std::string()> dataCallback) |
Tomáš Pecka | 7acf392 | 2021-08-10 11:16:57 +0200 | [diff] [blame] | 63 | : m_log(spdlog::get("system")) |
Tomáš Pecka | 070f60d | 2021-10-13 21:48:14 +0200 | [diff] [blame] | 64 | , m_dataCallback(std::move(dataCallback)) |
Tomáš Pecka | 7acf392 | 2021-08-10 11:16:57 +0200 | [diff] [blame] | 65 | { |
| 66 | } |
| 67 | |
| 68 | std::vector<NeighborEntry> LLDPDataProvider::getNeighbors() const |
| 69 | { |
| 70 | std::vector<NeighborEntry> res; |
| 71 | |
Tomáš Pecka | 070f60d | 2021-10-13 21:48:14 +0200 | [diff] [blame] | 72 | auto json = nlohmann::json::parse(m_dataCallback()); |
Tomáš Pecka | 7acf392 | 2021-08-10 11:16:57 +0200 | [diff] [blame] | 73 | |
Tomáš Pecka | 070f60d | 2021-10-13 21:48:14 +0200 | [diff] [blame] | 74 | for (const auto& [linkName, neighbors] : json.items()) { |
| 75 | for (const auto& n_ : neighbors) { |
| 76 | [[maybe_unused]] const auto& parameters = n_["neighbor"]; |
Tomáš Pecka | 7acf392 | 2021-08-10 11:16:57 +0200 | [diff] [blame] | 77 | NeighborEntry ne; |
| 78 | ne.m_portId = linkName; |
| 79 | |
Tomáš Pecka | 070f60d | 2021-10-13 21:48:14 +0200 | [diff] [blame] | 80 | if (auto it = parameters.find("chassisId"); it != parameters.end()) { |
| 81 | ne.m_properties["remoteChassisId"] = *it; |
Tomáš Pecka | 7acf392 | 2021-08-10 11:16:57 +0200 | [diff] [blame] | 82 | } |
Tomáš Pecka | 070f60d | 2021-10-13 21:48:14 +0200 | [diff] [blame] | 83 | if (auto it = parameters.find("portId"); it != parameters.end()) { |
| 84 | ne.m_properties["remotePortId"] = *it; |
Tomáš Pecka | 7acf392 | 2021-08-10 11:16:57 +0200 | [diff] [blame] | 85 | } |
Tomáš Pecka | 070f60d | 2021-10-13 21:48:14 +0200 | [diff] [blame] | 86 | if (auto it = parameters.find("systemName"); it != parameters.end()) { |
| 87 | ne.m_properties["remoteSysName"] = *it; |
Tomáš Pecka | 7acf392 | 2021-08-10 11:16:57 +0200 | [diff] [blame] | 88 | } |
Tomáš Pecka | 070f60d | 2021-10-13 21:48:14 +0200 | [diff] [blame] | 89 | if (auto it = parameters.find("enabledCapabilities"); it != parameters.end()) { |
| 90 | ne.m_properties["systemCapabilitiesEnabled"] = toBitsYANG(*it); |
Tomáš Pecka | 7acf392 | 2021-08-10 11:16:57 +0200 | [diff] [blame] | 91 | } |
| 92 | |
Tomáš Pecka | 070f60d | 2021-10-13 21:48:14 +0200 | [diff] [blame] | 93 | m_log->trace("Found LLDP neighbor {}", ne); |
Tomáš Pecka | 7acf392 | 2021-08-10 11:16:57 +0200 | [diff] [blame] | 94 | res.push_back(ne); |
| 95 | } |
| 96 | } |
| 97 | |
| 98 | return res; |
| 99 | } |
| 100 | |
| 101 | std::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áš Pecka | 070f60d | 2021-10-13 21:48:14 +0200 | [diff] [blame] | 113 | return os << "})"; |
Tomáš Pecka | 7acf392 | 2021-08-10 11:16:57 +0200 | [diff] [blame] | 114 | } |
| 115 | |
| 116 | } |