blob: 0147b49db693ecf2e453898954a913564fe7ec5d [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{
Václav Kubernát7efd6d52021-11-09 01:31:11 +0100120 auto oldDatastore = session.activeDatastore();
121 session.switchDatastore(datastore);
Tomáš Pecka272abaf2021-01-24 12:28:43 +0100122
Jan Kundrát498c3f82023-05-24 19:25:48 +0200123 valuesPush(values, removePaths, discardPaths, session);
Tomáš Pecka272abaf2021-01-24 12:28:43 +0100124
Václav Kubernát7efd6d52021-11-09 01:31:11 +0100125 session.switchDatastore(oldDatastore);
Tomáš Pecka272abaf2021-01-24 12:28:43 +0100126}
127
Tomáš Pecka498e91c2021-03-02 17:46:47 +0100128/** @brief Set or remove paths in Sysrepo's current datastore. */
Jan Kundrát498c3f82023-05-24 19:25:48 +0200129void 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 +0100130{
Tomáš Peckadaedba52021-03-08 12:52:20 +0100131 if (values.empty() && removePaths.empty()) return;
132
Václav Kubernát7efd6d52021-11-09 01:31:11 +0100133 std::optional<libyang::DataNode> edit;
Jan Kundrát498c3f82023-05-24 19:25:48 +0200134 valuesToYang(values, removePaths, discardPaths, session, edit);
Tomáš Pecka272abaf2021-01-24 12:28:43 +0100135
Václav Kubernát7efd6d52021-11-09 01:31:11 +0100136 if (edit) {
137 session.editBatch(*edit, sysrepo::DefaultOperation::Merge);
138 session.applyChanges();
139 }
Tomáš Pecka272abaf2021-01-24 12:28:43 +0100140}
141
Tomáš Pecka53f08ee2021-04-28 12:38:11 +0200142/** @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 +0200143void 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 +0200144{
Václav Kubernát7efd6d52021-11-09 01:31:11 +0100145 auto oldDatastore = session.activeDatastore();
146 session.switchDatastore(datastore);
Tomáš Pecka53f08ee2021-04-28 12:38:11 +0200147
Jan Kundrát498c3f82023-05-24 19:25:48 +0200148 valuesPush(values, removePaths, discardPaths, session);
Tomáš Pecka53f08ee2021-04-28 12:38:11 +0200149
Václav Kubernát7efd6d52021-11-09 01:31:11 +0100150 session.switchDatastore(oldDatastore);
Tomáš Pecka53f08ee2021-04-28 12:38:11 +0200151}
152
153/** @brief Set or remove paths in Sysrepo's current datastore. */
Jan Kundrát498c3f82023-05-24 19:25:48 +0200154void 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 +0200155{
156 if (values.empty() && removePaths.empty())
157 return;
158
Václav Kubernát7efd6d52021-11-09 01:31:11 +0100159 std::optional<libyang::DataNode> edit;
Jan Kundrát498c3f82023-05-24 19:25:48 +0200160 valuesToYang(values, removePaths, discardPaths, session, edit);
Tomáš Pecka53f08ee2021-04-28 12:38:11 +0200161
Václav Kubernát7efd6d52021-11-09 01:31:11 +0100162 if (edit) {
Jan Kundrát7c162082023-01-13 12:18:39 +0100163 session.editBatch(*edit, sysrepo::DefaultOperation::Merge);
164 session.applyChanges();
Václav Kubernát7efd6d52021-11-09 01:31:11 +0100165 }
Tomáš Pecka53f08ee2021-04-28 12:38:11 +0200166}
167
Václav Kubernát6fa7f342021-01-26 17:00:01 +0100168/** @brief Checks whether a module is implemented in Sysrepo and throws if not. */
Václav Kubernát7efd6d52021-11-09 01:31:11 +0100169void ensureModuleImplemented(::sysrepo::Session session, const std::string& module, const std::string& revision)
Václav Kubernát6fa7f342021-01-26 17:00:01 +0100170{
Jan Kundrátb3e99982022-03-18 17:38:20 +0100171 if (auto mod = session.getContext().getModule(module, revision); !mod || !mod->implemented()) {
Václav Kubernát6fa7f342021-01-26 17:00:01 +0100172 throw std::runtime_error(module + "@" + revision + " is not implemented in sysrepo.");
173 }
174}
Tomáš Pecka53f08ee2021-04-28 12:38:11 +0200175YANGPair::YANGPair(std::string xpath, std::string value)
176 : m_xpath(std::move(xpath))
177 , m_value(std::move(value))
178{
179}
Václav Kubernát7efd6d52021-11-09 01:31:11 +0100180
181void setErrors(::sysrepo::Session session, const std::string& msg)
182{
183 session.setNetconfError(sysrepo::NetconfErrorInfo{
184 .type = "application",
185 .tag = "operation-failed",
186 .appTag = {},
187 .path = {},
Jan Kundrátb3e99982022-03-18 17:38:20 +0100188 .message = msg,
Václav Kubernát7efd6d52021-11-09 01:31:11 +0100189 .infoElements = {},
190 });
Jan Kundrátb3e99982022-03-18 17:38:20 +0100191 session.setErrorMessage(msg);
Václav Kubernát7efd6d52021-11-09 01:31:11 +0100192}
Tomáš Peckaba2dc312021-01-23 22:29:11 +0100193}