Migrate to libyang2

libnetconf2: getSchema and getConfig were no longer used in netconf-cli,
so I deleted them. They can get readded once the bindings get split into
a separate project.

sysrepo_access: Some sr_val stuff was removed.

YangSchema: type descriptions are not available
            availableNodes returns only input nodes for RPC nodes
            impl_getSchemaNode: no longer disables error printing

libyang: No longer supports leafrefs without the leaf it points to.

Depends-on: https://cesnet-gerrit-czechlight/c/CzechLight/dependencies/+/5171
Depends-on: https://gerrit.cesnet.cz/c/CzechLight/dependencies/+/5171
Change-Id: Ie49381a003a61a7bb028be7b2fa1d9d926ac4e58
diff --git a/src/yang_schema.cpp b/src/yang_schema.cpp
index b964d31..f96aa46 100644
--- a/src/yang_schema.cpp
+++ b/src/yang_schema.cpp
@@ -6,9 +6,8 @@
  *
 */
 
-#include <libyang/Libyang.hpp>
-#include <libyang/Tree_Data.hpp>
-#include <libyang/Tree_Schema.hpp>
+#include <libyang-cpp/Enum.hpp>
+#include <libyang-cpp/Utils.hpp>
 #include <string_view>
 #include "UniqueResource.hpp"
 #include "utils.hpp"
@@ -33,11 +32,11 @@
 };
 
 YangSchema::YangSchema()
-    : m_context(std::make_shared<libyang::Context>(nullptr, LY_CTX_DISABLE_SEARCHDIR_CWD))
+    : m_context(nullptr, libyang::ContextOptions::DisableSearchDirs | libyang::ContextOptions::SetPrivParsed)
 {
 }
 
-YangSchema::YangSchema(std::shared_ptr<libyang::Context> lyCtx)
+YangSchema::YangSchema(libyang::Context lyCtx)
     : m_context(lyCtx)
 {
 }
@@ -46,29 +45,22 @@
 
 void YangSchema::addSchemaString(const char* schema)
 {
-    if (!m_context->parse_module_mem(schema, LYS_IN_YANG)) {
-        throw YangLoadError("Couldn't load schema");
-    }
+    m_context.parseModuleMem(schema, libyang::SchemaFormat::YANG);
 }
 
 void YangSchema::addSchemaDirectory(const char* directoryName)
 {
-    if (m_context->set_searchdir(directoryName)) {
-        throw YangLoadError("Couldn't add schema search directory");
-    }
+    m_context.setSearchDir(directoryName);
 }
 
 void YangSchema::addSchemaFile(const char* filename)
 {
-    if (!m_context->parse_module_path(filename, LYS_IN_YANG)) {
-        throw YangLoadError("Couldn't load schema");
-    }
+    m_context.parseModulePath(filename, libyang::SchemaFormat::YANG);
 }
 
 bool YangSchema::isModule(const std::string& name) const
 {
-    const auto set = modules();
-    return set.find(name) != set.end();
+    return m_context.getModuleImplemented(name.c_str()).has_value();
 }
 
 bool YangSchema::listHasKey(const schemaPath_& listPath, const std::string& key) const
@@ -80,49 +72,52 @@
 bool YangSchema::leafIsKey(const std::string& leafPath) const
 {
     auto node = getSchemaNode(leafPath);
-    if (!node || node->nodetype() != LYS_LEAF) {
+    if (!node || node->nodeType() != libyang::NodeType::Leaf) {
         return false;
     }
 
-    return libyang::Schema_Node_Leaf{node}.is_key().get();
+    return node->asLeaf().isKey();
 }
 
-libyang::S_Schema_Node YangSchema::impl_getSchemaNode(const std::string& node) const
+std::optional<libyang::SchemaNode> YangSchema::impl_getSchemaNode(const std::string& node) const
 {
-    // If no node is found find_path prints an error message, so we have to
-    // disable logging
-    // https://github.com/CESNET/libyang/issues/753
-    {
-        int oldOptions;
-        auto logBlocker = make_unique_resource(
-            [&oldOptions]() {
-                oldOptions = libyang::set_log_options(0);
-            },
-            [&oldOptions]() {
-                libyang::set_log_options(oldOptions);
-            });
-        auto res = m_context->get_node(nullptr, node.c_str());
-        if (!res) { // If no node is found, try output rpc nodes too.
-            res = m_context->get_node(nullptr, node.c_str(), 1);
+    // libyang::Context::findPath throws an exception, when no matching schema node is found. This exception has the
+    // ValidationFailure error code. We will catch that exception (and rethrow if it's not the correct error code.
+    //
+    // Also, we need to use findPath twice if we're trying to find output nodes.
+    try {
+        return m_context.findPath(node.c_str());
+    } catch (libyang::ErrorWithCode& err) {
+        if (err.code() != libyang::ErrorCode::ValidationFailure) {
+            throw;
         }
-        return res;
     }
+    try {
+        return m_context.findPath(node.c_str(), libyang::OutputNodes::Yes);
+    } catch (libyang::ErrorWithCode& err) {
+        if (err.code() != libyang::ErrorCode::ValidationFailure) {
+            throw;
+        }
+    }
+
+    // We didn't find a matching node.
+    return std::nullopt;
 }
 
 
-libyang::S_Schema_Node YangSchema::getSchemaNode(const std::string& node) const
+std::optional<libyang::SchemaNode> YangSchema::getSchemaNode(const std::string& node) const
 {
     return impl_getSchemaNode(node);
 }
 
-libyang::S_Schema_Node YangSchema::getSchemaNode(const schemaPath_& location, const ModuleNodePair& node) const
+std::optional<libyang::SchemaNode> YangSchema::getSchemaNode(const schemaPath_& location, const ModuleNodePair& node) const
 {
     std::string absPath = joinPaths(pathToSchemaString(location, Prefixes::Always), fullNodeName(location, node));
 
     return impl_getSchemaNode(absPath);
 }
 
-libyang::S_Schema_Node YangSchema::getSchemaNode(const schemaPath_& listPath) const
+std::optional<libyang::SchemaNode> YangSchema::getSchemaNode(const schemaPath_& listPath) const
 {
     std::string absPath = pathToSchemaString(listPath, Prefixes::Always);
     return impl_getSchemaNode(absPath);
@@ -131,174 +126,121 @@
 const std::set<std::string> YangSchema::listKeys(const schemaPath_& listPath) const
 {
     auto node = getSchemaNode(listPath);
-    if (node->nodetype() != LYS_LIST) {
+    if (node->nodeType() != libyang::NodeType::List) {
         return {};
     }
 
-    auto list = std::make_shared<libyang::Schema_Node_List>(node);
     std::set<std::string> keys;
-    const auto& keysVec = list->keys();
+    auto keysVec = node->asList().keys();
 
-    std::transform(keysVec.begin(), keysVec.end(), std::inserter(keys, keys.begin()), [](const auto& it) { return it->name(); });
+    std::transform(keysVec.begin(), keysVec.end(), std::inserter(keys, keys.begin()), [](const auto& it) { return std::string{it.name()}; });
     return keys;
 }
 
-namespace {
-enum class ResolveMode {
-    Enum,
-    Identity
-};
-/** @brief Resolves a typedef to a type which defines values.
- * When we need allowed values of a type and that type is a typedef, we need to recurse into the typedef until we find a
- * type which defines values. These values are the allowed values.
- * Example:
- *
- * typedef MyOtherEnum {
- *   type enumeration {
- *     enum "A";
- *     enum "B";
- *   }
- * }
- *
- * typedef MyEnum {
- *   type MyOtherEnum;
- * }
- *
- * If `toResolve` points to MyEnum, then just doing ->enums()->enm() returns nothing and that means that this particular
- * typedef (MyEnum) did not say which values are allowed. So, we need to dive into the parent enum (MyOtherEnum) with
- * ->der()->type(). This typedef (MyOtherEnum) DID specify allowed values and enums()->enm() WILL contain them. These
- *  values are the only relevant values and we don't care about other parent typedefs. We return these values to the
- *  caller.
- *
- *  For enums, this function simply returns all allowed enums.
- *  For identities, this function returns which bases `toResolve` has.
- */
-template <ResolveMode TYPE>
-auto resolveTypedef(const libyang::S_Type& toResolve)
+std::set<enum_> enumValues(const libyang::Type& type)
 {
-    auto type = toResolve;
-    auto getValuesFromType = [] (const libyang::S_Type& type) {
-        if constexpr (TYPE == ResolveMode::Identity) {
-            return type->info()->ident()->ref();
-        } else {
-            return type->info()->enums()->enm();
-        }
-    };
-    auto values = getValuesFromType(type);
-    while (values.empty()) {
-        type = type->der()->type();
-        values = getValuesFromType(type);
-    }
-
-    return values;
-}
-
-std::set<enum_> enumValues(const libyang::S_Type& type)
-{
-    auto values = resolveTypedef<ResolveMode::Enum>(type);
-
-    std::vector<libyang::S_Type_Enum> enabled;
-    std::copy_if(values.begin(), values.end(), std::back_inserter(enabled), [](const libyang::S_Type_Enum& it) {
-        auto iffeatures = it->iffeature();
-        return std::all_of(iffeatures.begin(), iffeatures.end(), [](auto it) { return it->value(); });
-    });
-
+    auto enums = type.asEnum().items();
     std::set<enum_> enumSet;
-    std::transform(enabled.begin(), enabled.end(), std::inserter(enumSet, enumSet.end()), [](auto it) { return enum_{it->name()}; });
+    std::transform(enums.begin(), enums.end(), std::inserter(enumSet, enumSet.end()), [](auto it) { return enum_{std::string{it.name}}; });
     return enumSet;
 }
 
-std::set<identityRef_> validIdentities(const libyang::S_Type& type)
+std::set<identityRef_> validIdentities(const libyang::Type& type)
 {
     std::set<identityRef_> identSet;
 
-    for (auto base : resolveTypedef<ResolveMode::Identity>(type)) { // Iterate over all bases
-        // Iterate over derived identities (this is recursive!)
-        if (auto der = base->der()) {
-            for (auto derived : der->schema()) {
-                identSet.emplace(derived->module()->name(), derived->name());
-            }
+    std::function<void(const std::vector<libyang::Identity>&)> impl = [&identSet, &impl] (const std::vector<libyang::Identity>& idents) {
+        if (idents.empty()) {
+            return;
         }
+
+        for (const auto& ident : idents) {
+            identSet.emplace(std::string{ident.module().name()}, std::string{ident.name()});
+            impl(ident.derived());
+        }
+    };
+
+    for (const auto& base : type.asIdentityRef().bases()) {
+        impl(base.derived());
     }
 
     return identSet;
 }
 
-std::string leafrefPath(const libyang::S_Type& type)
+std::string leafrefPath(const libyang::Type& type)
 {
-    return type->info()->lref()->target()->path(LYS_PATH_FIRST_PREFIX);
-}
+    return std::string{type.asLeafRef().path()};
 }
 
 template <typename NodeType>
-yang::TypeInfo YangSchema::impl_leafType(const libyang::S_Schema_Node& node) const
+yang::TypeInfo YangSchema::impl_leafType(const NodeType& node) const
 {
     using namespace std::string_literals;
     auto leaf = std::make_shared<NodeType>(node);
     auto leafUnits = leaf->units();
-    std::function<yang::TypeInfo(std::shared_ptr<libyang::Type>)> resolveType;
-    resolveType = [this, &resolveType, leaf, leafUnits](std::shared_ptr<libyang::Type> type) -> yang::TypeInfo {
+    std::function<yang::TypeInfo(const libyang::Type&)> resolveType;
+    resolveType = [&resolveType, leaf, leafUnits](const libyang::Type& type) -> yang::TypeInfo {
         yang::LeafDataType resType;
-        switch (type->base()) {
-        case LY_TYPE_STRING:
+        switch (type.base()) {
+        case libyang::LeafBaseType::String:
             resType.emplace<yang::String>();
             break;
-        case LY_TYPE_DEC64:
+        case libyang::LeafBaseType::Dec64:
             resType.emplace<yang::Decimal>();
             break;
-        case LY_TYPE_BOOL:
+        case libyang::LeafBaseType::Bool:
             resType.emplace<yang::Bool>();
             break;
-        case LY_TYPE_INT8:
+        case libyang::LeafBaseType::Int8:
             resType.emplace<yang::Int8>();
             break;
-        case LY_TYPE_INT16:
+        case libyang::LeafBaseType::Int16:
             resType.emplace<yang::Int16>();
             break;
-        case LY_TYPE_INT32:
+        case libyang::LeafBaseType::Int32:
             resType.emplace<yang::Int32>();
             break;
-        case LY_TYPE_INT64:
+        case libyang::LeafBaseType::Int64:
             resType.emplace<yang::Int64>();
             break;
-        case LY_TYPE_UINT8:
+        case libyang::LeafBaseType::Uint8:
             resType.emplace<yang::Uint8>();
             break;
-        case LY_TYPE_UINT16:
+        case libyang::LeafBaseType::Uint16:
             resType.emplace<yang::Uint16>();
             break;
-        case LY_TYPE_UINT32:
+        case libyang::LeafBaseType::Uint32:
             resType.emplace<yang::Uint32>();
             break;
-        case LY_TYPE_UINT64:
+        case libyang::LeafBaseType::Uint64:
             resType.emplace<yang::Uint64>();
             break;
-        case LY_TYPE_BINARY:
+        case libyang::LeafBaseType::Binary:
             resType.emplace<yang::Binary>();
             break;
-        case LY_TYPE_EMPTY:
+        case libyang::LeafBaseType::Empty:
             resType.emplace<yang::Empty>();
             break;
-        case LY_TYPE_ENUM:
+        case libyang::LeafBaseType::Enum:
             resType.emplace<yang::Enum>(enumValues(type));
             break;
-        case LY_TYPE_IDENT:
+        case libyang::LeafBaseType::IdentityRef:
             resType.emplace<yang::IdentityRef>(validIdentities(type));
             break;
-        case LY_TYPE_LEAFREF:
-            resType.emplace<yang::LeafRef>(::leafrefPath(type), std::make_unique<yang::TypeInfo>(leafType(::leafrefPath(type))));
+        case libyang::LeafBaseType::Leafref:
+            resType.emplace<yang::LeafRef>(::leafrefPath(type), std::make_unique<yang::TypeInfo>(resolveType(type.asLeafRef().resolvedType())));
             break;
-        case LY_TYPE_BITS: {
+        case libyang::LeafBaseType::Bits: {
             auto resBits = yang::Bits{};
-            for (const auto& bit : type->info()->bits()->bit()) {
-                resBits.m_allowedValues.emplace(bit->name());
+            for (const auto& bit : type.asBits().items()) {
+                resBits.m_allowedValues.emplace(std::string{bit.name});
             }
             resType.emplace<yang::Bits>(std::move(resBits));
             break;
         }
-        case LY_TYPE_UNION: {
+        case libyang::LeafBaseType::Union: {
             auto resUnion = yang::Union{};
-            for (auto unionType : type->info()->uni()->types()) {
+            for (auto unionType : type.asUnion().types()) {
                 resUnion.m_unionTypes.emplace_back(resolveType(unionType));
             }
             resType.emplace<yang::Union>(std::move(resUnion));
@@ -306,48 +248,26 @@
         }
         default:
             using namespace std::string_literals;
-            throw UnsupportedYangTypeException("the type of "s + leaf->name() + " is not supported: " + std::to_string(leaf->type()->base()));
+            throw UnsupportedYangTypeException("the type of "s +
+                    std::string{leaf->name()} +
+                    " is not supported: " +
+                    std::to_string(std::underlying_type_t<libyang::LeafBaseType>(leaf->valueType().base())));
         }
 
-        std::optional<std::string> resUnits;
 
-        if (leafUnits) {
-            resUnits = leafUnits;
-        } else {
-            for (auto parentTypedef = type->der(); parentTypedef; parentTypedef = parentTypedef->type()->der()) {
-                auto units = parentTypedef->units();
-                if (units) {
-                    resUnits = units;
-                    break;
-                }
-            }
-        }
-
-        std::optional<std::string> resDescription;
-
-        // checking for parentTypedef->type()->der() means I'm going to enter inside base types like "string". These
-        // also have a description, but it isn't too helpful ("human-readable string")
-        for (auto parentTypedef = type->der(); parentTypedef && parentTypedef->type()->der(); parentTypedef = parentTypedef->type()->der()) {
-            auto dsc = parentTypedef->dsc();
-            if (dsc) {
-                resDescription = dsc;
-                break;
-            }
-        }
-
-        return yang::TypeInfo(resType, resUnits, resDescription);
+        return yang::TypeInfo(resType, std::optional<std::string>{leafUnits}, std::optional<std::string>{type.description()});
     };
-    return resolveType(leaf->type());
+    return resolveType(leaf->valueType());
 }
 
 yang::TypeInfo YangSchema::leafType(const schemaPath_& location, const ModuleNodePair& node) const
 {
     auto lyNode = getSchemaNode(location, node);
-    switch (lyNode->nodetype()) {
-    case LYS_LEAF:
-        return impl_leafType<libyang::Schema_Node_Leaf>(lyNode);
-    case LYS_LEAFLIST:
-        return impl_leafType<libyang::Schema_Node_Leaflist>(lyNode);
+    switch (lyNode->nodeType()) {
+    case libyang::NodeType::Leaf:
+        return impl_leafType(lyNode->asLeaf());
+    case libyang::NodeType::Leaflist:
+        return impl_leafType(lyNode->asLeafList());
     default:
         throw std::logic_error("YangSchema::leafType: type must be leaf or leaflist");
     }
@@ -356,11 +276,11 @@
 yang::TypeInfo YangSchema::leafType(const std::string& path) const
 {
     auto lyNode = getSchemaNode(path);
-    switch (lyNode->nodetype()) {
-    case LYS_LEAF:
-        return impl_leafType<libyang::Schema_Node_Leaf>(lyNode);
-    case LYS_LEAFLIST:
-        return impl_leafType<libyang::Schema_Node_Leaflist>(lyNode);
+    switch (lyNode->nodeType()) {
+    case libyang::NodeType::Leaf:
+        return impl_leafType(lyNode->asLeaf());
+    case libyang::NodeType::Leaflist:
+        return impl_leafType(lyNode->asLeafList());
     default:
         throw std::logic_error("YangSchema::leafType: type must be leaf or leaflist");
     }
@@ -368,23 +288,22 @@
 
 std::optional<std::string> YangSchema::leafTypeName(const std::string& path) const
 {
-    libyang::Schema_Node_Leaf leaf(getSchemaNode(path));
-    return leaf.type()->der().get() && leaf.type()->der()->type()->der().get() ? std::optional{leaf.type()->der()->name()} : std::nullopt;
+    auto leaf = getSchemaNode(path)->asLeaf();
+    return std::string{leaf.valueType().name()};
 }
 
 std::string YangSchema::leafrefPath(const std::string& leafrefPath) const
 {
     using namespace std::string_literals;
-    libyang::Schema_Node_Leaf leaf(getSchemaNode(leafrefPath));
-    return leaf.type()->info()->lref()->target()->path(LYS_PATH_FIRST_PREFIX);
+    return ::leafrefPath(getSchemaNode(leafrefPath)->asLeaf().valueType());
 }
 
 std::set<std::string> YangSchema::modules() const
 {
-    const auto& modules = m_context->get_module_iter();
+    const auto& modules = m_context.modules();
 
     std::set<std::string> res;
-    std::transform(modules.begin(), modules.end(), std::inserter(res, res.end()), [](const auto module) { return module->name(); });
+    std::transform(modules.begin(), modules.end(), std::inserter(res, res.end()), [](const auto module) { return std::string{module.name()}; });
     return res;
 }
 
@@ -392,39 +311,49 @@
 {
     using namespace std::string_view_literals;
     std::set<ModuleNodePair> res;
-    std::vector<libyang::S_Schema_Node> nodes;
+    std::vector<libyang::ChildInstanstiables> nodeCollections;
     std::string topLevelModule;
 
     if (path.type() == typeid(module_)) {
-        nodes = m_context->get_module(boost::get<module_>(path).m_name.c_str())->data_instantiables(0);
+        nodeCollections.emplace_back(m_context.getModule(boost::get<module_>(path).m_name.c_str())->childInstantiables());
     } else {
         auto schemaPath = anyPathToSchemaPath(path);
         if (schemaPath.m_nodes.empty()) {
-            nodes = m_context->data_instantiables(0);
+            for (const auto& module : m_context.modules()) {
+                if (module.implemented()) {
+                    nodeCollections.emplace_back(module.childInstantiables());
+                }
+            }
         } else {
             const auto pathString = pathToSchemaString(schemaPath, Prefixes::Always);
             const auto node = getSchemaNode(pathString);
-            nodes = node->child_instantiables(0);
+            nodeCollections.emplace_back(node->childInstantiables());
             topLevelModule = schemaPath.m_nodes.begin()->m_prefix->m_name;
         }
     }
 
-    for (const auto& node : nodes) {
-        if (node->module()->name() == "ietf-yang-library"sv) {
-            continue;
-        }
+    for (const auto& coll : nodeCollections) {
+        for (const auto& node : coll) {
+            if (node.module().name() == "ietf-yang-library"sv) {
+                continue;
+            }
 
-        if (recursion == Recursion::Recursive) {
-            for (auto it : node->tree_dfs()) {
-                res.insert(ModuleNodePair(boost::none, it->path(LYS_PATH_FIRST_PREFIX)));
+            if (node.module().name() == "ietf-yang-schema-mount"sv) {
+                continue;
             }
-        } else {
-            ModuleNodePair toInsert;
-            if (topLevelModule.empty() || topLevelModule != node->module()->name()) {
-                toInsert.first = node->module()->type() == 0 ? node->module()->name() : libyang::Submodule(node->module()).belongsto()->name();
+
+            if (recursion == Recursion::Recursive) {
+                for (auto it : node.childrenDfs()) {
+                    res.insert(ModuleNodePair(boost::none, it.path()));
+                }
+            } else {
+                ModuleNodePair toInsert;
+                if (topLevelModule.empty() || topLevelModule != node.module().name()) {
+                    toInsert.first = std::string{node.module().name()};
+                }
+                toInsert.second = node.name();
+                res.insert(toInsert);
             }
-            toInsert.second = node->name();
-            res.insert(toInsert);
         }
     }
 
@@ -433,71 +362,81 @@
 
 void YangSchema::loadModule(const std::string& moduleName)
 {
-    m_context->load_module(moduleName.c_str());
+    m_context.loadModule(moduleName.c_str());
 }
 
-void YangSchema::enableFeature(const std::string& moduleName, const std::string& featureName)
+void YangSchema::setEnabledFeatures(const std::string& moduleName, const std::vector<std::string>& features)
 {
     using namespace std::string_literals;
     auto module = getYangModule(moduleName);
     if (!module) {
         throw std::runtime_error("Module \""s + moduleName + "\" doesn't exist.");
     }
-    if (module->feature_enable(featureName.c_str())) {
-        throw std::runtime_error("Can't enable feature \""s + featureName + "\" for module \"" + moduleName + "\".");
+    try {
+        module->setImplemented(features);
+    } catch (libyang::ErrorWithCode&) {
+        throw std::runtime_error("Can't enable features for module \"" + moduleName + "\".");
     }
 }
 
 void YangSchema::registerModuleCallback(const std::function<std::string(const char*, const char*, const char*, const char*)>& clb)
 {
-    auto lambda = [clb](const char* mod_name, const char* mod_revision, const char* submod_name, const char* submod_revision) {
+    auto lambda = [clb](const char* mod_name, const char* mod_revision, const char* submod_name, const char* submod_revision) -> std::optional<libyang::ModuleInfo> {
         (void)submod_revision;
         auto moduleSource = clb(mod_name, mod_revision, submod_name, submod_revision);
         if (moduleSource.empty()) {
-            return libyang::Context::mod_missing_cb_return{LYS_IN_YANG, nullptr};
+            return std::nullopt;
         }
-        return libyang::Context::mod_missing_cb_return{LYS_IN_YANG, strdup(moduleSource.c_str())};
+        return libyang::ModuleInfo {
+            .data = moduleSource.c_str(),
+            .format = libyang::SchemaFormat::YANG
+
+        };
     };
 
-    m_context->add_missing_module_callback(lambda, free);
+    m_context.registerModuleCallback(lambda);
 }
 
-std::shared_ptr<libyang::Data_Node> YangSchema::dataNodeFromPath(const std::string& path, const std::optional<const std::string> value) const
+libyang::CreatedNodes YangSchema::dataNodeFromPath(const std::string& path, const std::optional<const std::string> value) const
 {
-    return std::make_shared<libyang::Data_Node>(m_context,
-                                                path.c_str(),
-                                                value ? value.value().c_str() : nullptr,
-                                                LYD_ANYDATA_CONSTSTRING,
-                                                LYD_PATH_OPT_EDIT);
+    auto options = [this, &path, &value] {
+        // If we're creating a node without a value and it's not the "empty" type, then we also need the Opaque flag.
+        auto schema = getSchemaNode(path);
+        if (schema->nodeType() == libyang::NodeType::Leaf &&
+            schema->asLeaf().valueType().base() != libyang::LeafBaseType::Empty &&
+            !value) {
+            return std::optional<libyang::CreationOptions>{libyang::CreationOptions::Opaque};
+        }
+
+        return std::optional<libyang::CreationOptions>{};
+    }();
+    return m_context.newPath2(path.c_str(), value ? value->c_str() : nullptr, options);
 }
 
-std::shared_ptr<libyang::Module> YangSchema::getYangModule(const std::string& name)
+std::optional<libyang::Module> YangSchema::getYangModule(const std::string& name)
 {
-    return m_context->get_module(name.c_str());
+    return m_context.getModuleImplemented(name.c_str());
 }
 
 namespace {
-yang::NodeTypes impl_nodeType(const libyang::S_Schema_Node& node)
+yang::NodeTypes impl_nodeType(const libyang::SchemaNode& node)
 {
-    if (!node) {
-        throw InvalidNodeException();
-    }
-    switch (node->nodetype()) {
-    case LYS_CONTAINER:
-        return libyang::Schema_Node_Container{node}.presence() ? yang::NodeTypes::PresenceContainer : yang::NodeTypes::Container;
-    case LYS_LEAF:
+    switch (node.nodeType()) {
+    case libyang::NodeType::Container:
+        return node.asContainer().isPresence() ? yang::NodeTypes::PresenceContainer : yang::NodeTypes::Container;
+    case libyang::NodeType::Leaf:
         return yang::NodeTypes::Leaf;
-    case LYS_LIST:
+    case libyang::NodeType::List:
         return yang::NodeTypes::List;
-    case LYS_RPC:
+    case libyang::NodeType::RPC:
         return yang::NodeTypes::Rpc;
-    case LYS_ACTION:
+    case libyang::NodeType::Action:
         return yang::NodeTypes::Action;
-    case LYS_NOTIF:
+    case libyang::NodeType::Notification:
         return yang::NodeTypes::Notification;
-    case LYS_ANYXML:
+    case libyang::NodeType::AnyXML:
         return yang::NodeTypes::AnyXml;
-    case LYS_LEAFLIST:
+    case libyang::NodeType::Leaflist:
         return yang::NodeTypes::LeafList;
     default:
         throw InvalidNodeException(); // FIXME: Implement all types.
@@ -507,56 +446,57 @@
 
 yang::NodeTypes YangSchema::nodeType(const schemaPath_& location, const ModuleNodePair& node) const
 {
-    return impl_nodeType(getSchemaNode(location, node));
+    return impl_nodeType(*getSchemaNode(location, node));
 }
 
 yang::NodeTypes YangSchema::nodeType(const std::string& path) const
 {
-    return impl_nodeType(getSchemaNode(path));
+    return impl_nodeType(*getSchemaNode(path));
 }
 
 std::optional<std::string> YangSchema::description(const std::string& path) const
 {
-    auto node = getSchemaNode(path.c_str());
-    return node->dsc() ? std::optional{node->dsc()} : std::nullopt;
+    auto desc = getSchemaNode(path.c_str())->description();
+    return desc ? std::optional<std::string>{desc} : std::nullopt;
+
 }
 
 yang::Status YangSchema::status(const std::string& location) const
 {
     auto node = getSchemaNode(location.c_str());
-    if (node->flags() & LYS_STATUS_DEPRC) {
+    switch (node->status()) {
+    case libyang::Status::Deprecated:
         return yang::Status::Deprecated;
-    } else if (node->flags() & LYS_STATUS_OBSLT) {
+    case libyang::Status::Obsolete:
         return yang::Status::Obsolete;
-    } else {
+    case libyang::Status::Current:
         return yang::Status::Current;
     }
+
+    __builtin_unreachable();
 }
 
 bool YangSchema::hasInputNodes(const std::string& path) const
 {
     auto node = getSchemaNode(path.c_str());
-    if (auto type = node->nodetype(); type != LYS_ACTION && type != LYS_RPC) {
+    if (auto type = node->nodeType(); type != libyang::NodeType::Action && type != libyang::NodeType::RPC) {
         throw std::logic_error("StaticSchema::hasInputNodes called with non-RPC/action path");
     }
 
     // The first child gives the /input node and then I check whether it has a child.
-    return node->child()->child().get();
+    return node->child()->child().has_value();
 }
 
 bool YangSchema::isConfig(const std::string& path) const
 {
     auto node = getSchemaNode(path.c_str());
-    if (node->flags() & LYS_CONFIG_W) {
-        return true;
-    }
-
-    // Node can still be an input node.
-    while (node->parent()) {
-        node = node->parent();
-        if (node->nodetype() == LYS_INPUT) {
+    try {
+        if (node->config() == libyang::Config::True) {
             return true;
         }
+    } catch (libyang::Error&) {
+        // For non-data nodes (like `rpc`), the config value can't be retrieved. In this case, we'll just default to
+        // "false".
     }
 
     return false;
@@ -564,22 +504,10 @@
 
 std::optional<std::string> YangSchema::defaultValue(const std::string& leafPath) const
 {
-    libyang::Schema_Node_Leaf leaf(getSchemaNode(leafPath));
-
-    if (auto leafDefault = leaf.dflt()) {
-        return leafDefault;
-    }
-
-    for (auto type = leaf.type()->der(); type != nullptr; type = type->type()->der()) {
-        if (auto defaultValue = type->dflt()) {
-            return defaultValue;
-        }
-    }
-
-    return std::nullopt;
+    return std::optional<std::string>{getSchemaNode(leafPath)->asLeaf().defaultValueStr()};
 }
 
 std::string YangSchema::dataPathToSchemaPath(const std::string& path)
 {
-    return getSchemaNode(path)->path(LYS_PATH_FIRST_PREFIX);
+    return std::string{getSchemaNode(path)->path()};
 }