blob: a6dbb7c6cb2fdffcef240da2d1090639cd4e36be [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
8#include <libyang/Libyang.hpp>
9#include <libyang/Tree_Data.hpp>
Václav Kubernát02a71152020-01-21 14:52:51 +010010#include "libyang_utils.hpp"
Václav Kubernát26b56082020-02-03 18:28:56 +010011#include "netconf-client.hpp"
Václav Kubernátc31bd602019-03-07 11:44:48 +010012#include "netconf_access.hpp"
13#include "utils.hpp"
14#include "yang_schema.hpp"
15
Jan Kundrát8f63fb72020-01-24 01:40:01 +010016
Václav Kubernátc31bd602019-03-07 11:44:48 +010017NetconfAccess::~NetconfAccess() = default;
Václav Kubernátf5d75152020-12-03 03:52:34 +010018namespace {
19auto targetToDs_get(const DatastoreTarget target)
20{
21 switch (target) {
22 case DatastoreTarget::Operational:
23 return libnetconf::NmdaDatastore::Operational;
24 case DatastoreTarget::Running:
25 return libnetconf::NmdaDatastore::Running;
26 case DatastoreTarget::Startup:
27 return libnetconf::NmdaDatastore::Startup;
28 }
Václav Kubernátc31bd602019-03-07 11:44:48 +010029
Václav Kubernátf5d75152020-12-03 03:52:34 +010030 __builtin_unreachable();
31}
32
33auto targetToDs_set(const DatastoreTarget target)
34{
35 switch (target) {
36 case DatastoreTarget::Operational:
37 case DatastoreTarget::Running:
38 return libnetconf::NmdaDatastore::Candidate;
39 case DatastoreTarget::Startup:
40 return libnetconf::NmdaDatastore::Startup;
41 }
42
43 __builtin_unreachable();
44}
45}
Václav Kubernátd6282912020-06-23 14:49:34 +020046DatastoreAccess::Tree NetconfAccess::getItems(const std::string& path) const
Václav Kubernátc31bd602019-03-07 11:44:48 +010047{
Jan Kundrátb331b552020-01-23 15:25:29 +010048 Tree res;
Václav Kubernát06b0f382021-10-04 11:20:47 +020049 auto config = [this, &path] {
50 if (m_serverHasNMDA) {
51 return m_session->getData(targetToDs_get(m_target), (path != "/") ? std::optional{path} : std::nullopt);
52 }
53
54 return m_session->get((path != "/") ? std::optional{path} : std::nullopt);
55 }();
Václav Kubernát9456b5c2019-10-02 21:14:52 +020056
57 if (config) {
Václav Kubernátdaf40312020-06-19 11:34:55 +020058 lyNodesToTree(res, config->tree_for());
Václav Kubernátc31bd602019-03-07 11:44:48 +010059 }
Václav Kubernátc31bd602019-03-07 11:44:48 +010060 return res;
61}
62
Václav Kubernátc31bd602019-03-07 11:44:48 +010063NetconfAccess::NetconfAccess(const std::string& hostname, uint16_t port, const std::string& user, const std::string& pubKey, const std::string& privKey)
Václav Kubernát1d50a5b2020-02-03 16:44:22 +010064 : m_session(libnetconf::client::Session::connectPubkey(hostname, port, user, pubKey, privKey))
65 , m_schema(std::make_shared<YangSchema>(m_session->libyangContext()))
Václav Kubernátc31bd602019-03-07 11:44:48 +010066{
Václav Kubernát06b0f382021-10-04 11:20:47 +020067 checkNMDA();
Václav Kubernátc31bd602019-03-07 11:44:48 +010068}
69
Václav Kubernát7a9463f2020-10-30 08:57:59 +010070NetconfAccess::NetconfAccess(const int source, const int sink)
71 : m_session(libnetconf::client::Session::connectFd(source, sink))
72 , m_schema(std::make_shared<YangSchema>(m_session->libyangContext()))
73{
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)
Václav Kubernát1d50a5b2020-02-03 16:44:22 +010085 : m_session(libnetconf::client::Session::connectSocket(socketPath))
86 , m_schema(std::make_shared<YangSchema>(m_session->libyangContext()))
Václav Kubernátc31bd602019-03-07 11:44:48 +010087{
Václav Kubernát06b0f382021-10-04 11:20:47 +020088 checkNMDA();
89}
90
91void NetconfAccess::checkNMDA()
92{
93 auto nmdaMod = m_schema->getYangModule("ietf-netconf-nmda");
94 m_serverHasNMDA = nmdaMod && nmdaMod->implemented();
Václav Kubernátc31bd602019-03-07 11:44:48 +010095}
96
Václav Kubernáte2e15ee2020-02-05 17:38:13 +010097void NetconfAccess::setNcLogLevel(NC_VERB_LEVEL level)
98{
99 libnetconf::client::setLogLevel(level);
100}
101
102void NetconfAccess::setNcLogCallback(const LogCb& callback)
103{
104 libnetconf::client::setLogCallback(callback);
105}
106
Václav Kubernátc31bd602019-03-07 11:44:48 +0100107void NetconfAccess::setLeaf(const std::string& path, leaf_data_ value)
108{
Jan Kundrát379bb572020-05-07 03:23:13 +0200109 auto lyValue = value.type() == typeid(empty_) ? std::nullopt : std::optional(leafDataToString(value));
110 auto node = m_schema->dataNodeFromPath(path, lyValue);
Václav Kubernátc31bd602019-03-07 11:44:48 +0100111 doEditFromDataNode(node);
112}
113
Jan Kundrátcbf288b2020-06-18 20:44:39 +0200114void NetconfAccess::createItem(const std::string& path)
Václav Kubernátc31bd602019-03-07 11:44:48 +0100115{
116 auto node = m_schema->dataNodeFromPath(path);
117 doEditFromDataNode(node);
118}
119
Jan Kundrátcbf288b2020-06-18 20:44:39 +0200120void NetconfAccess::deleteItem(const std::string& path)
Václav Kubernátc31bd602019-03-07 11:44:48 +0100121{
122 auto node = m_schema->dataNodeFromPath(path);
Václav Kubernátda8e4b92020-02-04 11:56:30 +0100123 auto container = *(node->find_path(path.c_str())->data().begin());
124 container->insert_attr(m_schema->getYangModule("ietf-netconf"), "operation", "delete");
Václav Kubernátc31bd602019-03-07 11:44:48 +0100125 doEditFromDataNode(node);
126}
127
Václav Kubernátbf65dd72020-05-28 02:32:31 +0200128struct impl_toYangInsert {
129 std::string operator()(yang::move::Absolute& absolute)
130 {
131 return absolute == yang::move::Absolute::Begin ? "first" : "last";
132 }
133 std::string operator()(yang::move::Relative& relative)
134 {
135 return relative.m_position == yang::move::Relative::Position::After ? "after" : "before";
136 }
137};
138
139std::string toYangInsert(std::variant<yang::move::Absolute, yang::move::Relative> move)
140{
141 return std::visit(impl_toYangInsert{}, move);
142}
143
144void NetconfAccess::moveItem(const std::string& source, std::variant<yang::move::Absolute, yang::move::Relative> move)
145{
146 auto node = m_schema->dataNodeFromPath(source);
147 auto sourceNode = *(node->find_path(source.c_str())->data().begin());
148 auto yangModule = m_schema->getYangModule("yang");
149 sourceNode->insert_attr(yangModule, "insert", toYangInsert(move).c_str());
150
151 if (std::holds_alternative<yang::move::Relative>(move)) {
152 auto relative = std::get<yang::move::Relative>(move);
153 if (m_schema->nodeType(source) == yang::NodeTypes::LeafList) {
154 sourceNode->insert_attr(yangModule, "value", leafDataToString(relative.m_path.at(".")).c_str());
155 } else {
Václav Kubernát2c4778b2020-06-18 11:55:20 +0200156 sourceNode->insert_attr(yangModule, "key", instanceToString(relative.m_path, node->node_module()->name()).c_str());
Václav Kubernátbf65dd72020-05-28 02:32:31 +0200157 }
158 }
159 doEditFromDataNode(sourceNode);
160}
161
Václav Kubernátc31bd602019-03-07 11:44:48 +0100162void NetconfAccess::doEditFromDataNode(std::shared_ptr<libyang::Data_Node> dataNode)
163{
164 auto data = dataNode->print_mem(LYD_XML, 0);
Václav Kubernát06b0f382021-10-04 11:20:47 +0200165 if (m_serverHasNMDA) {
166 m_session->editData(targetToDs_set(m_target), data);
167 } else {
168 m_session->editConfig(NC_DATASTORE_CANDIDATE, NC_RPC_EDIT_DFLTOP_MERGE, NC_RPC_EDIT_TESTOPT_TESTSET, NC_RPC_EDIT_ERROPT_STOP, data);
169 }
Václav Kubernátc31bd602019-03-07 11:44:48 +0100170}
171
172void NetconfAccess::commitChanges()
173{
174 m_session->commit();
175}
176
177void NetconfAccess::discardChanges()
178{
179 m_session->discard();
180}
181
Václav Kubernátb3960f82020-12-01 03:21:48 +0100182DatastoreAccess::Tree NetconfAccess::execute(const std::string& path, const Tree& input)
Jan Kundrát6ee84792020-01-24 01:43:36 +0100183{
Václav Kubernátfbab2d42021-02-05 16:12:34 +0100184 auto inputNode = treeToRpcInput(m_session->libyangContext(), path, input);
185 auto data = inputNode->print_mem(LYD_XML, 0);
Jan Kundrát6ee84792020-01-24 01:43:36 +0100186
Václav Kubernáta8789602020-07-20 15:18:19 +0200187 auto output = m_session->rpc_or_action(data);
Václav Kubernátfbab2d42021-02-05 16:12:34 +0100188 return rpcOutputToTree(path, output);
Jan Kundrát6ee84792020-01-24 01:43:36 +0100189}
190
Václav Kubernát7160a132020-04-03 02:11:01 +0200191NC_DATASTORE toNcDatastore(Datastore datastore)
192{
193 switch (datastore) {
194 case Datastore::Running:
195 return NC_DATASTORE_RUNNING;
196 case Datastore::Startup:
197 return NC_DATASTORE_STARTUP;
198 }
199 __builtin_unreachable();
200}
201
202void NetconfAccess::copyConfig(const Datastore source, const Datastore destination)
203{
204 m_session->copyConfig(toNcDatastore(source), toNcDatastore(destination));
205}
206
Václav Kubernátc31bd602019-03-07 11:44:48 +0100207std::shared_ptr<Schema> NetconfAccess::schema()
208{
209 return m_schema;
210}
Václav Kubernátab612e92019-11-26 19:51:31 +0100211
212std::vector<ListInstance> NetconfAccess::listInstances(const std::string& path)
213{
214 std::vector<ListInstance> res;
215 auto list = m_schema->dataNodeFromPath(path);
216
217 // This inserts selection nodes - I only want keys not other data
218 // To get the keys, I have to call find_path here - otherwise I would get keys of a top-level node (which might not even be a list)
219 auto keys = libyang::Schema_Node_List{(*(list->find_path(path.c_str())->data().begin()))->schema()}.keys();
220 for (const auto& keyLeaf : keys) {
221 // Have to call find_path here - otherwise I'll have the list, not the leaf inside it
222 auto selectionLeaf = *(m_schema->dataNodeFromPath(keyLeaf->path())->find_path(keyLeaf->path().c_str())->data().begin());
223 auto actualList = *(list->find_path(path.c_str())->data().begin());
224 actualList->insert(selectionLeaf);
225 }
226
Jan Kundrátbb525b42020-02-04 11:56:59 +0100227 auto instances = m_session->get(list->print_mem(LYD_XML, 0));
Václav Kubernátab612e92019-11-26 19:51:31 +0100228
Václav Kubernát45e55462020-02-04 11:19:32 +0100229 if (!instances) {
230 return res;
231 }
Václav Kubernátab612e92019-11-26 19:51:31 +0100232
233 for (const auto& instance : instances->find_path(path.c_str())->data()) {
234 ListInstance instanceRes;
235
236 // I take the first child here, because the first element (the parent of the child()) will be the list
237 for (const auto& keyLeaf : instance->child()->tree_for()) {
Václav Kubernátba84ede2021-02-04 17:21:11 +0100238 // FIXME: even though we specified we only want the key leafs, Netopeer disregards that and sends more data,
239 // even lists and other stuff. We only want keys, so filter out non-leafs and non-keys
240 // https://github.com/CESNET/netopeer2/issues/825
241 if (keyLeaf->schema()->nodetype() != LYS_LEAF) {
242 continue;
243 }
244 if (!std::make_shared<libyang::Schema_Node_Leaf>(keyLeaf->schema())->is_key()) {
245 continue;
246 }
247
Václav Kubernát2e4cafe2020-11-05 01:53:21 +0100248 auto leafData = std::make_shared<libyang::Data_Node_Leaf_List>(keyLeaf);
Václav Kubernátb4e5b182020-11-16 19:55:09 +0100249 instanceRes.insert({leafData->schema()->name(), leafValueFromNode(leafData)});
Václav Kubernátab612e92019-11-26 19:51:31 +0100250 }
Václav Kubernátfaacd022020-07-08 16:44:38 +0200251 res.emplace_back(instanceRes);
Václav Kubernátab612e92019-11-26 19:51:31 +0100252 }
253
254 return res;
255}
Václav Kubernát70d7f7a2020-06-23 14:40:40 +0200256
257std::string NetconfAccess::dump(const DataFormat format) const
258{
259 auto config = m_session->get();
260 if (!config) {
261 return "";
262 }
263 return config->print_mem(format == DataFormat::Xml ? LYD_XML : LYD_JSON, LYP_WITHSIBLINGS | LYP_FORMAT);
264}