diff options
Diffstat (limited to '')
-rw-r--r-- | src/lib/yang/translator.h | 432 |
1 files changed, 432 insertions, 0 deletions
diff --git a/src/lib/yang/translator.h b/src/lib/yang/translator.h new file mode 100644 index 0000000..5bbaa7d --- /dev/null +++ b/src/lib/yang/translator.h @@ -0,0 +1,432 @@ +// Copyright (C) 2018-2022 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 <sysrepo-cpp/Session.hpp> + +#include <unordered_map> + +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 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 elem, + libyang::LeafBaseType 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 |