Add sysrepo error handling
Change-Id: I810de1afd0ad6161a18903f816e19db28ed9edf1
diff --git a/src/datastore_access.cpp b/src/datastore_access.cpp
index 2b7243b..6e3b37a 100644
--- a/src/datastore_access.cpp
+++ b/src/datastore_access.cpp
@@ -8,4 +8,30 @@
#include "datastore_access.hpp"
+DatastoreError::DatastoreError(const std::string& message, const std::optional<std::string>& xpath)
+ : message(message)
+ , xpath(xpath)
+{
+}
+
DatastoreAccess::~DatastoreAccess() = default;
+
+DatastoreException::DatastoreException(const std::vector<DatastoreError>& errors)
+{
+ m_what = "The following errors occured:\n";
+ for (const auto& it : errors) {
+ m_what += " Message: ";
+ m_what += it.message;
+ m_what += "\n";
+ if (it.xpath) {
+ m_what += " Xpath: ";
+ m_what += it.xpath.value();
+ m_what += "\n";
+ }
+ }
+}
+
+const char* DatastoreException::what() const noexcept
+{
+ return m_what.c_str();
+}
diff --git a/src/datastore_access.hpp b/src/datastore_access.hpp
index 2f75e51..67b3d29 100644
--- a/src/datastore_access.hpp
+++ b/src/datastore_access.hpp
@@ -9,6 +9,7 @@
#pragma once
#include <map>
+#include <optional>
#include <string>
#include "ast_values.hpp"
@@ -16,6 +17,22 @@
* \brief Abstract class for accessing a datastore
*/
+struct DatastoreError {
+ std::string message;
+ std::optional<std::string> xpath;
+
+ DatastoreError(const std::string& message, const std::optional<std::string>& xpath);
+};
+
+class DatastoreException : std::exception {
+public:
+ DatastoreException(const std::vector<DatastoreError>& errors);
+ ~DatastoreException() override = default;
+ const char* what() const noexcept override;
+
+private:
+ std::string m_what;
+};
class DatastoreAccess {
public:
diff --git a/src/main.cpp b/src/main.cpp
index 4ba886e..3019d36 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -78,6 +78,8 @@
boost::apply_visitor(Interpreter(parser, datastore), cmd);
} catch (InvalidCommandException& ex) {
std::cerr << ex.what() << std::endl;
+ } catch (DatastoreException& ex) {
+ std::cerr << ex.what() << std::endl;
}
lineEditor.history_add(line);
diff --git a/src/sysrepo_access.cpp b/src/sysrepo_access.cpp
index 4e1ab4b..16dfab5 100644
--- a/src/sysrepo_access.cpp
+++ b/src/sysrepo_access.cpp
@@ -72,7 +72,11 @@
: m_connection(new sysrepo::Connection(appname.c_str()))
, m_schema(new YangSchema())
{
- m_session = std::make_shared<sysrepo::Session>(m_connection);
+ try {
+ m_session = std::make_shared<sysrepo::Session>(m_connection);
+ } catch (sysrepo::sysrepo_exception& ex) {
+ reportErrors();
+ }
m_schema->registerModuleCallback([this](const char* moduleName, const char* revision, const char* submodule) {
return fetchSchema(moduleName, revision, submodule);
});
@@ -95,57 +99,94 @@
}
};
- if (path == "/") {
- // Sysrepo doesn't have a root node ("/"), so we take all top-level nodes from all schemas
- auto schemas = m_session->list_schemas();
- for (unsigned int i = 0; i < schemas->schema_cnt(); i++) {
- fillMap(m_session->get_items(("/"s + schemas->schema(i)->module_name() + ":*//.").c_str()));
+ try {
+ if (path == "/") {
+ // Sysrepo doesn't have a root node ("/"), so we take all top-level nodes from all schemas
+ auto schemas = m_session->list_schemas();
+ for (unsigned int i = 0; i < schemas->schema_cnt(); i++) {
+ fillMap(m_session->get_items(("/"s + schemas->schema(i)->module_name() + ":*//.").c_str()));
+ }
+ } else {
+ fillMap(m_session->get_items((path + "//.").c_str()));
}
- } else {
- fillMap(m_session->get_items((path + "//.").c_str()));
+ } catch (sysrepo::sysrepo_exception& ex) {
+ reportErrors();
}
-
return res;
}
void SysrepoAccess::setLeaf(const std::string& path, leaf_data_ value)
{
- m_session->set_item(path.c_str(), boost::apply_visitor(valFromValue(), value));
+ try {
+ m_session->set_item(path.c_str(), boost::apply_visitor(valFromValue(), value));
+ } catch (sysrepo::sysrepo_exception& ex) {
+ reportErrors();
+ }
}
void SysrepoAccess::createPresenceContainer(const std::string& path)
{
- m_session->set_item(path.c_str());
+ try {
+ m_session->set_item(path.c_str());
+ } catch (sysrepo::sysrepo_exception& ex) {
+ reportErrors();
+ }
}
void SysrepoAccess::deletePresenceContainer(const std::string& path)
{
- m_session->delete_item(path.c_str());
+ try {
+ m_session->delete_item(path.c_str());
+ } catch (sysrepo::sysrepo_exception& ex) {
+ reportErrors();
+ }
}
void SysrepoAccess::createListInstance(const std::string& path)
{
- m_session->set_item(path.c_str());
+ try {
+ m_session->set_item(path.c_str());
+ } catch (sysrepo::sysrepo_exception& ex) {
+ reportErrors();
+ }
}
void SysrepoAccess::deleteListInstance(const std::string& path)
{
- m_session->delete_item(path.c_str());
+ try {
+ m_session->delete_item(path.c_str());
+ } catch (sysrepo::sysrepo_exception& ex) {
+ reportErrors();
+ }
}
void SysrepoAccess::commitChanges()
{
- m_session->commit();
+ try {
+ m_session->commit();
+ } catch (sysrepo::sysrepo_exception& ex) {
+ reportErrors();
+ }
}
void SysrepoAccess::discardChanges()
{
- m_session->discard_changes();
+ try {
+ m_session->discard_changes();
+ } catch (sysrepo::sysrepo_exception& ex) {
+ reportErrors();
+ }
}
std::string SysrepoAccess::fetchSchema(const char* module, const char* revision, const char* submodule)
{
- auto schema = m_session->get_schema(module, revision, submodule, SR_SCHEMA_YANG); // FIXME: maybe we should use get_submodule_schema for submodules?
+ std::string schema;
+ try {
+ schema = m_session->get_schema(module, revision, submodule, SR_SCHEMA_YANG); // FIXME: maybe we should use get_submodule_schema for submodules?
+ } catch (sysrepo::sysrepo_exception& ex) {
+ reportErrors();
+ }
+
if (schema.empty())
throw std::runtime_error(std::string("Module ") + module + " not available");
@@ -155,7 +196,12 @@
std::vector<std::string> SysrepoAccess::listImplementedSchemas()
{
std::vector<std::string> res;
- auto schemas = m_session->list_schemas();
+ std::shared_ptr<sysrepo::Yang_Schemas> schemas;
+ try {
+ schemas = m_session->list_schemas();
+ } catch (sysrepo::sysrepo_exception& ex) {
+ reportErrors();
+ }
for (unsigned int i = 0; i < schemas->schema_cnt(); i++) {
auto schema = schemas->schema(i);
if (schema->implemented())
@@ -168,3 +214,21 @@
{
return m_schema;
}
+
+[[noreturn]] void SysrepoAccess::reportErrors()
+{
+ // I only use get_last_errors to get error info, since the error code from
+ // sysrepo_exception doesn't really give any meaningful information. For
+ // example an "invalid argument" error could mean a node isn't enabled, or
+ // it could mean something totally different and there is no documentation
+ // for that, so it's better to just use the message sysrepo gives me.
+ auto srErrors = m_session->get_last_errors();
+ std::vector<DatastoreError> res;
+
+ for (size_t i = 0; i < srErrors->error_cnt(); i++) {
+ auto error = srErrors->error(i);
+ res.emplace_back(error->message(), error->xpath() ? std::optional<std::string>{error->xpath()} : std::nullopt);
+ }
+
+ throw DatastoreException(res);
+}
diff --git a/src/sysrepo_access.hpp b/src/sysrepo_access.hpp
index 76e9088..97d229e 100644
--- a/src/sysrepo_access.hpp
+++ b/src/sysrepo_access.hpp
@@ -43,6 +43,8 @@
std::shared_ptr<Schema> schema();
private:
+ [[noreturn]] void reportErrors();
+
std::shared_ptr<sysrepo::Connection> m_connection;
std::shared_ptr<sysrepo::Session> m_session;
std::shared_ptr<YangSchema> m_schema;