Merge changes 1870 and 1923

Change-Id: I259729999f5935368b05a9310535c6f294a5c7db
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 8d4ee5b..8de23df 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -236,7 +236,7 @@
     cli_test(list_manipulation)
     cli_test(parser_methods)
     datastore_test(sysrepo sysrepo.cpp ${CMAKE_CURRENT_SOURCE_DIR}/example-schema.yang)
-    target_link_libraries(test_sysrepo sysrepoaccess yangschema)
+    target_link_libraries(test_sysrepo sysrepoaccess yangschema parser)
     datastore_test(netconf sysrepo.cpp ${CMAKE_CURRENT_SOURCE_DIR}/example-schema.yang)
     add_test(NAME start_daemons COMMAND ${FAKEROOT_EXECUTABLE} ${CMAKE_CURRENT_BINARY_DIR}/start_daemons.sh)
     add_test(NAME setup_netopeer COMMAND ${SYSREPOCFG_EXECUTABLE} ietf-netconf-server -i ${CMAKE_CURRENT_SOURCE_DIR}/netopeer-test-config --datastore=startup --format=xml)
diff --git a/src/ast_commands.hpp b/src/ast_commands.hpp
index 5f8f052..c5ffb61 100644
--- a/src/ast_commands.hpp
+++ b/src/ast_commands.hpp
@@ -50,7 +50,7 @@
         /> ls /module:node)";
     bool operator==(const ls_& b) const;
     std::vector<LsOption> m_options;
-    boost::optional<boost::variant<dataPath_, schemaPath_>> m_path;
+    boost::optional<boost::variant<boost::variant<dataPath_, schemaPath_>, module_>> m_path;
 };
 
 struct cd_ : x3::position_tagged {
@@ -141,7 +141,7 @@
         /> get
         /> get /module:path)";
     bool operator==(const get_& b) const;
-    boost::optional<boost::variant<dataPath_, schemaPath_>> m_path;
+    boost::optional<boost::variant<boost::variant<dataPath_, schemaPath_>, module_>> m_path;
 };
 
 struct help_;
diff --git a/src/grammars.hpp b/src/grammars.hpp
index 2c511ad..4fd7b15 100644
--- a/src/grammars.hpp
+++ b/src/grammars.hpp
@@ -305,7 +305,7 @@
     x3::eps;
 
 auto const ls_def =
-    ls_::name >> *(space_separator >> ls_options) >> -(space_separator >> (dataPathListEnd | dataPath | schemaPath));
+    ls_::name >> *(space_separator >> ls_options) >> -(space_separator >> ((dataPathListEnd | dataPath | schemaPath) | (module >> "*")));
 
 auto const cd_def =
     cd_::name >> space_separator > dataPath;
@@ -317,7 +317,7 @@
     delete_::name >> space_separator > (presenceContainerPath | listInstancePath);
 
 auto const get_def =
-    get_::name >> -(space_separator >> (dataPathListEnd | dataPath));
+    get_::name >> -(space_separator >> ((dataPathListEnd | dataPath) | (module >> "*")));
 
 auto const set_def =
     set_::name >> space_separator > leafPath > space_separator > leaf_data;
diff --git a/src/interpreter.cpp b/src/interpreter.cpp
index b469149..3630c25 100644
--- a/src/interpreter.cpp
+++ b/src/interpreter.cpp
@@ -135,18 +135,25 @@
 
 std::string Interpreter::absolutePathFromCommand(const get_& get) const
 {
+    using namespace std::string_literals;
     if (!get.m_path) {
         return m_parser.currentNode();
     }
 
     const auto path = *get.m_path;
-    std::string pathString = boost::apply_visitor(pathToStringVisitor(), path);
-    auto pathScope{boost::apply_visitor(getPathScopeVisitor(), path)};
+    if (path.type() == typeid(module_)) {
+        return "/"s + boost::get<module_>(path).m_name + ":*";
 
-    if (pathScope == Scope::Absolute) {
-        return "/" + pathString;
     } else {
-        return joinPaths(m_parser.currentNode(), pathString);
+        auto actualPath = boost::get<boost::variant<dataPath_, schemaPath_>>(path);
+        std::string pathString = boost::apply_visitor(pathToStringVisitor(), actualPath);
+        auto pathScope{boost::apply_visitor(getPathScopeVisitor(), actualPath)};
+
+        if (pathScope == Scope::Absolute) {
+            return "/" + pathString;
+        } else {
+            return joinPaths(m_parser.currentNode(), pathString);
+        }
     }
 }
 
diff --git a/src/netconf_access.cpp b/src/netconf_access.cpp
index 9974fca..c1bd5dc 100644
--- a/src/netconf_access.cpp
+++ b/src/netconf_access.cpp
@@ -33,7 +33,7 @@
     case LY_TYPE_UINT64:
         return value->uint64();
     case LY_TYPE_BOOL:
-        return value->bln();
+        return bool(value->bln());
     case LY_TYPE_STRING:
         return std::string(value->string());
     case LY_TYPE_ENUM:
@@ -64,15 +64,13 @@
         }
     };
 
-    if (path == "/") {
-        for (auto it : m_session->getConfig(NC_DATASTORE_RUNNING)->tree_for()) {
+    auto config = m_session->getConfig(NC_DATASTORE_RUNNING, (path != "/") ? std::optional{path} : std::nullopt);
+
+    if (config) {
+        for (auto it : config->tree_for()) {
             fillMap(it->tree_dfs());
         }
-    } else {
-        auto data = m_session->getConfig(NC_DATASTORE_RUNNING, path);
-        fillMap(data->tree_dfs());
     }
-
     return res;
 }
 
diff --git a/src/parser.cpp b/src/parser.cpp
index 26e186a..370ba1b 100644
--- a/src/parser.cpp
+++ b/src/parser.cpp
@@ -95,11 +95,15 @@
 };
 
 
-std::set<std::string> Parser::availableNodes(const boost::optional<boost::variant<dataPath_, schemaPath_>>& path, const Recursion& option) const
+std::set<std::string> Parser::availableNodes(const boost::optional<boost::variant<boost::variant<dataPath_, schemaPath_>, module_>>& path, const Recursion& option) const
 {
     auto pathArg = dataPathToSchemaPath(m_curDir);
     if (path) {
-        auto schemaPath = boost::apply_visitor(getSchemaPathVisitor(), *path);
+        if (path->type() == typeid(module_)) {
+            return m_schema->moduleNodes(boost::get<module_>(*path), option);
+        }
+
+        auto schemaPath = boost::apply_visitor(getSchemaPathVisitor(), boost::get<boost::variant<dataPath_, schemaPath_>>(*path));
         if (schemaPath.m_scope == Scope::Absolute) {
             pathArg = schemaPath;
         } else {
diff --git a/src/parser.hpp b/src/parser.hpp
index 29ce3f1..3c009e6 100644
--- a/src/parser.hpp
+++ b/src/parser.hpp
@@ -30,7 +30,7 @@
     command_ parseCommand(const std::string& line, std::ostream& errorStream);
     void changeNode(const dataPath_& name);
     std::string currentNode() const;
-    std::set<std::string> availableNodes(const boost::optional<boost::variant<dataPath_, schemaPath_>>& path, const Recursion& option) const;
+    std::set<std::string> availableNodes(const boost::optional<boost::variant<boost::variant<dataPath_, schemaPath_>, module_>>& path, const Recursion& option) const;
     std::set<std::string> completeCommand(const std::string& line, std::ostream& errorStream) const;
 
 private:
diff --git a/src/schema.hpp b/src/schema.hpp
index e0d30af..b5e17a2 100644
--- a/src/schema.hpp
+++ b/src/schema.hpp
@@ -79,4 +79,5 @@
     virtual const std::set<std::string> validIdentities(const schemaPath_& location, const ModuleNodePair& node, const Prefixes prefixes) const = 0;
     virtual const std::set<std::string> enumValues(const schemaPath_& location, const ModuleNodePair& node) const = 0;
     virtual std::set<std::string> childNodes(const schemaPath_& path, const Recursion recursion) const = 0;
+    virtual std::set<std::string> moduleNodes(const module_& module, const Recursion recursion) const = 0;
 };
diff --git a/src/static_schema.cpp b/src/static_schema.cpp
index 3c3fced..a3f59aa 100644
--- a/src/static_schema.cpp
+++ b/src/static_schema.cpp
@@ -6,6 +6,7 @@
  *
 */
 
+#include <boost/algorithm/string/predicate.hpp>
 #include "static_schema.hpp"
 #include "utils.hpp"
 
@@ -254,3 +255,18 @@
                 [] (auto it) { return it.first; });
     return res;
 }
+
+// We do not test StaticSchema, so we don't need to implement recursive moduleNodes
+// for this class.
+std::set<std::string> StaticSchema::moduleNodes(const module_& module, const Recursion) const
+{
+    std::set<std::string> res;
+    auto topLevelNodes = m_nodes.at("");
+    auto modulePlusColon = module.m_name + ":";
+    for (const auto& it : topLevelNodes) {
+        if (boost::algorithm::starts_with(it.first, modulePlusColon)) {
+            res.insert(it.first);
+        }
+    }
+    return res;
+}
diff --git a/src/static_schema.hpp b/src/static_schema.hpp
index 559be6c..09841ac 100644
--- a/src/static_schema.hpp
+++ b/src/static_schema.hpp
@@ -63,6 +63,7 @@
     const std::set<std::string> enumValues(const schemaPath_& location, const ModuleNodePair& node) const override;
     const std::set<std::string> validIdentities(const schemaPath_& location, const ModuleNodePair& node, const Prefixes prefixes) const override;
     std::set<std::string> childNodes(const schemaPath_& path, const Recursion) const override;
+    std::set<std::string> moduleNodes(const module_& module, const Recursion recursion) const override;
 
     void addContainer(const std::string& location, const std::string& name, yang::ContainerTraits isPresence = yang::ContainerTraits::None);
     void addLeaf(const std::string& location, const std::string& name, const yang::LeafDataTypes& type);
diff --git a/src/utils.cpp b/src/utils.cpp
index 979bda4..672cd36 100644
--- a/src/utils.cpp
+++ b/src/utils.cpp
@@ -113,6 +113,14 @@
         return data;
     }
 
+    std::string operator()(const bool& data) const
+    {
+        if (data)
+            return "true";
+        else
+            return "false";
+    }
+
     template <typename T>
     std::string operator()(const T& data) const
     {
diff --git a/src/yang_schema.cpp b/src/yang_schema.cpp
index 629aa2b..c9e3d3c 100644
--- a/src/yang_schema.cpp
+++ b/src/yang_schema.cpp
@@ -346,6 +346,26 @@
     return res;
 }
 
+std::set<std::string> YangSchema::moduleNodes(const module_& module, const Recursion recursion) const
+{
+    std::set<std::string> res;
+    const auto yangModule = m_context->get_module(module.m_name.c_str());
+
+    std::vector<libyang::S_Schema_Node> nodes;
+
+    for (const auto node : yangModule->data_instantiables(0)) {
+        if (recursion == Recursion::Recursive) {
+            for (const auto it : node->tree_dfs()) {
+                res.insert(it->path(LYS_PATH_FIRST_PREFIX));
+            }
+        } else {
+            res.insert(module.m_name + ":" + node->name());
+        }
+    }
+
+    return res;
+}
+
 void YangSchema::loadModule(const std::string& moduleName)
 {
     m_context->load_module(moduleName.c_str());
diff --git a/src/yang_schema.hpp b/src/yang_schema.hpp
index fb4b922..e4de5de 100644
--- a/src/yang_schema.hpp
+++ b/src/yang_schema.hpp
@@ -44,6 +44,7 @@
     const std::set<std::string> validIdentities(const schemaPath_& location, const ModuleNodePair& node, const Prefixes prefixes) const override;
     const std::set<std::string> enumValues(const schemaPath_& location, const ModuleNodePair& node) const override;
     std::set<std::string> childNodes(const schemaPath_& path, const Recursion recursion) const override;
+    std::set<std::string> moduleNodes(const module_& module, const Recursion recursion) const override;
 
     void registerModuleCallback(const std::function<std::string(const char*, const char*, const char*)>& clb);
 
diff --git a/tests/ls.cpp b/tests/ls.cpp
index d56df29..e18cf25 100644
--- a/tests/ls.cpp
+++ b/tests/ls.cpp
@@ -157,6 +157,12 @@
                 expected.m_path = schemaPath_{Scope::Relative, {schemaNode_(module_{"example"}, list_{"list"}),
                                                                 schemaNode_(container_{"contInList"})}};
             }
+
+            SECTION("ls example:*")
+            {
+                input = "ls example:*";
+                expected.m_path = module_{"example"};
+            }
         }
 
         command_ command = parser.parseCommand(input, errorStream);
diff --git a/tests/parser_methods.cpp b/tests/parser_methods.cpp
index d8080b3..57e6e56 100644
--- a/tests/parser_methods.cpp
+++ b/tests/parser_methods.cpp
@@ -44,7 +44,7 @@
     SECTION("availableNodes")
     {
         std::set<std::string> expected;
-        boost::optional<boost::variant<dataPath_, schemaPath_>> arg{boost::none};
+        boost::optional<boost::variant<boost::variant<dataPath_, schemaPath_>, module_>> arg{boost::none};
         SECTION("cwd: /")
         {
             SECTION("arg: <none>")
diff --git a/tests/sysrepo.cpp b/tests/sysrepo.cpp
index c544cf3..9c04bf9 100644
--- a/tests/sysrepo.cpp
+++ b/tests/sysrepo.cpp
@@ -17,13 +17,27 @@
 #error "Unknown backend"
 #endif
 #include "sysrepo_subscription.hpp"
+#include "utils.hpp"
 
 class MockRecorder : public Recorder {
 public:
     MAKE_MOCK3(write, void(const std::string&, const std::string&, const std::string&), override);
 };
 
-TEST_CASE("setting values")
+namespace std {
+std::ostream& operator<<(std::ostream& s, const std::map<std::string, leaf_data_> map)
+{
+    s << std::endl
+      << "{";
+    for (const auto& it : map) {
+        s << "{\"" << it.first << "\", " << leafDataToString(it.second) << "}" << std::endl;
+    }
+    s << "}" << std::endl;
+    return s;
+}
+}
+
+TEST_CASE("setting/getting values")
 {
     trompeloeil::sequence seq1;
     MockRecorder mock;
@@ -161,6 +175,43 @@
             datastore.commitChanges();
         }
     }
+    SECTION("bool values get correctly represented as bools")
+    {
+        {
+            REQUIRE_CALL(mock, write("/example-schema:down", "", "true"));
+            datastore.setLeaf("/example-schema:down", bool{true});
+            datastore.commitChanges();
+        }
+
+        std::map<std::string, leaf_data_> expected{{"/example-schema:down", bool{true}}};
+        REQUIRE(datastore.getItems("/example-schema:down") == expected);
+    }
+
+    SECTION("getting items from the whole module")
+    {
+        {
+            REQUIRE_CALL(mock, write("/example-schema:up", "", "true"));
+            REQUIRE_CALL(mock, write("/example-schema:down", "", "false"));
+            datastore.setLeaf("/example-schema:up", bool{true});
+            datastore.setLeaf("/example-schema:down", bool{false});
+            datastore.commitChanges();
+        }
+
+        std::map<std::string, leaf_data_> expected{{"/example-schema:down", bool{false}},
+        // Sysrepo always returns containers when getting values, but
+        // libnetconf does not. This is fine by the YANG standard:
+        // https://tools.ietf.org/html/rfc7950#section-7.5.7 Furthermore,
+        // NetconfAccess implementation actually only iterates over leafs,
+        // so even if libnetconf did include containers, they wouldn't get
+        // shown here anyway. With sysrepo2, this won't be necessary,
+        // because it'll use the same data structure as libnetconf, so the
+        // results will be consistent.
+#ifdef sysrepo_BACKEND
+                                                   {"/example-schema:lol", std::string{"(container)"}},
+#endif
+                                                   {"/example-schema:up", bool{true}}};
+        REQUIRE(datastore.getItems("/example-schema:*") == expected);
+    }
 
     waitForCompletionAndBitMore(seq1);
 }