blob: a3d30bab99d49c29586dd7c07cf2c27872737824 [file] [log] [blame]
Václav Kubernátc31bd602019-03-07 11:44:48 +01001/*
2 * Copyright (C) 2019 CESNET, https://photonics.cesnet.cz/
3 *
4 * Written by Václav Kubernát <kubernat@cesnet.cz>
5 *
6*/
7
Václav Kubernát02a71152020-01-21 14:52:51 +01008#include "libyang_utils.hpp"
Václav Kubernát26b56082020-02-03 18:28:56 +01009#include "netconf-client.hpp"
Václav Kubernátc31bd602019-03-07 11:44:48 +010010#include "netconf_access.hpp"
11#include "utils.hpp"
12#include "yang_schema.hpp"
13
Jan Kundrát8f63fb72020-01-24 01:40:01 +010014
Václav Kubernátc31bd602019-03-07 11:44:48 +010015NetconfAccess::~NetconfAccess() = default;
Václav Kubernátf5d75152020-12-03 03:52:34 +010016namespace {
17auto targetToDs_get(const DatastoreTarget target)
18{
19 switch (target) {
20 case DatastoreTarget::Operational:
21 return libnetconf::NmdaDatastore::Operational;
22 case DatastoreTarget::Running:
23 return libnetconf::NmdaDatastore::Running;
24 case DatastoreTarget::Startup:
25 return libnetconf::NmdaDatastore::Startup;
26 }
Václav Kubernátc31bd602019-03-07 11:44:48 +010027
Václav Kubernátf5d75152020-12-03 03:52:34 +010028 __builtin_unreachable();
29}
30
31auto targetToDs_set(const DatastoreTarget target)
32{
33 switch (target) {
34 case DatastoreTarget::Operational:
35 case DatastoreTarget::Running:
36 return libnetconf::NmdaDatastore::Candidate;
37 case DatastoreTarget::Startup:
38 return libnetconf::NmdaDatastore::Startup;
39 }
40
41 __builtin_unreachable();
42}
43}
Václav Kubernátd6282912020-06-23 14:49:34 +020044DatastoreAccess::Tree NetconfAccess::getItems(const std::string& path) const
Václav Kubernátc31bd602019-03-07 11:44:48 +010045{
Jan Kundrátb331b552020-01-23 15:25:29 +010046 Tree res;
Václav Kubernát06b0f382021-10-04 11:20:47 +020047 auto config = [this, &path] {
48 if (m_serverHasNMDA) {
49 return m_session->getData(targetToDs_get(m_target), (path != "/") ? std::optional{path} : std::nullopt);
50 }
51
52 return m_session->get((path != "/") ? std::optional{path} : std::nullopt);
53 }();
Václav Kubernát9456b5c2019-10-02 21:14:52 +020054
55 if (config) {
Václav Kubernátcfdb9222021-07-07 22:36:24 +020056 lyNodesToTree(res, config->siblings());
Václav Kubernátc31bd602019-03-07 11:44:48 +010057 }
Václav Kubernátc31bd602019-03-07 11:44:48 +010058 return res;
59}
60
Václav Kubernátc31bd602019-03-07 11:44:48 +010061NetconfAccess::NetconfAccess(const std::string& hostname, uint16_t port, const std::string& user, const std::string& pubKey, const std::string& privKey)
Jan Kundrátf59b83c2022-03-18 18:12:08 +010062 : m_context(std::nullopt, libyang::ContextOptions::SetPrivParsed)
Václav Kubernáted4e3782022-03-02 23:57:33 +010063 , m_session(libnetconf::client::Session::connectPubkey(hostname, port, user, pubKey, privKey, m_context))
64 , m_schema(std::make_shared<YangSchema>(m_context))
Václav Kubernátc31bd602019-03-07 11:44:48 +010065{
Václav Kubernát06b0f382021-10-04 11:20:47 +020066 checkNMDA();
Václav Kubernátc31bd602019-03-07 11:44:48 +010067}
68
Václav Kubernát7a9463f2020-10-30 08:57:59 +010069NetconfAccess::NetconfAccess(const int source, const int sink)
Jan Kundrátf59b83c2022-03-18 18:12:08 +010070 : m_context(std::nullopt, libyang::ContextOptions::SetPrivParsed)
Václav Kubernáted4e3782022-03-02 23:57:33 +010071 , m_session(libnetconf::client::Session::connectFd(source, sink, m_context))
72 , m_schema(std::make_shared<YangSchema>(m_context))
Václav Kubernát7a9463f2020-10-30 08:57:59 +010073{
Václav Kubernát06b0f382021-10-04 11:20:47 +020074 checkNMDA();
Václav Kubernát7a9463f2020-10-30 08:57:59 +010075}
76
Jan Kundrátdab815e2020-01-22 19:44:11 +010077NetconfAccess::NetconfAccess(std::unique_ptr<libnetconf::client::Session>&& session)
78 : m_session(std::move(session))
Václav Kubernát1d50a5b2020-02-03 16:44:22 +010079 , m_schema(std::make_shared<YangSchema>(m_session->libyangContext()))
Jan Kundrátdab815e2020-01-22 19:44:11 +010080{
Václav Kubernát06b0f382021-10-04 11:20:47 +020081 checkNMDA();
Jan Kundrátdab815e2020-01-22 19:44:11 +010082}
83
Václav Kubernátc31bd602019-03-07 11:44:48 +010084NetconfAccess::NetconfAccess(const std::string& socketPath)
Jan Kundrátf59b83c2022-03-18 18:12:08 +010085 : m_context(std::nullopt, libyang::ContextOptions::SetPrivParsed)
Václav Kubernáted4e3782022-03-02 23:57:33 +010086 , m_session(libnetconf::client::Session::connectSocket(socketPath, m_context))
87 , m_schema(std::make_shared<YangSchema>(m_context))
Václav Kubernátc31bd602019-03-07 11:44:48 +010088{
Václav Kubernát06b0f382021-10-04 11:20:47 +020089 checkNMDA();
90}
91
92void NetconfAccess::checkNMDA()
93{
94 auto nmdaMod = m_schema->getYangModule("ietf-netconf-nmda");
95 m_serverHasNMDA = nmdaMod && nmdaMod->implemented();
Václav Kubernátc31bd602019-03-07 11:44:48 +010096}
97
Václav Kubernáte2e15ee2020-02-05 17:38:13 +010098void NetconfAccess::setNcLogLevel(NC_VERB_LEVEL level)
99{
100 libnetconf::client::setLogLevel(level);
101}
102
103void NetconfAccess::setNcLogCallback(const LogCb& callback)
104{
105 libnetconf::client::setLogCallback(callback);
106}
107
Václav Kubernátc31bd602019-03-07 11:44:48 +0100108void NetconfAccess::setLeaf(const std::string& path, leaf_data_ value)
109{
Jan Kundrát379bb572020-05-07 03:23:13 +0200110 auto lyValue = value.type() == typeid(empty_) ? std::nullopt : std::optional(leafDataToString(value));
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200111 auto nodes = m_schema->dataNodeFromPath(path, lyValue);
112 doEditFromDataNode(*nodes.createdParent);
Václav Kubernátc31bd602019-03-07 11:44:48 +0100113}
114
Jan Kundrátcbf288b2020-06-18 20:44:39 +0200115void NetconfAccess::createItem(const std::string& path)
Václav Kubernátc31bd602019-03-07 11:44:48 +0100116{
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200117 auto nodes = m_schema->dataNodeFromPath(path);
118 doEditFromDataNode(*nodes.createdParent);
Václav Kubernátc31bd602019-03-07 11:44:48 +0100119}
120
Jan Kundrátcbf288b2020-06-18 20:44:39 +0200121void NetconfAccess::deleteItem(const std::string& path)
Václav Kubernátc31bd602019-03-07 11:44:48 +0100122{
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200123 auto nodes = m_schema->dataNodeFromPath(path);
124
125 // When deleting leafs, `nodes.newNode` is opaque, because the leaf does not have a value. We need to use
126 // newAttrOpaqueJSON for opaque leafs.
127 if (nodes.createdNode->isOpaque()) {
128 nodes.createdNode->newAttrOpaqueJSON("ietf-netconf", "ietf-netconf:operation", "delete");
129 } else {
130 nodes.createdNode->newMeta(*m_schema->getYangModule("ietf-netconf"), "operation", "delete");
131 }
132 doEditFromDataNode(*nodes.createdParent);
Václav Kubernátc31bd602019-03-07 11:44:48 +0100133}
134
Václav Kubernátbf65dd72020-05-28 02:32:31 +0200135struct impl_toYangInsert {
136 std::string operator()(yang::move::Absolute& absolute)
137 {
138 return absolute == yang::move::Absolute::Begin ? "first" : "last";
139 }
140 std::string operator()(yang::move::Relative& relative)
141 {
142 return relative.m_position == yang::move::Relative::Position::After ? "after" : "before";
143 }
144};
145
146std::string toYangInsert(std::variant<yang::move::Absolute, yang::move::Relative> move)
147{
148 return std::visit(impl_toYangInsert{}, move);
149}
150
151void NetconfAccess::moveItem(const std::string& source, std::variant<yang::move::Absolute, yang::move::Relative> move)
152{
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200153 auto nodes = m_schema->dataNodeFromPath(source);
Jan Kundrátf59b83c2022-03-18 18:12:08 +0100154 auto sourceNode = *(nodes.createdNode->findPath(source));
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200155 auto yangModule = *m_schema->getYangModule("yang");
Jan Kundrátf59b83c2022-03-18 18:12:08 +0100156 sourceNode.newMeta(yangModule, "insert", toYangInsert(move));
Václav Kubernátbf65dd72020-05-28 02:32:31 +0200157
158 if (std::holds_alternative<yang::move::Relative>(move)) {
159 auto relative = std::get<yang::move::Relative>(move);
160 if (m_schema->nodeType(source) == yang::NodeTypes::LeafList) {
Jan Kundrátf59b83c2022-03-18 18:12:08 +0100161 sourceNode.newMeta(yangModule, "value", leafDataToString(relative.m_path.at(".")));
Václav Kubernátbf65dd72020-05-28 02:32:31 +0200162 } else {
Jan Kundrátf59b83c2022-03-18 18:12:08 +0100163 sourceNode.newMeta(yangModule, "key", instanceToString(relative.m_path, std::string{nodes.createdNode->schema().module().name()}));
Václav Kubernátbf65dd72020-05-28 02:32:31 +0200164 }
165 }
166 doEditFromDataNode(sourceNode);
167}
168
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200169void NetconfAccess::doEditFromDataNode(libyang::DataNode dataNode)
Václav Kubernátc31bd602019-03-07 11:44:48 +0100170{
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200171 auto data = dataNode.printStr(libyang::DataFormat::XML, libyang::PrintFlags::WithSiblings);
Václav Kubernát06b0f382021-10-04 11:20:47 +0200172 if (m_serverHasNMDA) {
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200173 m_session->editData(targetToDs_set(m_target), std::string{*data});
Václav Kubernát06b0f382021-10-04 11:20:47 +0200174 } else {
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200175 m_session->editConfig(NC_DATASTORE_CANDIDATE, NC_RPC_EDIT_DFLTOP_MERGE, NC_RPC_EDIT_TESTOPT_TESTSET, NC_RPC_EDIT_ERROPT_STOP, std::string{*data});
Václav Kubernát06b0f382021-10-04 11:20:47 +0200176 }
Václav Kubernátc31bd602019-03-07 11:44:48 +0100177}
178
179void NetconfAccess::commitChanges()
180{
181 m_session->commit();
182}
183
184void NetconfAccess::discardChanges()
185{
186 m_session->discard();
187}
188
Václav Kubernátb3960f82020-12-01 03:21:48 +0100189DatastoreAccess::Tree NetconfAccess::execute(const std::string& path, const Tree& input)
Jan Kundrát6ee84792020-01-24 01:43:36 +0100190{
Václav Kubernátfbab2d42021-02-05 16:12:34 +0100191 auto inputNode = treeToRpcInput(m_session->libyangContext(), path, input);
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200192 auto data = inputNode.printStr(libyang::DataFormat::XML, libyang::PrintFlags::WithSiblings);
Jan Kundrát6ee84792020-01-24 01:43:36 +0100193
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200194 auto output = m_session->rpc_or_action(std::string{*data});
195 if (!output) {
196 return {};
197 }
198 return rpcOutputToTree(*output);
Jan Kundrát6ee84792020-01-24 01:43:36 +0100199}
200
Václav Kubernát7160a132020-04-03 02:11:01 +0200201NC_DATASTORE toNcDatastore(Datastore datastore)
202{
203 switch (datastore) {
204 case Datastore::Running:
205 return NC_DATASTORE_RUNNING;
206 case Datastore::Startup:
207 return NC_DATASTORE_STARTUP;
208 }
209 __builtin_unreachable();
210}
211
212void NetconfAccess::copyConfig(const Datastore source, const Datastore destination)
213{
214 m_session->copyConfig(toNcDatastore(source), toNcDatastore(destination));
215}
216
Václav Kubernátc31bd602019-03-07 11:44:48 +0100217std::shared_ptr<Schema> NetconfAccess::schema()
218{
219 return m_schema;
220}
Václav Kubernátab612e92019-11-26 19:51:31 +0100221
222std::vector<ListInstance> NetconfAccess::listInstances(const std::string& path)
223{
224 std::vector<ListInstance> res;
Jan Kundrátf59b83c2022-03-18 18:12:08 +0100225 auto keys = m_session->libyangContext().findXPath(path).front().asList().keys();
226 auto nodes = m_session->libyangContext().newPath2(path, std::nullopt, libyang::CreationOptions::Opaque);
Václav Kubernátab612e92019-11-26 19:51:31 +0100227
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200228 // Here we create a tree with "selection leafs" for all they keys of our wanted list. These leafs tell NETCONF, that
229 // we only want the list's keys and not any other data.
Václav Kubernátab612e92019-11-26 19:51:31 +0100230 for (const auto& keyLeaf : keys) {
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200231 // Selection leafs need to be inserted directly to the list using relative paths, that's why `newNode` is used
232 // here.
Jan Kundrátf59b83c2022-03-18 18:12:08 +0100233 nodes.createdNode->newPath(keyLeaf.name().data(), std::nullopt, libyang::CreationOptions::Opaque);
Václav Kubernátab612e92019-11-26 19:51:31 +0100234 }
235
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200236 // Have to use `newParent` in case our wanted list is a nested list. With `newNode` I would only send the inner
237 // nested list and not the whole tree.
238 auto instances = m_session->get(std::string{*nodes.createdParent->printStr(libyang::DataFormat::XML, libyang::PrintFlags::WithSiblings)});
Václav Kubernátab612e92019-11-26 19:51:31 +0100239
Václav Kubernát45e55462020-02-04 11:19:32 +0100240 if (!instances) {
241 return res;
242 }
Václav Kubernátab612e92019-11-26 19:51:31 +0100243
Jan Kundrátf59b83c2022-03-18 18:12:08 +0100244 for (const auto& instance : instances->findXPath(path)) {
Václav Kubernátab612e92019-11-26 19:51:31 +0100245 ListInstance instanceRes;
246
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200247 for (const auto& keyLeaf : instance.child()->siblings()) {
Václav Kubernátba84ede2021-02-04 17:21:11 +0100248 // FIXME: even though we specified we only want the key leafs, Netopeer disregards that and sends more data,
249 // even lists and other stuff. We only want keys, so filter out non-leafs and non-keys
250 // https://github.com/CESNET/netopeer2/issues/825
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200251 if (keyLeaf.schema().nodeType() != libyang::NodeType::Leaf) {
Václav Kubernátba84ede2021-02-04 17:21:11 +0100252 continue;
253 }
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200254 if (!keyLeaf.schema().asLeaf().isKey()) {
Václav Kubernátba84ede2021-02-04 17:21:11 +0100255 continue;
256 }
257
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200258 auto leafData = keyLeaf.asTerm();
259 instanceRes.insert({std::string{leafData.schema().name()}, leafValueFromNode(leafData)});
Václav Kubernátab612e92019-11-26 19:51:31 +0100260 }
Václav Kubernátfaacd022020-07-08 16:44:38 +0200261 res.emplace_back(instanceRes);
Václav Kubernátab612e92019-11-26 19:51:31 +0100262 }
263
264 return res;
265}
Václav Kubernát70d7f7a2020-06-23 14:40:40 +0200266
267std::string NetconfAccess::dump(const DataFormat format) const
268{
269 auto config = m_session->get();
270 if (!config) {
271 return "";
272 }
Václav Kubernátcfdb9222021-07-07 22:36:24 +0200273 auto str = config->printStr(format == DataFormat::Xml ? libyang::DataFormat::XML : libyang::DataFormat::JSON, libyang::PrintFlags::WithSiblings);
274 if (!str) {
275 return "";
276 }
277
278 return std::string{*str};
Václav Kubernát70d7f7a2020-06-23 14:40:40 +0200279}