blob: 481c2b59eba5ee4c2edfe32872b9fe44a5a59dfd [file] [log] [blame]
Tomáš Peckaba2dc312021-01-23 22:29:11 +01001/*
2 * Copyright (C) 2016-2021 CESNET, https://photonics.cesnet.cz/
3 *
4 * Written by Jan Kundrát <jan.kundrat@cesnet.cz>
5 * Written by Tomáš Pecka <tomas.pecka@cesnet.cz>
6 *
7 */
8
9#include "sysrepo.h"
10#include "utils/log.h"
11
12extern "C" {
13#include <sysrepo.h>
14}
15
16extern "C" {
17/** @short Propagate sysrepo events to spdlog */
18static void spdlog_sr_log_cb(sr_log_level_t level, const char* message)
19{
20 // Thread safety note: this is, as far as I know, thread safe:
21 // - the static initialization itself is OK
22 // - all loggers which we instantiate are thread-safe
23 // - std::shared_ptr::operator-> is const, and all const members of that class are documented to be thread-safe
24 static auto log = spdlog::get("sysrepo");
25 assert(log);
26 switch (level) {
27 case SR_LL_NONE:
28 case SR_LL_ERR:
29 log->error(message);
30 break;
31 case SR_LL_WRN:
32 log->warn(message);
33 break;
34 case SR_LL_INF:
35 log->info(message);
36 break;
37 case SR_LL_DBG:
38 log->debug(message);
39 break;
40 }
41}
42}
43
Tomáš Pecka53f08ee2021-04-28 12:38:11 +020044namespace {
45std::vector<velia::utils::YANGPair> mapToVector(const std::map<std::string, std::string>& values)
46{
47 std::vector<velia::utils::YANGPair> res;
48 for (const auto& [xpath, value] : values) {
49 res.emplace_back(xpath, value);
50 }
51
52 return res;
53}
54
55}
56
Tomáš Peckaba2dc312021-01-23 22:29:11 +010057namespace velia::utils {
58
59/** @short Setup sysrepo log forwarding
60You must call cla::utils::initLogs prior to this function.
61*/
62void initLogsSysrepo()
63{
64 sr_log_set_cb(spdlog_sr_log_cb);
65}
66
Tomáš Pecka498e91c2021-03-02 17:46:47 +010067void valuesToYang(const std::map<std::string, std::string>& values, const std::vector<std::string>& removePaths, std::shared_ptr<::sysrepo::Session> session, std::shared_ptr<libyang::Data_Node>& parent)
Tomáš Peckaba2dc312021-01-23 22:29:11 +010068{
Tomáš Pecka53f08ee2021-04-28 12:38:11 +020069 valuesToYang(mapToVector(values), removePaths, std::move(session), parent);
70}
71
72void valuesToYang(const std::vector<YANGPair>& values, const std::vector<std::string>& removePaths, std::shared_ptr<::sysrepo::Session> session, std::shared_ptr<libyang::Data_Node>& parent)
73{
Tomáš Pecka498e91c2021-03-02 17:46:47 +010074 auto netconf = session->get_context()->get_module("ietf-netconf");
Tomáš Pecka5b293f42021-03-02 17:47:03 +010075 auto log = spdlog::get("main");
Tomáš Pecka498e91c2021-03-02 17:46:47 +010076
77 for (const auto& propertyName : removePaths) {
Tomáš Pecka5b293f42021-03-02 17:47:03 +010078 log->trace("Processing node deletion {}", propertyName);
79
Tomáš Pecka498e91c2021-03-02 17:46:47 +010080 if (!parent) {
81 parent = std::make_shared<libyang::Data_Node>(
82 session->get_context(),
83 propertyName.c_str(),
84 nullptr,
85 LYD_ANYDATA_CONSTSTRING,
86 LYD_PATH_OPT_EDIT);
87 } else {
88 parent->new_path(
89 session->get_context(),
90 propertyName.c_str(),
91 nullptr,
92 LYD_ANYDATA_CONSTSTRING,
93 LYD_PATH_OPT_EDIT);
94 }
95
96 auto deletion = parent->find_path(propertyName.c_str());
97 if (deletion->number() != 1) {
98 throw std::logic_error {"Cannot find XPath " + propertyName + " for deletion in libyang's new_path() output"};
99 }
100 deletion->data()[0]->insert_attr(netconf, "operation", "remove");
101 }
102
Tomáš Peckaba2dc312021-01-23 22:29:11 +0100103 for (const auto& [propertyName, value] : values) {
Tomáš Pecka5b293f42021-03-02 17:47:03 +0100104 log->trace("Processing node update {} -> {}", propertyName, value);
105
Tomáš Peckaba2dc312021-01-23 22:29:11 +0100106 if (!parent) {
107 parent = std::make_shared<libyang::Data_Node>(
108 session->get_context(),
109 propertyName.c_str(),
110 value.c_str(),
111 LYD_ANYDATA_CONSTSTRING,
112 LYD_PATH_OPT_OUTPUT);
113 } else {
114 parent->new_path(
115 session->get_context(),
116 propertyName.c_str(),
117 value.c_str(),
118 LYD_ANYDATA_CONSTSTRING,
119 LYD_PATH_OPT_OUTPUT);
120 }
121 }
122}
123
Tomáš Pecka498e91c2021-03-02 17:46:47 +0100124/** @brief Set or remove values in Sysrepo's specified datastore. It changes the datastore and after the data are applied, the original datastore is restored. */
125void valuesPush(const std::map<std::string, std::string>& values, const std::vector<std::string>& removePaths, std::shared_ptr<::sysrepo::Session> session, sr_datastore_t datastore)
Tomáš Pecka272abaf2021-01-24 12:28:43 +0100126{
127 sr_datastore_t oldDatastore = session->session_get_ds();
128 session->session_switch_ds(datastore);
129
Tomáš Pecka498e91c2021-03-02 17:46:47 +0100130 valuesPush(values, removePaths, session);
Tomáš Pecka272abaf2021-01-24 12:28:43 +0100131
Tomáš Pecka272abaf2021-01-24 12:28:43 +0100132 session->session_switch_ds(oldDatastore);
133}
134
Tomáš Pecka498e91c2021-03-02 17:46:47 +0100135/** @brief Set or remove paths in Sysrepo's current datastore. */
136void valuesPush(const std::map<std::string, std::string>& values, const std::vector<std::string>& removePaths, std::shared_ptr<::sysrepo::Session> session)
Tomáš Pecka272abaf2021-01-24 12:28:43 +0100137{
Tomáš Peckadaedba52021-03-08 12:52:20 +0100138 if (values.empty() && removePaths.empty()) return;
139
Tomáš Pecka272abaf2021-01-24 12:28:43 +0100140 libyang::S_Data_Node edit;
Tomáš Pecka498e91c2021-03-02 17:46:47 +0100141 valuesToYang(values, removePaths, session, edit);
Tomáš Pecka272abaf2021-01-24 12:28:43 +0100142
143 session->edit_batch(edit, "merge");
144 session->apply_changes();
145}
146
Tomáš Pecka53f08ee2021-04-28 12:38:11 +0200147/** @brief Set or remove values in Sysrepo's specified datastore. It changes the datastore and after the data are applied, the original datastore is restored. */
148void valuesPush(const std::vector<YANGPair>& values, const std::vector<std::string>& removePaths, std::shared_ptr<::sysrepo::Session> session, sr_datastore_t datastore)
149{
150 sr_datastore_t oldDatastore = session->session_get_ds();
151 session->session_switch_ds(datastore);
152
153 valuesPush(values, removePaths, session);
154
155 session->session_switch_ds(oldDatastore);
156}
157
158/** @brief Set or remove paths in Sysrepo's current datastore. */
159void valuesPush(const std::vector<YANGPair>& values, const std::vector<std::string>& removePaths, std::shared_ptr<::sysrepo::Session> session)
160{
161 if (values.empty() && removePaths.empty())
162 return;
163
164 libyang::S_Data_Node edit;
165 valuesToYang(values, removePaths, session, edit);
166
167 session->edit_batch(edit, "merge");
168 session->apply_changes();
169}
170
Václav Kubernát6fa7f342021-01-26 17:00:01 +0100171/** @brief Checks whether a module is implemented in Sysrepo and throws if not. */
172void ensureModuleImplemented(std::shared_ptr<::sysrepo::Session> session, const std::string& module, const std::string& revision)
173{
174 if (auto mod = session->get_context()->get_module(module.c_str(), revision.c_str()); !mod || !mod->implemented()) {
175 throw std::runtime_error(module + "@" + revision + " is not implemented in sysrepo.");
176 }
177}
Tomáš Pecka53f08ee2021-04-28 12:38:11 +0200178YANGPair::YANGPair(std::string xpath, std::string value)
179 : m_xpath(std::move(xpath))
180 , m_value(std::move(value))
181{
182}
Tomáš Peckaba2dc312021-01-23 22:29:11 +0100183}