Kea 3.3.3
translator.cc
Go to the documentation of this file.
1// Copyright (C) 2018-2026 Internet Systems Consortium, Inc. ("ISC")
2//
3// This Source Code Form is subject to the terms of the Mozilla Public
4// License, v. 2.0. If a copy of the MPL was not distributed with this
5// file, You can obtain one at http://mozilla.org/MPL/2.0/.
6
7#include <config.h>
8
10#include <yang/adaptor.h>
11#include <yang/translator.h>
12
13#include <sysrepo-cpp/utils/exception.hpp>
14
15#include <cstring>
16#include <vector>
17
18using namespace std;
19using namespace isc::data;
20using namespace isc::util::encode;
21using namespace libyang;
22using namespace sysrepo;
23
24namespace isc {
25namespace yang {
26
27Translator::Translator(Session session, const string& model)
28 : session_(session), model_(model) {
29}
30
31string
32Translator::quoteXPathValue(string const& value) {
33 bool const has_single(value.find('\'') != string::npos);
34 bool const has_double(value.find('"') != string::npos);
35 if (has_single && has_double) {
36 isc_throw(BadValue, "XPath literal contains both single and double "
37 "quotes and cannot be safely quoted: " << value);
38 }
39 if (has_single) {
40 return ("\"" + value + "\"");
41 }
42 return ("'" + value + "'");
43}
44
45void
47 DataNode const& data_node,
48 string const& name) const {
49 ElementPtr const& x(getItem(data_node, name));
50 if (x) {
51 storage->set(name, x);
52 }
53}
54
55void
57 DataNode const& data_node,
58 string const& name,
59 string const& yang_name) const {
60 ElementPtr const& x(getItem(data_node, yang_name));
61 if (x) {
62 storage->set(name, x);
63 }
64}
65
66void
68 DataNode const& data_node,
69 string const& name) const {
70 ElementPtr const& x(getItem(data_node, name));
71 if (x) {
72 storage->set(name, Element::fromJSON(x->stringValue()));
73 }
74}
75
76void
78 string const& xpath,
79 string const& name,
80 LeafBaseType const type) {
81 ConstElementPtr const& x(from->get(name));
82 if (x) {
83 setItem(xpath + "/" + name, x, type);
84 }
85}
86
87void
89 string const& xpath,
90 string const& name,
91 string const& yang_name,
92 LeafBaseType const type) {
93 ConstElementPtr const& x(from->get(name));
94 if (x) {
95 setItem(xpath + "/" + yang_name, x, type);
96 }
97}
98
99void
101 string const& xpath,
102 string const& name,
103 LeafBaseType const type) {
104 ConstElementPtr const& leaf_list(from->get(name));
105 if (leaf_list && !leaf_list->empty()) {
106 for (ElementPtr const& leaf : leaf_list->listValue()) {
107 setItem(xpath + "/" + name, leaf, type);
108 }
109 }
110}
111
112void
114 string const& xpath) {
115 ConstElementPtr const& user_context(Adaptor::getContext(from));
116 if (user_context) {
117 setItem(xpath + "/user-context", Element::create(user_context->str()),
118 LeafBaseType::String);
119 }
120}
121
122void
124 string const& xpath,
125 string const& name) {
126 ConstElementPtr const& x(from->get(name));
127 if (x) {
128 ElementPtr const& json(Element::create(x->str()));
129 setItem(xpath + "/" + name, json, LeafBaseType::String);
130 }
131}
132
133void
134Translator::deleteItem(string const& xpath) {
135 try {
136 if (session_.getData(xpath)) {
137 session_.deleteItem(xpath);
138 }
139 } catch (sysrepo::Error const& ex) {
140 isc_throw(NetconfError, "deleting item at '" << xpath << "': " << ex.what());
141 }
142 session_.applyChanges();
143}
144
145DataNode
146Translator::findXPath(string const& xpath) const {
147 optional<DataNode> const& data_node(getData(xpath));
148 if (!data_node) {
149 isc_throw(NetconfError, "no data at xpath " << xpath);
150 }
151 Set<DataNode> at_path(data_node->findXPath(xpath));
152 if (at_path.empty()) {
153 isc_throw(NetconfError, "no data at xpath " << xpath);
154 }
155 return at_path.front();
156}
157
158optional<DataNode>
159Translator::getData(string const& xpath) const {
160 optional<DataNode> data_node;
161 try {
162 data_node = session_.getData(xpath);
163 } catch (sysrepo::Error const& ex) {
164 isc_throw(NetconfError, "getting item at '" << xpath << "': " << ex.what());
165 }
166
167 return data_node;
168}
169
171Translator::getItem(DataNode const& data_node,
172 string const& xpath) const {
173 try {
174 Set<DataNode> const& nodes(data_node.findXPath(xpath));
175 if (nodes.empty()) {
176 return ElementPtr();
177 }
178 DataNode const& front(nodes.front());
179 NodeType const node_type(front.schema().nodeType());
180
181 // Leaf
182 if (node_type == NodeType::Leaf) {
183 return translateFromYang(front);
184 } else if (node_type == NodeType::Leaflist) {
186 for (DataNode const& i : nodes) {
187 result->add(translateFromYang(i));
188 }
189 return result;
190 } else {
191 isc_throw(NotImplemented, "getting node of type "
192 << node_type << " not supported, xpath is '" << xpath
193 << "'");
194 }
195
196 } catch (sysrepo::Error const& ex) {
197 isc_throw(NetconfError, "getting item at '" << xpath << "': " << ex.what());
198 }
199}
200
202Translator::getItemFromAbsoluteXpath(string const& xpath) const {
203 optional<DataNode> const& data_node(getData(xpath));
204 if (!data_node) {
205 return ElementPtr();
206 }
207 return getItem(*data_node, xpath);
208}
209
210void
212 DataNode const& data_node,
213 string const& name) const {
214 ElementPtr const& x(getItem(data_node, name));
215 if (!x) {
216 isc_throw(MissingNode, name);
217 }
218 storage->set(name, x);
219}
220
221void
223 DataNode const& data_node,
224 string const& name,
225 string const& yang_name) const {
226 ElementPtr const& x(getItem(data_node, yang_name));
227 if (!x) {
228 isc_throw(MissingNode, yang_name);
229 }
230 storage->set(name, x);
231}
232
233bool
234Translator::schemaNodeExists(string const& xpath) const {
235 Context const& context(session_.getContext());
236 try {
237 context.findPath(xpath);
238 } catch (libyang::Error const& ex) {
239 return false;
240 }
241 return true;
242}
243
244void
245Translator::setItem(const string& xpath,
246 ConstElementPtr const elem,
247 LeafBaseType const type) {
248 optional<string> const value(translateToYang(elem, type));
249 try {
250 session_.setItem(xpath, value);
251 } catch (sysrepo::Error const& ex) {
252 isc_throw(NetconfError, "setting item '" << (elem ? elem->str() : "nullopt")
253 << "' at '" << xpath << "': " << ex.what());
254 }
255 session_.applyChanges();
256}
257
258void
260 string const& xpath,
261 string const& name,
262 LeafBaseType const type) {
263 ConstElementPtr const& x(from->get(name));
264 if (!x) {
265 isc_throw(MissingNode, "xpath: " << xpath << ", name: " << name);
266 }
267 setItem(xpath + "/" + name, x, type);
268}
269
270void
272 string const& xpath,
273 string const& name,
274 string const& yang_name,
275 LeafBaseType const type) {
276 ConstElementPtr const& x(from->get(name));
277 if (!x) {
278 isc_throw(MissingNode, "xpath: " << xpath << ", name: " << name);
279 }
280 setItem(xpath + "/" + yang_name, x, type);
281}
282
284Translator::translateFromYang(optional<DataNode> data_node) {
285 NodeType const node_type(data_node->schema().nodeType());
286 if (node_type == NodeType::Leaf || node_type == NodeType::Leaflist) {
287 DataNodeTerm const& leaf(data_node->asTerm());
288 LeafBaseType type;
289 if (node_type == NodeType::Leaf) {
290 type = leaf.schema().asLeaf().valueType().base();
291 } else {
292 type = leaf.schema().asLeafList().valueType().base();
293 }
294
295 static Deserializer deserializer(initializeDeserializer());
296 return deserializer.at(type)(string(leaf.valueStr()));
297 }
298 return ElementPtr();
299}
300
301optional<string>
303 libyang::LeafBaseType const type) {
304 string string_representation;
305 if (!element) {
306 // A null ElementPtr is how we signal that this item requires no value.
307 // Useful when setting YANG lists which is the only way to set their
308 // keys in sysrepo since setting the key itself results in an error.
309 return nullopt;
310 } else if (element->getType() == Element::map) {
311 isc_throw(NotImplemented, "Translator::value(): map element");
312 } else if (element->getType() == Element::list) {
313 isc_throw(NotImplemented, "Translator::value(): list element");
314 } else if (element->getType() == Element::string) {
315 // If it's a string, get the variant without quotes.
316 string_representation = element->stringValue();
317 } else {
318 // If it's not a string, also get the variant without quotes, but it's a different method.
319 string_representation = element->str();
320 }
321
322 static Serializer serializer(initializeSerializer());
323 return serializer.at(type)(string_representation);
324}
325
326string
327Translator::decode64(string const& input) {
328 vector<uint8_t> binary;
329 decodeBase64(input, binary);
330 string result;
331 result.resize(binary.size());
332 if (!result.empty()) {
333 memcpy(&result[0], &binary[0], result.size());
334 }
335 return (result);
336}
337
338string
339Translator::encode64(string const& input) {
340 vector<uint8_t> binary;
341 binary.resize(input.size());
342 if (!binary.empty()) {
343 memcpy(&binary[0], input.c_str(), binary.size());
344 }
345 return (encodeBase64(binary));
346}
347
350 Deserializer result;
351
352 result.emplace(LeafBaseType::Binary, [](string const& value) -> ElementPtr const {
353 return Element::create(decode64(value));
354 });
355
356 for (LeafBaseType const& i :
357 {LeafBaseType::Bool, LeafBaseType::Dec64, LeafBaseType::Int8, LeafBaseType::Int16,
358 LeafBaseType::Int32, LeafBaseType::Int64, LeafBaseType::Uint8, LeafBaseType::Uint16,
359 LeafBaseType::Uint32, LeafBaseType::Uint64}) {
360 result.emplace(i, [](string const& value) -> ElementPtr const {
361 return Element::fromJSON(value);
362 });
363 }
364
365 // The rest of YANG values can be expressed as strings.
366 for (LeafBaseType const& i :
367 {LeafBaseType::Bits, LeafBaseType::Empty, LeafBaseType::Enum, LeafBaseType::IdentityRef,
368 LeafBaseType::InstanceIdentifier, LeafBaseType::Leafref, LeafBaseType::String,
369 LeafBaseType::Union}) {
370 result.emplace(i, [](string const& value) -> ElementPtr const {
371 return Element::create(value);
372 });
373 }
374
375 return result;
376}
377
380 Serializer result;
381
382 result.emplace(LeafBaseType::Binary, [](string const& value) -> string const {
383 return encode64(value);
384 });
385
386 // The rest of YANG values can be expressed directly.
387 for (LeafBaseType const& i :
388 {LeafBaseType::Bits, LeafBaseType::Bool, LeafBaseType::Dec64, LeafBaseType::Empty,
389 LeafBaseType::Enum, LeafBaseType::IdentityRef, LeafBaseType::InstanceIdentifier,
390 LeafBaseType::Int8, LeafBaseType::Int16, LeafBaseType::Int32, LeafBaseType::Int64,
391 LeafBaseType::Leafref, LeafBaseType::String, LeafBaseType::Uint8, LeafBaseType::Uint16,
392 LeafBaseType::Uint32, LeafBaseType::Uint64, LeafBaseType::Union, LeafBaseType::Unknown}) {
393 result.emplace(i, [](string const& value) -> string const {
394 return value;
395 });
396 }
397
398 return result;
399}
400
401} // namespace yang
402} // namespace isc
static ElementPtr create(const Position &pos=ZERO_POSITION())
Create a NullElement.
Definition data.cc:300
@ map
Definition data.h:160
@ list
Definition data.h:159
@ string
Definition data.h:157
static ElementPtr fromJSON(const std::string &in, bool preproc=false)
These functions will parse the given string (JSON) representation of a compound element.
Definition data.cc:865
static ElementPtr createList(const Position &pos=ZERO_POSITION())
Creates an empty ListElement type ElementPtr.
Definition data.cc:350
A generic exception that is thrown if a parameter given to a method is considered invalid in that con...
A generic exception that is thrown when a function is not implemented.
static isc::data::ConstElementPtr getContext(isc::data::ConstElementPtr parent)
Get user context.
Definition adaptor.cc:20
void getMandatoryDivergingLeaf(isc::data::ElementPtr &storage, libyang::DataNode const &data_node, std::string const &name, std::string const &yang_name) const
Retrieves a child YANG data node identified by one name from the given parent YANG container node and...
void setMandatoryLeaf(isc::data::ConstElementPtr const &from, std::string const &xpath, std::string const &name, libyang::LeafBaseType const type)
Get an element from given ElementPtr node and set it in sysrepo at given xpath.
isc::data::ElementPtr getItemFromAbsoluteXpath(std::string const &xpath) const
Translate a basic value from YANG to JSON for a given absolute xpath.
void checkAndSetLeaf(isc::data::ConstElementPtr const &from, std::string const &xpath, std::string const &name, libyang::LeafBaseType const type)
Get an element from given ElementPtr node and set it in sysrepo at given xpath.
Definition translator.cc:77
static std::string decode64(std::string const &input)
Decode a YANG element of binary type to a string that can be stored in an Element::string JSON.
isc::data::ElementPtr getItem(libyang::DataNode const &data_node, std::string const &xpath) const
Translate a basic value from YANG to JSON for a given xpath that is relative to the given source node...
std::unordered_map< libyang::LeafBaseType, std::function< std::string const (std::string const &)> > Serializer
Maps YANG types to functions that transform the string representation of an Element into a string tha...
Definition translator.h:435
static isc::data::ElementPtr translateFromYang(std::optional< libyang::DataNode > data_node)
Translate basic value from the given YANG data node to JSON element.
void setMandatoryDivergingLeaf(isc::data::ConstElementPtr const &from, std::string const &xpath, std::string const &name, std::string const &yang_name, libyang::LeafBaseType const type)
Get an element from given ElementPtr node and set it in sysrepo at given xpath.
static std::string quoteXPathValue(std::string const &value)
Quote a value for use in an XPath predicate literal.
Definition translator.cc:32
libyang::DataNode findXPath(std::string const &xpath) const
Retrieves a YANG data node by xpath.
void getMandatoryLeaf(isc::data::ElementPtr &storage, libyang::DataNode const &data_node, std::string const &name) const
Retrieves a child YANG data node identified by name from the given parent YANG container node and sto...
void checkAndGetLeaf(isc::data::ElementPtr &storage, libyang::DataNode const &data_node, std::string const &name) const
Retrieves a child YANG data node identified by name from the given parent YANG container node and sto...
Definition translator.cc:46
void checkAndSetUserContext(isc::data::ConstElementPtr const &from, std::string const &xpath)
Get an element from given ElementPtr node and set it in sysrepo at given xpath.
static Serializer initializeSerializer()
Initializes the serializer which is used to translate the string value of an Element to a string that...
std::unordered_map< libyang::LeafBaseType, std::function< isc::data::ElementPtr const (std::string const &)> > Deserializer
Maps YANG types to functions that transform a YANG type into an ElementPtr.
Definition translator.h:424
void deleteItem(const std::string &xpath)
Delete basic value from YANG.
void checkAndGetDivergingLeaf(isc::data::ElementPtr &storage, libyang::DataNode const &data_node, std::string const &name, std::string const &yang_name) const
Retrieves a child YANG data node identified by name from the given parent YANG container node and sto...
Definition translator.cc:56
void setItem(const std::string &xpath, isc::data::ConstElementPtr const elem, libyang::LeafBaseType const type)
Translate and set basic value from JSON to YANG.
std::string model_
The model.
Definition translator.h:449
void checkAndSetDivergingLeaf(isc::data::ConstElementPtr const &from, std::string const &xpath, std::string const &name, std::string const &yang_name, libyang::LeafBaseType const type)
Get an element from given ElementPtr node and set it in sysrepo at given xpath.
Definition translator.cc:88
bool schemaNodeExists(std::string const &xpath) const
Checks whether a YANG node exists in the schema.
void checkAndGetAndJsonifyLeaf(isc::data::ElementPtr &storage, libyang::DataNode const &data_node, const std::string &name) const
Retrieves a child YANG data node identified by name from the given parent YANG container node,...
Definition translator.cc:67
void checkAndSetLeafList(isc::data::ConstElementPtr const &from, std::string const &xpath, std::string const &name, libyang::LeafBaseType const type)
Get an element from given ElementPtr node and set it in sysrepo at given xpath as a leaf-list.
sysrepo::Session session_
The sysrepo session.
Definition translator.h:446
std::optional< libyang::DataNode > getData(std::string const &xpath) const
Get a YANG data node found at the given absolute xpath.
void checkAndStringifyAndSetLeaf(isc::data::ConstElementPtr const &from, std::string const &xpath, std::string const &name)
Get an element from given ElementPtr node and set it in sysrepo at given xpath.
static std::optional< std::string > translateToYang(isc::data::ConstElementPtr const &elem, libyang::LeafBaseType const type)
Translate basic value from JSON to YANG.
static Deserializer initializeDeserializer()
Initializes the deserializer which is used to translate a YANG node to an ElementPtr.
static std::string encode64(std::string const &input)
Encode a string such that it can be stored in a YANG element of binary type.
Translator(sysrepo::Session session, const std::string &model)
Constructor.
Definition translator.cc:27
#define isc_throw(type, stream)
A shortcut macro to insert known values into exception arguments.
boost::shared_ptr< const Element > ConstElementPtr
Definition data.h:30
boost::shared_ptr< Element > ElementPtr
Definition data.h:29
string encodeBase64(const vector< uint8_t > &binary)
Encode binary data in the base64 format.
Definition encode.cc:337
void decodeBase64(const std::string &encoded_str, std::vector< uint8_t > &output)
Decode a base64 encoded string into binary data.
Definition encode.cc:343
Defines the logger used by the top-level component of kea-lfc.
Missing node error.
Generic NETCONF error.