| /* |
| * Copyright (C) 2020-2023 CESNET, https://photonics.cesnet.cz/ |
| * |
| * Written by Tomáš Pecka <tomas.pecka@fit.cvut.cz> |
| * |
| */ |
| |
| #include <regex> |
| #include <sysrepo-cpp/Connection.hpp> |
| #include "Sysrepo.h" |
| #include "utils/alarms.h" |
| #include "utils/log.h" |
| #include "utils/sysrepo.h" |
| |
| namespace { |
| |
| const auto ALARM_SENSOR_MISSING = "velia-alarms:sensor-missing-alarm"; |
| const auto ALARM_THRESHOLD_CROSSING_LOW = "velia-alarms:sensor-low-value-alarm"; |
| const auto ALARM_THRESHOLD_CROSSING_HIGH = "velia-alarms:sensor-high-value-alarm"; |
| |
| /** @brief Extracts component path prefix from an XPath under /ietf-hardware/component node |
| * |
| * Example input: /ietf-hardware:hardware/component[name='ne:psu:child']/oper-state/disabled |
| * Example output: /ietf-hardware:hardware/component[name='ne:psu:child'] |
| */ |
| std::string extractComponentPrefix(const std::string& componentXPath) |
| { |
| static const std::regex regex(R"((/ietf-hardware:hardware/component\[name=('|").*?(\2)\]).*)"); |
| std::smatch match; |
| |
| if (std::regex_match(componentXPath, match, regex)) { |
| return match.str(1); |
| } |
| |
| throw std::logic_error("Invalid xPath provided ('" + componentXPath + "')"); |
| } |
| } |
| |
| namespace velia::ietf_hardware::sysrepo { |
| |
| /** @brief The constructor expects the HardwareState instance which will provide the actual hardware state data and the poll interval */ |
| Sysrepo::Sysrepo(::sysrepo::Session session, std::shared_ptr<IETFHardware> hwState, std::chrono::microseconds pollInterval) |
| : m_log(spdlog::get("hardware")) |
| , m_pollInterval(std::move(pollInterval)) |
| , m_session(std::move(session)) |
| , m_hwState(std::move(hwState)) |
| , m_quit(false) |
| { |
| for (const auto& sensorXPath : m_hwState->sensorsXPaths()) { |
| auto componentXPath = extractComponentPrefix(sensorXPath); |
| utils::addResourceToAlarmInventoryEntry(m_session, ALARM_THRESHOLD_CROSSING_LOW, std::nullopt, componentXPath); |
| utils::addResourceToAlarmInventoryEntry(m_session, ALARM_THRESHOLD_CROSSING_HIGH, std::nullopt, componentXPath); |
| utils::addResourceToAlarmInventoryEntry(m_session, ALARM_SENSOR_MISSING, std::nullopt, componentXPath); |
| } |
| |
| m_pollThread = std::thread([&]() { |
| auto conn = m_session.getConnection(); |
| |
| DataTree prevValues; |
| |
| while (!m_quit) { |
| m_log->trace("IetfHardware poll"); |
| |
| auto [hwStateValues, thresholds] = m_hwState->process(); |
| std::set<std::string> deletedComponents; |
| |
| /* Some data readers can stop returning data in some cases (e.g. ejected PSU). |
| * Prune tree components that were removed before updating to avoid having not current data from previous invocations. |
| */ |
| for (const auto& [k, v] : prevValues) { |
| if (!hwStateValues.contains(k)) { |
| deletedComponents.emplace(extractComponentPrefix(k)); |
| } |
| } |
| |
| std::vector<std::string> discards; |
| discards.reserve(deletedComponents.size()); |
| std::copy(deletedComponents.begin(), deletedComponents.end(), std::back_inserter(discards)); |
| |
| utils::valuesPush(hwStateValues, {}, discards, m_session, ::sysrepo::Datastore::Operational); |
| |
| prevValues = std::move(hwStateValues); |
| std::this_thread::sleep_for(m_pollInterval); |
| } |
| }); |
| } |
| |
| Sysrepo::~Sysrepo() |
| { |
| m_log->trace("Requesting poll thread stop"); |
| m_quit = true; |
| m_pollThread.join(); |
| } |
| } |