blob: dcf62e99d629620ca2bdfcf51555cb19facac36a [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
Jan Kundrát498c3f82023-05-24 19:25:48 +020067void valuesToYang(const std::map<std::string, std::string>& values, const std::vector<std::string>& removePaths, const std::vector<std::string>& discardPaths, ::sysrepo::Session session, std::optional<libyang::DataNode>& parent)
Tomáš Peckaba2dc312021-01-23 22:29:11 +010068{
Jan Kundrát498c3f82023-05-24 19:25:48 +020069 valuesToYang(mapToVector(values), removePaths, discardPaths, std::move(session), parent);
Tomáš Pecka53f08ee2021-04-28 12:38:11 +020070}
71
Jan Kundrát498c3f82023-05-24 19:25:48 +020072void valuesToYang(const std::vector<YANGPair>& values, const std::vector<std::string>& removePaths, const std::vector<std::string>& discardPaths, ::sysrepo::Session session, std::optional<libyang::DataNode>& parent)
Tomáš Pecka53f08ee2021-04-28 12:38:11 +020073{
Václav Kubernát7efd6d52021-11-09 01:31:11 +010074 auto netconf = session.getContext().getModuleImplemented("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) {
Jan Kundrátb3e99982022-03-18 17:38:20 +010081 parent = session.getContext().newPath(propertyName, std::nullopt, libyang::CreationOptions::Opaque);
Tomáš Pecka498e91c2021-03-02 17:46:47 +010082 } else {
Jan Kundrátb3e99982022-03-18 17:38:20 +010083 parent->newPath(propertyName, std::nullopt, libyang::CreationOptions::Opaque);
Tomáš Pecka498e91c2021-03-02 17:46:47 +010084 }
85
Jan Kundrátb3e99982022-03-18 17:38:20 +010086 auto deletion = parent->findPath(propertyName);
Václav Kubernát7efd6d52021-11-09 01:31:11 +010087 if (!deletion) {
Tomáš Pecka498e91c2021-03-02 17:46:47 +010088 throw std::logic_error {"Cannot find XPath " + propertyName + " for deletion in libyang's new_path() output"};
89 }
Václav Kubernát7efd6d52021-11-09 01:31:11 +010090 deletion->newMeta(*netconf, "operation", "remove");
Tomáš Pecka498e91c2021-03-02 17:46:47 +010091 }
92
Tomáš Peckaba2dc312021-01-23 22:29:11 +010093 for (const auto& [propertyName, value] : values) {
Tomáš Pecka5b293f42021-03-02 17:47:03 +010094 log->trace("Processing node update {} -> {}", propertyName, value);
95
Tomáš Peckaba2dc312021-01-23 22:29:11 +010096 if (!parent) {
Jan Kundrátb3e99982022-03-18 17:38:20 +010097 parent = session.getContext().newPath(propertyName, value, libyang::CreationOptions::Output);
Tomáš Peckaba2dc312021-01-23 22:29:11 +010098 } else {
Jan Kundrátb3e99982022-03-18 17:38:20 +010099 parent->newPath(propertyName, value, libyang::CreationOptions::Output);
Tomáš Peckaba2dc312021-01-23 22:29:11 +0100100 }
101 }
Jan Kundrát498c3f82023-05-24 19:25:48 +0200102
103 for (const auto& propertyName : discardPaths) {
104 log->trace("Processing node discard {}", propertyName);
105
106 auto discard = session.getContext().newOpaqueJSON("sysrepo", "discard-items", libyang::JSON{propertyName});
107
108 if (!parent) {
109 parent = discard;
110 } else {
111 parent->insertSibling(*discard);
112 parent->newPath(propertyName, std::nullopt, libyang::CreationOptions::Opaque);
113 }
114 }
Tomáš Peckaba2dc312021-01-23 22:29:11 +0100115}
116
Tomáš Pecka498e91c2021-03-02 17:46:47 +0100117/** @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. */
Jan Kundrát498c3f82023-05-24 19:25:48 +0200118void valuesPush(const std::map<std::string, std::string>& values, const std::vector<std::string>& removePaths, const std::vector<std::string>& discardPaths, ::sysrepo::Session session, sysrepo::Datastore datastore)
Tomáš Pecka272abaf2021-01-24 12:28:43 +0100119{
Tomáš Pecka190c7242024-01-23 15:50:51 +0100120 ScopedDatastoreSwitch s(session, datastore);
Jan Kundrát498c3f82023-05-24 19:25:48 +0200121 valuesPush(values, removePaths, discardPaths, session);
Tomáš Pecka272abaf2021-01-24 12:28:43 +0100122}
123
Tomáš Pecka498e91c2021-03-02 17:46:47 +0100124/** @brief Set or remove paths in Sysrepo's current datastore. */
Jan Kundrát498c3f82023-05-24 19:25:48 +0200125void valuesPush(const std::map<std::string, std::string>& values, const std::vector<std::string>& removePaths, const std::vector<std::string>& discardPaths, ::sysrepo::Session session)
Tomáš Pecka272abaf2021-01-24 12:28:43 +0100126{
Tomáš Peckaa0ccc672023-06-21 21:47:44 +0200127 if (values.empty() && removePaths.empty() && discardPaths.empty()) return;
Tomáš Peckadaedba52021-03-08 12:52:20 +0100128
Václav Kubernát7efd6d52021-11-09 01:31:11 +0100129 std::optional<libyang::DataNode> edit;
Jan Kundrát498c3f82023-05-24 19:25:48 +0200130 valuesToYang(values, removePaths, discardPaths, session, edit);
Tomáš Pecka272abaf2021-01-24 12:28:43 +0100131
Václav Kubernát7efd6d52021-11-09 01:31:11 +0100132 if (edit) {
133 session.editBatch(*edit, sysrepo::DefaultOperation::Merge);
134 session.applyChanges();
135 }
Tomáš Pecka272abaf2021-01-24 12:28:43 +0100136}
137
Tomáš Pecka53f08ee2021-04-28 12:38:11 +0200138/** @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. */
Jan Kundrát498c3f82023-05-24 19:25:48 +0200139void valuesPush(const std::vector<YANGPair>& values, const std::vector<std::string>& removePaths, const std::vector<std::string>& discardPaths, sysrepo::Session session, sysrepo::Datastore datastore)
Tomáš Pecka53f08ee2021-04-28 12:38:11 +0200140{
Tomáš Pecka190c7242024-01-23 15:50:51 +0100141 ScopedDatastoreSwitch s(session, datastore);
Jan Kundrát498c3f82023-05-24 19:25:48 +0200142 valuesPush(values, removePaths, discardPaths, session);
Tomáš Pecka53f08ee2021-04-28 12:38:11 +0200143}
144
145/** @brief Set or remove paths in Sysrepo's current datastore. */
Jan Kundrát498c3f82023-05-24 19:25:48 +0200146void valuesPush(const std::vector<YANGPair>& values, const std::vector<std::string>& removePaths, const std::vector<std::string>& discardPaths, sysrepo::Session session)
Tomáš Pecka53f08ee2021-04-28 12:38:11 +0200147{
Tomáš Peckaa0ccc672023-06-21 21:47:44 +0200148 if (values.empty() && removePaths.empty() && discardPaths.empty())
Tomáš Pecka53f08ee2021-04-28 12:38:11 +0200149 return;
150
Václav Kubernát7efd6d52021-11-09 01:31:11 +0100151 std::optional<libyang::DataNode> edit;
Jan Kundrát498c3f82023-05-24 19:25:48 +0200152 valuesToYang(values, removePaths, discardPaths, session, edit);
Tomáš Pecka53f08ee2021-04-28 12:38:11 +0200153
Václav Kubernát7efd6d52021-11-09 01:31:11 +0100154 if (edit) {
Jan Kundrát7c162082023-01-13 12:18:39 +0100155 session.editBatch(*edit, sysrepo::DefaultOperation::Merge);
156 session.applyChanges();
Václav Kubernát7efd6d52021-11-09 01:31:11 +0100157 }
Tomáš Pecka53f08ee2021-04-28 12:38:11 +0200158}
159
Václav Kubernát6fa7f342021-01-26 17:00:01 +0100160/** @brief Checks whether a module is implemented in Sysrepo and throws if not. */
Václav Kubernát7efd6d52021-11-09 01:31:11 +0100161void ensureModuleImplemented(::sysrepo::Session session, const std::string& module, const std::string& revision)
Václav Kubernát6fa7f342021-01-26 17:00:01 +0100162{
Jan Kundrátb3e99982022-03-18 17:38:20 +0100163 if (auto mod = session.getContext().getModule(module, revision); !mod || !mod->implemented()) {
Václav Kubernát6fa7f342021-01-26 17:00:01 +0100164 throw std::runtime_error(module + "@" + revision + " is not implemented in sysrepo.");
165 }
166}
Tomáš Pecka53f08ee2021-04-28 12:38:11 +0200167YANGPair::YANGPair(std::string xpath, std::string value)
168 : m_xpath(std::move(xpath))
169 , m_value(std::move(value))
170{
171}
Václav Kubernát7efd6d52021-11-09 01:31:11 +0100172
173void setErrors(::sysrepo::Session session, const std::string& msg)
174{
175 session.setNetconfError(sysrepo::NetconfErrorInfo{
176 .type = "application",
177 .tag = "operation-failed",
178 .appTag = {},
179 .path = {},
Jan Kundrátb3e99982022-03-18 17:38:20 +0100180 .message = msg,
Václav Kubernát7efd6d52021-11-09 01:31:11 +0100181 .infoElements = {},
182 });
Jan Kundrátb3e99982022-03-18 17:38:20 +0100183 session.setErrorMessage(msg);
Václav Kubernát7efd6d52021-11-09 01:31:11 +0100184}
Tomáš Pecka190c7242024-01-23 15:50:51 +0100185
186ScopedDatastoreSwitch::ScopedDatastoreSwitch(sysrepo::Session session, sysrepo::Datastore ds)
187 : m_session(std::move(session))
188 , m_oldDatastore(m_session.activeDatastore())
189{
190 m_session.switchDatastore(ds);
191}
192
193ScopedDatastoreSwitch::~ScopedDatastoreSwitch()
194{
195 m_session.switchDatastore(m_oldDatastore);
196}
Tomáš Peckaba2dc312021-01-23 22:29:11 +0100197}