1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
// Copyright (C) 2018-2024 Internet Systems Consortium, Inc. ("ISC")
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

#ifndef ISC_TRANSLATOR_H
#define ISC_TRANSLATOR_H 1

#include <cc/data.h>
#include <yang/netconf_error.h>

#include <sysrepo-cpp/Connection.hpp><--- Include file:  not found. Please note: Cppcheck does not need standard library headers to get proper results.
#include <sysrepo-cpp/Enum.hpp><--- Include file:  not found. Please note: Cppcheck does not need standard library headers to get proper results.
#include <sysrepo-cpp/Session.hpp><--- Include file:  not found. Please note: Cppcheck does not need standard library headers to get proper results.

#include <unordered_map><--- Include file:  not found. Please note: Cppcheck does not need standard library headers to get proper results.

namespace isc {
namespace yang {

/// @brief Between YANG and JSON translator class for basic values.
class Translator {
public:
    /// @brief Constructor.
    ///
    /// @param session Sysrepo session.
    /// @param model Model name (used and shared by derived classes).
    Translator(sysrepo::Session session, const std::string& model);

    /// @brief Destructor.
    virtual ~Translator() = default;

    /// @brief Calls {translate} for the element found at {xpath} relative to
    /// {data_node} and sets the result in {storage} with the {xpath} key.
    ///
    /// @tparam T typename of the function to be called
    ///
    /// @param storage ElementMap where result will be stored
    /// @param data_node parent data node of container type
    /// @param xpath relative xpath to search by
    /// @param translate function to be called to translate a data node to an element pointer
    template <typename T>
    void checkAndGet(isc::data::ElementPtr const& storage,
                     libyang::DataNode const& data_node,
                     std::string const& xpath,
                     T translate) const {
        libyang::Set<libyang::DataNode> const& nodes(data_node.findXPath(xpath));
        if (!nodes.empty()) {
            isc::data::ElementPtr const& element(translate(nodes.front()));
            if (element && !element->empty()) {
                storage->set(xpath, element);
            }
        }
    }

    /// @brief Calls {translate} for the element found at {xpath} relative to
    /// {data_node} and sets the result in {storage} with the {key} key.
    ///
    /// It's the counterpart for @ref checkAndGet, but when the YANG xpath
    /// and the JSON key diverge.
    ///
    /// @tparam T typename of the function to be called
    ///
    /// @param storage ElementMap where result will be stored
    /// @param data_node parent data node of container type
    /// @param key where to set the result in the {storage} map
    /// @param xpath relative xpath to search by
    /// @param translate function to be called to translate a data node to an element pointer
    template <typename T>
    void checkAndGetDiverging(isc::data::ElementPtr const& storage,
                              libyang::DataNode const& data_node,
                              std::string const& key,
                              std::string const& xpath,
                              T translate) const {
        libyang::Set<libyang::DataNode> const& nodes(data_node.findXPath(xpath));
        if (!nodes.empty()) {
            isc::data::ElementPtr const& element(translate(nodes.front()));
            if (element && !element->empty()) {
                storage->set(key, element);
            }
        }
    }

    /// @brief Retrieves a child YANG data node identified by name from the
    /// given parent YANG container node and stores it in the specified storage.
    ///
    /// @param storage ElementMap where result will be stored
    /// @param data_node parent data node of container type
    /// @param name the name of the parameter to be set in storage
    void checkAndGetLeaf(isc::data::ElementPtr& storage,
                         libyang::DataNode const& data_node,
                         std::string const& name) const;

    /// @brief Retrieves a child YANG data node identified by name from the
    /// given parent YANG container node and stores it in the specified storage.
    ///
    /// It's the counterpart for @ref checkAndGetLeaf, but when the YANG xpath
    /// and the JSON key diverge.
    ///
    /// @param storage ElementMap where result will be stored
    /// @param data_node parent data node of container type
    /// @param name the name of the parameter to be set in storage
    /// @param yang_name the name by which to find the parameter in the YANG data node
    void checkAndGetDivergingLeaf(isc::data::ElementPtr& storage,
                                  libyang::DataNode const& data_node,
                                  std::string const& name,
                                  std::string const& yang_name) const;

    /// @brief Retrieves a child YANG data node identified by name from the given parent YANG
    /// container node, converts it from string to JSON and stores it in the specified storage.
    ///
    /// @param storage ElementMap where result will be stored
    /// @param data_node parent data node of container type
    /// @param name the name of the parameter to be set in storage
    void checkAndGetAndJsonifyLeaf(isc::data::ElementPtr& storage,
                                   libyang::DataNode const& data_node,
                                   const std::string& name) const;

    /// @brief Get an element from given ElementPtr node and set it in sysrepo
    /// at given xpath.
    ///
    /// @param from the parent configuration node from which to take the value
    /// @param xpath the xpath to the YANG node without the last node
    /// @param name the name of the YANG node which should also match the key
    /// under which data is set in the MapElement {from}
    /// @param type the YANG node type
    void checkAndSetLeaf(isc::data::ConstElementPtr const& from,
                         std::string const& xpath,
                         std::string const& name,
                         libyang::LeafBaseType const type);

    /// @brief Get an element from given ElementPtr node and set it in sysrepo
    /// at given xpath.
    ///
    /// It's the counterpart for @ref checkAndSetLeaf, but when the YANG xpath
    /// and the JSON key diverge.
    ///
    /// @param from the parent configuration node from which to take the value
    /// @param xpath the xpath to the YANG node without the last node
    /// @param name the key under which data is set in the MapElement {from}
    /// @param yang_name the name of the YANG node
    /// @param type the YANG node type
    void checkAndSetDivergingLeaf(isc::data::ConstElementPtr const& from,
                                  std::string const& xpath,
                                  std::string const& name,
                                  std::string const& yang_name,
                                  libyang::LeafBaseType const type);

    /// @brief Get an element from given ElementPtr node and set it in sysrepo
    /// at given xpath as a leaf-list.
    ///
    /// @param from the parent configuration node from which to take the values
    /// @param xpath the xpath to the YANG node without the last node
    /// @param name the name of the YANG node which should also match the key
    /// under which data is set in the MapElement {from}
    /// @param type the YANG node type of the underlying leaf-list nodes
    void checkAndSetLeafList(isc::data::ConstElementPtr const& from,
                             std::string const& xpath,
                             std::string const& name,
                             libyang::LeafBaseType const type);

    /// @brief Get an element from given ElementPtr node and set it in sysrepo
    /// at given xpath.
    ///
    /// @param from the parent configuration node from which to take the value
    /// @param xpath the xpath to the YANG node without the last node
    void checkAndSetUserContext(isc::data::ConstElementPtr const& from,
                                std::string const& xpath);

    /// @brief Get an element from given ElementPtr node and set it in sysrepo
    /// at given xpath.
    ///
    /// @param from the parent configuration node from which to take the value
    /// @param xpath the xpath to the YANG node without the last node
    /// @param name the name of the YANG node which should also match the key
    /// under which data is set in the MapElement {from}
    void checkAndStringifyAndSetLeaf(isc::data::ConstElementPtr const& from,
                                     std::string const& xpath,
                                     std::string const& name);

    /// @brief Delete basic value from YANG.
    ///
    /// @param xpath The xpath of the basic value.
    void deleteItem(const std::string& xpath);

    /// @brief Retrieves a YANG data node by xpath.
    ///
    /// @note This is a computationally expensive operation that makes a lookup in the sysrepo
    /// datastore by calling Session::getData(). It should be used sparingly in production code,
    /// mainly to get an initial data node to work with. It may be used at will in unit tests.
    ///
    /// @param xpath the xpath of the requested node
    ///
    /// @return the requested YANG data node
    ///
    /// @throw NetconfError if no YANG data node was found
    libyang::DataNode findXPath(std::string const& xpath) const;

    /// @brief Run a function for a node and all its children.
    ///
    /// @tparam functor_t typename of the function to be called
    ///
    /// @note This is a computationally expensive operation that makes a lookup
    /// in the sysrepo datastore by calling Session::getData(). It should be
    /// used sparingly in production code. It is primarily meant for unit tests.
    ///
    /// @param xpath the xpath of the root node belonging to the tree being traversed
    /// @param f the function to be called on the node itself and each descendant
    template <typename functor_t>
    void forAll(std::string const& xpath, functor_t f) const {
        std::optional<libyang::DataNode> const& data_node(session_.getData(xpath));
        if (!data_node) {
            return;
        }

        for (libyang::DataNode const& sibling : data_node->siblings()) {
            for (libyang::DataNode const& n : sibling.childrenDfs()) {
                f(n);
            }
        }
    }

    /// @brief Get a YANG data node found at the given absolute xpath.
    ///
    /// @note This is a computationally expensive operation that makes a lookup in the sysrepo
    /// datastore by calling Session::getData(). It should be used sparingly in production code,
    /// mainly to get an initial data node to work with. It may be used at will in unit tests.
    ///
    /// @param xpath the xpath at which the YANG data node is to be found
    ///
    /// @return a DataNode if found, or nullopt otherwise
    ///
    /// @throw NetconfError when the used sysrepo API throws an error
    std::optional<libyang::DataNode> getData(std::string const& xpath) const;

    /// @brief Translate a basic value from YANG to JSON for
    /// a given xpath that is relative to the given source node.
    ///
    /// @param data_node the source data node
    /// @param xpath the relative xpath
    ///
    /// @return The Element representing the item at xpath or null
    /// when not found.
    ///
    /// @throw NetconfError when sysrepo raises an error.
    isc::data::ElementPtr getItem(libyang::DataNode const& data_node,
                                  std::string const& xpath) const;

    /// @brief Translate a basic value from YANG to JSON for a given absolute xpath.
    ///
    /// @note This is a computationally expensive operation that makes a lookup in the sysrepo
    /// datastore by calling Session::getData(). It should be used sparingly in production code,
    /// mainly to get an initial data node to work with. It may be used at will in unit tests.
    ///
    /// @param xpath The xpath of the basic value.
    /// @return The Element representing the item at xpath or null
    /// when not found.
    /// @throw NetconfError when sysrepo raises an error.
    isc::data::ElementPtr getItemFromAbsoluteXpath(std::string const& xpath) const;

    /// @brief Retrieve a list as ElementPtr from sysrepo from a certain xpath.
    ///
    /// @tparam T typename of the translator that holds the function that will
    /// be called on the xpath of each child from the list
    ///
    /// @param data_node the YANG data node to retrieve data from
    /// @param xpath the xpath to the element to be retrieved from, relative to {data_node}
    /// @param t address of a translator instance that holds the function that
    /// will be called on the xpath of each child from the list
    /// @param f the function that will be called on the xpath of each child
    /// from the list
    template <typename T>
    isc::data::ElementPtr getList(libyang::DataNode const& data_node,
                                  std::string const& xpath,
                                  T& t,
                                  isc::data::ElementPtr (T::*f)(libyang::DataNode const&)) const {
        try {
            libyang::Set<libyang::DataNode> const& nodes(data_node.findXPath(xpath));
            if (nodes.empty()) {
                return (isc::data::ElementPtr());
            }
            isc::data::ElementPtr const result(isc::data::Element::createList());
            for (libyang::DataNode const& i : nodes) {
                result->add((t.*f)(i));
            }
            return result;
        } catch (libyang::Error const& ex) {
            isc_throw(NetconfError, "getting item: " << ex.what());
        }
    }

    /// @brief Retrieves a child YANG data node identified by name from the
    /// given parent YANG container node and stores it in the specified storage.
    ///
    /// Unlike @ref checkAndGetLeaf, the leaf is expected to be present.
    ///
    /// @param storage ElementMap where result will be stored
    /// @param data_node parent data node of container type
    /// @param name the name of the YANG node which should also match the map
    /// key in the JSON configuration
    ///
    /// @throw MissingNode if leaf is not found
    void getMandatoryLeaf(isc::data::ElementPtr& storage,
                          libyang::DataNode const& data_node,
                          std::string const& name) const;

    /// @brief Retrieves a child YANG data node identified by one name from the
    /// given parent YANG container node and stores it in the specified storage
    /// under a different name.
    ///
    /// Unlike @ref checkAndGetLeaf, the leaf is expected to be present.
    ///
    /// @param storage ElementMap where result will be stored
    /// @param data_node parent data node of container type
    /// @param name the map key in the JSON configuration
    /// @param yang_name the name by which to find the parameter in the YANG data node
    ///

    /// @throw MissingNode if leaf is not found
    void getMandatoryDivergingLeaf(isc::data::ElementPtr& storage,
                                   libyang::DataNode const& data_node,
                                   std::string const& name,
                                   std::string const& yang_name) const;

    /// @brief Checks whether a YANG node exists in the schema.
    ///
    /// @param xpath the xpath to be checked
    ///
    /// @return true if the YANG node exists in the schema, false otherwise
    bool schemaNodeExists(std::string const& xpath) const;

    /// @brief Translate and set basic value from JSON to YANG.
    ///
    /// @param xpath The xpath of the basic value.
    /// @param elem The JSON element.
    /// @param type The sysrepo type.
    void setItem(const std::string& xpath,
                 isc::data::ConstElementPtr const elem,
                 libyang::LeafBaseType const type);

    /// @brief Get an element from given ElementPtr node and set it in sysrepo
    /// at given xpath.
    ///
    /// @param from the parent configuration node from which to take the value
    /// @param xpath the xpath to the YANG node without the last node
    /// @param name the name of the YANG node which should also match the map
    /// key in the JSON configuration
    /// @param type the YANG node type
    void setMandatoryLeaf(isc::data::ConstElementPtr const& from,
                          std::string const& xpath,
                          std::string const& name,
                          libyang::LeafBaseType const type);

    /// @brief Get an element from given ElementPtr node and set it in sysrepo
    /// at given xpath.
    ///
    /// @param from the parent configuration node from which to take the value
    /// @param xpath the xpath to the YANG node without the last node
    /// @param name the map key in the JSON configuration
    /// @param yang_name the name by which to find the parameter in the YANG data node
    /// @param type the YANG node type
    void setMandatoryDivergingLeaf(isc::data::ConstElementPtr const& from,
                                   std::string const& xpath,
                                   std::string const& name,
                                   std::string const& yang_name,
                                   libyang::LeafBaseType const type);

    /// @brief Translate basic value from the given YANG data node to JSON element.
    ///
    /// @param data_node the YANG data node
    ///
    /// @return the translated JSON element
    static isc::data::ElementPtr translateFromYang(std::optional<libyang::DataNode> data_node);

    /// @brief Translate basic value from JSON to YANG.
    ///
    /// @param elem The JSON element.
    /// @param type The sysrepo type.
    ///
    /// @return string representation of {elem}, or nullopt if {elem} is null
    static std::optional<std::string> translateToYang(isc::data::ConstElementPtr const& elem,
                                                      libyang::LeafBaseType const type);

protected:
    /// @brief Decode a YANG element of binary type to a string that
    /// can be stored in an Element::string JSON.
    ///
    /// @param input string to be decoded
    ///
    /// @return the decoded string
    static std::string decode64(std::string const& input);

    /// @brief Encode a string such that it can be stored in a YANG element of binary type.
    ///
    /// @param input string to be encoded
    ///
    /// @return the encoded string
    static std::string encode64(std::string const& input);

    /// @brief Maps YANG types to functions that transform a YANG type into an ElementPtr.
    using Deserializer =
        std::unordered_map<libyang::LeafBaseType,
                           std::function<isc::data::ElementPtr const(std::string const&)>>;

    /// @brief Initializes the deserializer which is used to translate a YANG node to an ElementPtr.
    ///
    /// @return a copy of the deserializer
    static Deserializer initializeDeserializer();

    /// @brief Maps YANG types to functions that transform the string representation of an
    /// Element into a string that can be passed into Session::setItem().
    using Serializer =
        std::unordered_map<libyang::LeafBaseType,
                           std::function<std::string const(std::string const&)>>;

    /// @brief Initializes the serializer which is used to translate the string value of an Element
    /// to a string that can be passed into Session::setItem().
    ///
    /// @return a copy of the serializer
    static Serializer initializeSerializer();

    /// @brief The sysrepo session.
    sysrepo::Session session_;

    /// @brief The model.
    std::string model_;
};  // Translator

}  // namespace yang
}  // namespace isc

#endif  // ISC_TRANSLATOR_H