blob: 9180fe2932ac8622844784967fc630b55305f012 [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
Václav Kubernát7efd6d52021-11-09 01:31:11 +010067void valuesToYang(const std::map<std::string, std::string>& values, const std::vector<std::string>& removePaths, ::sysrepo::Session session, std::optional<libyang::DataNode>& 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
Václav Kubernát7efd6d52021-11-09 01:31:11 +010072void valuesToYang(const std::vector<YANGPair>& values, const std::vector<std::string>& removePaths, ::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 }
102}
103
Tomáš Pecka498e91c2021-03-02 17:46:47 +0100104/** @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. */
Václav Kubernát7efd6d52021-11-09 01:31:11 +0100105void valuesPush(const std::map<std::string, std::string>& values, const std::vector<std::string>& removePaths, ::sysrepo::Session session, sysrepo::Datastore datastore)
Tomáš Pecka272abaf2021-01-24 12:28:43 +0100106{
Václav Kubernát7efd6d52021-11-09 01:31:11 +0100107 auto oldDatastore = session.activeDatastore();
108 session.switchDatastore(datastore);
Tomáš Pecka272abaf2021-01-24 12:28:43 +0100109
Tomáš Pecka498e91c2021-03-02 17:46:47 +0100110 valuesPush(values, removePaths, session);
Tomáš Pecka272abaf2021-01-24 12:28:43 +0100111
Václav Kubernát7efd6d52021-11-09 01:31:11 +0100112 session.switchDatastore(oldDatastore);
Tomáš Pecka272abaf2021-01-24 12:28:43 +0100113}
114
Tomáš Pecka498e91c2021-03-02 17:46:47 +0100115/** @brief Set or remove paths in Sysrepo's current datastore. */
Václav Kubernát7efd6d52021-11-09 01:31:11 +0100116void valuesPush(const std::map<std::string, std::string>& values, const std::vector<std::string>& removePaths, ::sysrepo::Session session)
Tomáš Pecka272abaf2021-01-24 12:28:43 +0100117{
Tomáš Peckadaedba52021-03-08 12:52:20 +0100118 if (values.empty() && removePaths.empty()) return;
119
Václav Kubernát7efd6d52021-11-09 01:31:11 +0100120 std::optional<libyang::DataNode> edit;
Tomáš Pecka498e91c2021-03-02 17:46:47 +0100121 valuesToYang(values, removePaths, session, edit);
Tomáš Pecka272abaf2021-01-24 12:28:43 +0100122
Václav Kubernát7efd6d52021-11-09 01:31:11 +0100123 if (edit) {
124 session.editBatch(*edit, sysrepo::DefaultOperation::Merge);
125 session.applyChanges();
126 }
Tomáš Pecka272abaf2021-01-24 12:28:43 +0100127}
128
Tomáš Pecka53f08ee2021-04-28 12:38:11 +0200129/** @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. */
Václav Kubernát7efd6d52021-11-09 01:31:11 +0100130void valuesPush(const std::vector<YANGPair>& values, const std::vector<std::string>& removePaths, sysrepo::Session session, sysrepo::Datastore datastore)
Tomáš Pecka53f08ee2021-04-28 12:38:11 +0200131{
Václav Kubernát7efd6d52021-11-09 01:31:11 +0100132 auto oldDatastore = session.activeDatastore();
133 session.switchDatastore(datastore);
Tomáš Pecka53f08ee2021-04-28 12:38:11 +0200134
135 valuesPush(values, removePaths, session);
136
Václav Kubernát7efd6d52021-11-09 01:31:11 +0100137 session.switchDatastore(oldDatastore);
Tomáš Pecka53f08ee2021-04-28 12:38:11 +0200138}
139
140/** @brief Set or remove paths in Sysrepo's current datastore. */
Václav Kubernát7efd6d52021-11-09 01:31:11 +0100141void valuesPush(const std::vector<YANGPair>& values, const std::vector<std::string>& removePaths, sysrepo::Session session)
Tomáš Pecka53f08ee2021-04-28 12:38:11 +0200142{
143 if (values.empty() && removePaths.empty())
144 return;
145
Václav Kubernát7efd6d52021-11-09 01:31:11 +0100146 std::optional<libyang::DataNode> edit;
Tomáš Pecka53f08ee2021-04-28 12:38:11 +0200147 valuesToYang(values, removePaths, session, edit);
148
Václav Kubernát7efd6d52021-11-09 01:31:11 +0100149 if (edit) {
150 session.editBatch(*edit, sysrepo::DefaultOperation::Merge);
151 session.applyChanges();
152 }
Tomáš Pecka53f08ee2021-04-28 12:38:11 +0200153}
154
Václav Kubernát6fa7f342021-01-26 17:00:01 +0100155/** @brief Checks whether a module is implemented in Sysrepo and throws if not. */
Václav Kubernát7efd6d52021-11-09 01:31:11 +0100156void ensureModuleImplemented(::sysrepo::Session session, const std::string& module, const std::string& revision)
Václav Kubernát6fa7f342021-01-26 17:00:01 +0100157{
Jan Kundrátb3e99982022-03-18 17:38:20 +0100158 if (auto mod = session.getContext().getModule(module, revision); !mod || !mod->implemented()) {
Václav Kubernát6fa7f342021-01-26 17:00:01 +0100159 throw std::runtime_error(module + "@" + revision + " is not implemented in sysrepo.");
160 }
161}
Tomáš Pecka53f08ee2021-04-28 12:38:11 +0200162YANGPair::YANGPair(std::string xpath, std::string value)
163 : m_xpath(std::move(xpath))
164 , m_value(std::move(value))
165{
166}
Václav Kubernát7efd6d52021-11-09 01:31:11 +0100167
168void setErrors(::sysrepo::Session session, const std::string& msg)
169{
170 session.setNetconfError(sysrepo::NetconfErrorInfo{
171 .type = "application",
172 .tag = "operation-failed",
173 .appTag = {},
174 .path = {},
Jan Kundrátb3e99982022-03-18 17:38:20 +0100175 .message = msg,
Václav Kubernát7efd6d52021-11-09 01:31:11 +0100176 .infoElements = {},
177 });
Jan Kundrátb3e99982022-03-18 17:38:20 +0100178 session.setErrorMessage(msg);
Václav Kubernát7efd6d52021-11-09 01:31:11 +0100179}
Tomáš Peckaba2dc312021-01-23 22:29:11 +0100180}