blob: 2a2fa96012327022b798eaaa6afd8c7240dbb9f6 [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átcf9224f2020-06-02 09:55:29 +02008#include <boost/algorithm/string/predicate.hpp>
Václav Kubernátc31bd602019-03-07 11:44:48 +01009#include <libyang/Libyang.hpp>
10#include <libyang/Tree_Data.hpp>
Václav Kubernát02a71152020-01-21 14:52:51 +010011#include "libyang_utils.hpp"
Václav Kubernát26b56082020-02-03 18:28:56 +010012#include "netconf-client.hpp"
Václav Kubernátc31bd602019-03-07 11:44:48 +010013#include "netconf_access.hpp"
14#include "utils.hpp"
15#include "yang_schema.hpp"
16
Jan Kundrát8f63fb72020-01-24 01:40:01 +010017
Václav Kubernátc31bd602019-03-07 11:44:48 +010018NetconfAccess::~NetconfAccess() = default;
19
Jan Kundrátb331b552020-01-23 15:25:29 +010020DatastoreAccess::Tree NetconfAccess::getItems(const std::string& path)
Václav Kubernátc31bd602019-03-07 11:44:48 +010021{
Jan Kundrátb331b552020-01-23 15:25:29 +010022 Tree res;
Jan Kundrátb091dd42020-01-30 09:28:28 +010023 auto config = m_session->get((path != "/") ? std::optional{path} : std::nullopt);
Václav Kubernát9456b5c2019-10-02 21:14:52 +020024
25 if (config) {
Václav Kubernátcf9224f2020-06-02 09:55:29 +020026 auto siblings = config->tree_for();
27 for (auto it = siblings.begin(); it < siblings.end(); it++) {
28 if ((*it)->schema()->nodetype() == LYS_LEAFLIST) {
29 auto leafListPath = stripLeafListValueFromPath((*it)->path());
30 res.emplace_back(leafListPath, special_{SpecialValue::LeafList});
31 while (it != siblings.end() && boost::starts_with((*it)->path(), leafListPath)) {
Václav Kubernát3c8fe022020-06-04 01:35:03 +020032 lyNodesToTree(res, (*it)->tree_dfs());
Václav Kubernátcf9224f2020-06-02 09:55:29 +020033 it++;
34 }
35 } else {
Václav Kubernát3c8fe022020-06-04 01:35:03 +020036 lyNodesToTree(res, (*it)->tree_dfs());
Václav Kubernátcf9224f2020-06-02 09:55:29 +020037 }
Václav Kubernátc31bd602019-03-07 11:44:48 +010038 }
Václav Kubernátc31bd602019-03-07 11:44:48 +010039 }
Václav Kubernátc31bd602019-03-07 11:44:48 +010040 return res;
41}
42
Václav Kubernátc31bd602019-03-07 11:44:48 +010043NetconfAccess::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 +010044 : m_session(libnetconf::client::Session::connectPubkey(hostname, port, user, pubKey, privKey))
45 , m_schema(std::make_shared<YangSchema>(m_session->libyangContext()))
Václav Kubernátc31bd602019-03-07 11:44:48 +010046{
Václav Kubernátc31bd602019-03-07 11:44:48 +010047}
48
Jan Kundrátdab815e2020-01-22 19:44:11 +010049NetconfAccess::NetconfAccess(std::unique_ptr<libnetconf::client::Session>&& session)
50 : m_session(std::move(session))
Václav Kubernát1d50a5b2020-02-03 16:44:22 +010051 , m_schema(std::make_shared<YangSchema>(m_session->libyangContext()))
Jan Kundrátdab815e2020-01-22 19:44:11 +010052{
Jan Kundrátdab815e2020-01-22 19:44:11 +010053}
54
Václav Kubernátc31bd602019-03-07 11:44:48 +010055NetconfAccess::NetconfAccess(const std::string& socketPath)
Václav Kubernát1d50a5b2020-02-03 16:44:22 +010056 : m_session(libnetconf::client::Session::connectSocket(socketPath))
57 , m_schema(std::make_shared<YangSchema>(m_session->libyangContext()))
Václav Kubernátc31bd602019-03-07 11:44:48 +010058{
Václav Kubernátc31bd602019-03-07 11:44:48 +010059}
60
61void NetconfAccess::setLeaf(const std::string& path, leaf_data_ value)
62{
Jan Kundrát379bb572020-05-07 03:23:13 +020063 auto lyValue = value.type() == typeid(empty_) ? std::nullopt : std::optional(leafDataToString(value));
64 auto node = m_schema->dataNodeFromPath(path, lyValue);
Václav Kubernátc31bd602019-03-07 11:44:48 +010065 doEditFromDataNode(node);
66}
67
68void NetconfAccess::createPresenceContainer(const std::string& path)
69{
70 auto node = m_schema->dataNodeFromPath(path);
71 doEditFromDataNode(node);
72}
73
74void NetconfAccess::deletePresenceContainer(const std::string& path)
75{
76 auto node = m_schema->dataNodeFromPath(path);
Václav Kubernátda8e4b92020-02-04 11:56:30 +010077 auto container = *(node->find_path(path.c_str())->data().begin());
78 container->insert_attr(m_schema->getYangModule("ietf-netconf"), "operation", "delete");
Václav Kubernátc31bd602019-03-07 11:44:48 +010079 doEditFromDataNode(node);
80}
81
82void NetconfAccess::createListInstance(const std::string& path)
83{
84 auto node = m_schema->dataNodeFromPath(path);
85 doEditFromDataNode(node);
86}
87
88void NetconfAccess::deleteListInstance(const std::string& path)
89{
90 auto node = m_schema->dataNodeFromPath(path);
Václav Kubernátcc7a93f2020-02-04 11:48:15 +010091 auto list = *(node->find_path(path.c_str())->data().begin());
92 list->insert_attr(m_schema->getYangModule("ietf-netconf"), "operation", "delete");
Václav Kubernátc31bd602019-03-07 11:44:48 +010093 doEditFromDataNode(node);
94}
95
Václav Kubernát5b8a8f32020-05-20 00:57:22 +020096void NetconfAccess::createLeafListInstance(const std::string& path)
97{
98 auto node = m_schema->dataNodeFromPath(path);
99 doEditFromDataNode(node);
100}
101void NetconfAccess::deleteLeafListInstance(const std::string& path)
102{
103 auto node = m_schema->dataNodeFromPath(path);
104 auto list = *(node->find_path(path.c_str())->data().begin());
105 list->insert_attr(m_schema->getYangModule("ietf-netconf"), "operation", "delete");
106 doEditFromDataNode(node);
107}
108
Václav Kubernátbf65dd72020-05-28 02:32:31 +0200109struct impl_toYangInsert {
110 std::string operator()(yang::move::Absolute& absolute)
111 {
112 return absolute == yang::move::Absolute::Begin ? "first" : "last";
113 }
114 std::string operator()(yang::move::Relative& relative)
115 {
116 return relative.m_position == yang::move::Relative::Position::After ? "after" : "before";
117 }
118};
119
120std::string toYangInsert(std::variant<yang::move::Absolute, yang::move::Relative> move)
121{
122 return std::visit(impl_toYangInsert{}, move);
123}
124
125void NetconfAccess::moveItem(const std::string& source, std::variant<yang::move::Absolute, yang::move::Relative> move)
126{
127 auto node = m_schema->dataNodeFromPath(source);
128 auto sourceNode = *(node->find_path(source.c_str())->data().begin());
129 auto yangModule = m_schema->getYangModule("yang");
130 sourceNode->insert_attr(yangModule, "insert", toYangInsert(move).c_str());
131
132 if (std::holds_alternative<yang::move::Relative>(move)) {
133 auto relative = std::get<yang::move::Relative>(move);
134 if (m_schema->nodeType(source) == yang::NodeTypes::LeafList) {
135 sourceNode->insert_attr(yangModule, "value", leafDataToString(relative.m_path.at(".")).c_str());
136 } else {
137 sourceNode->insert_attr(yangModule, "key", instanceToString(node->node_module()->name(), relative.m_path).c_str());
138 }
139 }
140 doEditFromDataNode(sourceNode);
141}
142
Václav Kubernátc31bd602019-03-07 11:44:48 +0100143void NetconfAccess::doEditFromDataNode(std::shared_ptr<libyang::Data_Node> dataNode)
144{
145 auto data = dataNode->print_mem(LYD_XML, 0);
146 m_session->editConfig(NC_DATASTORE_CANDIDATE, NC_RPC_EDIT_DFLTOP_MERGE, NC_RPC_EDIT_TESTOPT_TESTSET, NC_RPC_EDIT_ERROPT_STOP, data);
147}
148
149void NetconfAccess::commitChanges()
150{
151 m_session->commit();
152}
153
154void NetconfAccess::discardChanges()
155{
156 m_session->discard();
157}
158
Jan Kundrát6ee84792020-01-24 01:43:36 +0100159DatastoreAccess::Tree NetconfAccess::executeRpc(const std::string& path, const Tree& input)
160{
161 auto root = m_schema->dataNodeFromPath(path);
162 for (const auto& [k, v] : input) {
163 auto node = m_schema->dataNodeFromPath(joinPaths(path, k), leafDataToString(v));
164 root->merge(node, 0);
165 }
166 auto data = root->print_mem(LYD_XML, 0);
167
168 Tree res;
169 auto output = m_session->rpc(data);
170 if (output) {
171 for (auto it : output->tree_for()) {
Václav Kubernát3c8fe022020-06-04 01:35:03 +0200172 lyNodesToTree(res, it->tree_dfs(), joinPaths(path, "/"));
Jan Kundrát6ee84792020-01-24 01:43:36 +0100173 }
174 }
175 return res;
176}
177
Václav Kubernát7160a132020-04-03 02:11:01 +0200178NC_DATASTORE toNcDatastore(Datastore datastore)
179{
180 switch (datastore) {
181 case Datastore::Running:
182 return NC_DATASTORE_RUNNING;
183 case Datastore::Startup:
184 return NC_DATASTORE_STARTUP;
185 }
186 __builtin_unreachable();
187}
188
189void NetconfAccess::copyConfig(const Datastore source, const Datastore destination)
190{
191 m_session->copyConfig(toNcDatastore(source), toNcDatastore(destination));
192}
193
Václav Kubernátb52dc252019-12-04 13:03:39 +0100194std::string NetconfAccess::fetchSchema(const std::string_view module, const
195 std::optional<std::string_view> revision, const
196 std::optional<std::string_view> submodule, const
197 std::optional<std::string_view> submoduleRevision)
Václav Kubernátc31bd602019-03-07 11:44:48 +0100198{
Václav Kubernátb52dc252019-12-04 13:03:39 +0100199 if (submodule) {
200 return m_session->getSchema(*submodule, submoduleRevision);
201 }
Václav Kubernátc31bd602019-03-07 11:44:48 +0100202 return m_session->getSchema(module, revision);
203}
204
205std::vector<std::string> NetconfAccess::listImplementedSchemas()
206{
207 auto data = m_session->get("/ietf-netconf-monitoring:netconf-state/schemas");
208 auto set = data->find_path("/ietf-netconf-monitoring:netconf-state/schemas/schema/identifier");
209
210 std::vector<std::string> res;
211 for (auto it : set->data()) {
212 if (it->schema()->nodetype() == LYS_LEAF) {
213 libyang::Data_Node_Leaf_List leaf(it);
214 res.push_back(leaf.value_str());
215 }
216 }
217 return res;
218}
219
220std::shared_ptr<Schema> NetconfAccess::schema()
221{
222 return m_schema;
223}
Václav Kubernátab612e92019-11-26 19:51:31 +0100224
225std::vector<ListInstance> NetconfAccess::listInstances(const std::string& path)
226{
227 std::vector<ListInstance> res;
228 auto list = m_schema->dataNodeFromPath(path);
229
230 // This inserts selection nodes - I only want keys not other data
231 // 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)
232 auto keys = libyang::Schema_Node_List{(*(list->find_path(path.c_str())->data().begin()))->schema()}.keys();
233 for (const auto& keyLeaf : keys) {
234 // Have to call find_path here - otherwise I'll have the list, not the leaf inside it
235 auto selectionLeaf = *(m_schema->dataNodeFromPath(keyLeaf->path())->find_path(keyLeaf->path().c_str())->data().begin());
236 auto actualList = *(list->find_path(path.c_str())->data().begin());
237 actualList->insert(selectionLeaf);
238 }
239
Jan Kundrátbb525b42020-02-04 11:56:59 +0100240 auto instances = m_session->get(list->print_mem(LYD_XML, 0));
Václav Kubernátab612e92019-11-26 19:51:31 +0100241
Václav Kubernát45e55462020-02-04 11:19:32 +0100242 if (!instances) {
243 return res;
244 }
Václav Kubernátab612e92019-11-26 19:51:31 +0100245
246 for (const auto& instance : instances->find_path(path.c_str())->data()) {
247 ListInstance instanceRes;
248
249 // I take the first child here, because the first element (the parent of the child()) will be the list
250 for (const auto& keyLeaf : instance->child()->tree_for()) {
251 auto leafData = libyang::Data_Node_Leaf_List{keyLeaf};
252 auto leafSchema = libyang::Schema_Node_Leaf{leafData.schema()};
253 instanceRes.insert({ leafSchema.name(), leafValueFromValue(leafData.value(), leafSchema.type()->base())});
254 }
255 res.push_back(instanceRes);
256 }
257
258 return res;
259}