Allow listing all module nodes with moduleName:*
Change-Id: I1c9d8ace936641db06f38c292655b3c5c5557385
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 0bdb166..1d3789e 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 13fec88..c1bd5dc 100644
--- a/src/netconf_access.cpp
+++ b/src/netconf_access.cpp
@@ -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/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 7125a9a..9c04bf9 100644
--- a/tests/sysrepo.cpp
+++ b/tests/sysrepo.cpp
@@ -187,5 +187,31 @@
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);
}