summaryrefslogtreecommitdiffstats
path: root/src/bin/netconf/netconf.cc
diff options
context:
space:
mode:
Diffstat (limited to 'src/bin/netconf/netconf.cc')
-rw-r--r--src/bin/netconf/netconf.cc797
1 files changed, 797 insertions, 0 deletions
diff --git a/src/bin/netconf/netconf.cc b/src/bin/netconf/netconf.cc
new file mode 100644
index 0000000..42b6d3e
--- /dev/null
+++ b/src/bin/netconf/netconf.cc
@@ -0,0 +1,797 @@
+// 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/.
+
+/// @file netconf.cc
+/// Contains the Netconf agent methods.
+
+#include <config.h>
+
+#include <netconf/netconf.h>
+#include <netconf/netconf_controller.h>
+#include <netconf/netconf_log.h>
+#include <cc/command_interpreter.h>
+#include <yang/translator_config.h>
+#include <yang/yang_revisions.h>
+
+#include <boost/algorithm/string.hpp>
+
+#include <sstream>
+
+using namespace std;
+using namespace isc::config;
+using namespace isc::data;
+using namespace isc::netconf;
+using namespace isc::yang;
+using namespace sysrepo;
+
+using libyang::S_Context;
+using libyang::S_Module;
+
+namespace {
+
+/// @brief Module change subscription callback.
+class NetconfAgentCallback {
+public:
+ /// @brief Constructor.
+ ///
+ /// @param service_pair The service name and configuration pair.
+ NetconfAgentCallback(const CfgServersMapPair& service_pair)
+ : service_pair_(service_pair) {
+ }
+
+ /// @brief Server name and configuration pair.
+ CfgServersMapPair const service_pair_;
+
+ /// @brief Module configuration change callback.
+ ///
+ /// This callback is called by sysrepo when there is a change to
+ /// module configuration.
+ ///
+ /// @param sess The running datastore session.
+ /// @param module_name The module name.
+ /// @param event The event.
+ /// @param private_ctx The private context.
+ /// @return the sysrepo return code.
+ sr_error_t module_change(S_Session sess,
+ const char* module_name,
+ const char* /* xpath */,
+ sr_event_t event,
+ void* /* private_ctx */) {
+ ostringstream event_type;
+ switch (event) {
+ case SR_EV_UPDATE:
+ // This could potentially be a hook point for mid-flight
+ // configuration changes.
+ event_type << "SR_EV_UPDATE";
+ break;
+ case SR_EV_CHANGE:
+ event_type << "SR_EV_CHANGE";
+ break;
+ case SR_EV_DONE:
+ event_type << "SR_EV_DONE";
+ break;
+ case SR_EV_ABORT:
+ event_type << "SR_EV_ABORT";
+ break;
+ case SR_EV_ENABLED:
+ event_type << "SR_EV_ENABLED";
+ break;
+ case SR_EV_RPC:
+ event_type << "SR_EV_RPC";
+ break;
+ default:
+ event_type << "UNKNOWN (" << event << ")";
+ break;
+ }
+ LOG_INFO(netconf_logger, NETCONF_CONFIG_CHANGE_EVENT)
+ .arg(event_type.str());
+ NetconfAgent::logChanges(sess, module_name);
+ switch (event) {
+ case SR_EV_CHANGE:
+ return (NetconfAgent::change(sess, service_pair_));
+ case SR_EV_DONE:
+ return (NetconfAgent::done(sess, service_pair_));
+ default:
+ return (SR_ERR_OK);
+ }
+ }
+
+ void event_notif(sysrepo::S_Session /* session */,
+ sr_ev_notif_type_t const notification_type,
+ char const* /* path */,
+ sysrepo::S_Vals const vals,
+ time_t /* timestamp */,
+ void* /* private_data */) {
+ string n;
+ switch (notification_type) {
+ case SR_EV_NOTIF_REALTIME:
+ n = "SR_EV_NOTIF_REALTIME";
+ break;
+ case SR_EV_NOTIF_REPLAY:
+ n = "SR_EV_NOTIF_REPLAY";
+ break;
+ case SR_EV_NOTIF_REPLAY_COMPLETE:
+ n = "SR_EV_NOTIF_REPLAY_COMPLETE";
+ break;
+ case SR_EV_NOTIF_STOP:
+ n = "SR_EV_NOTIF_STOP";
+ break;
+ case SR_EV_NOTIF_SUSPENDED:
+ n = "SR_EV_NOTIF_SUSPENDED";
+ break;
+ case SR_EV_NOTIF_RESUMED:
+ n = "SR_EV_NOTIF_RESUMED";
+ break;
+ }
+
+ stringstream s;
+ for (size_t i(0); i < vals->val_cnt(); ++i) {
+ if (i != 0) {
+ s << ", ";
+ }
+ s << vals->val(i)->to_string();
+ }
+
+ LOG_INFO(netconf_logger, NETCONF_NOTIFICATION_RECEIVED)
+ .arg(n)
+ .arg(service_pair_.first)
+ .arg(s.str());
+ }
+};
+
+} // end of anonymous namespace
+
+namespace isc {
+namespace netconf {
+
+NetconfAgent::~NetconfAgent() {
+ clear();
+}
+
+void
+NetconfAgent::init(NetconfCfgMgrPtr cfg_mgr) {
+ // Check for a configuration manager.
+ if (!cfg_mgr) {
+ isc_throw(Unexpected, "missing configuration manager");
+ return;
+ }
+
+ // Retrieve configuration from existing running DHCP daemons.
+ const CfgServersMapPtr& servers =
+ cfg_mgr->getNetconfConfig()->getCfgServersMap();
+ for (auto const& pair : *servers) {
+ keaConfig(pair);
+ }
+
+ // Initialize sysrepo.
+ initSysrepo();
+
+ // Check modules / revisions.
+ checkModules(servers);
+
+ for (auto const& pair : *servers) {
+ yangConfig(pair);
+ subscribeConfig(pair);
+ subscribeToNotifications(pair);
+ }
+}
+
+void
+NetconfAgent::clear() {
+ for (auto subs : subscriptions_) {
+ subs.second.reset();
+ }
+ subscriptions_.clear();
+ running_sess_.reset();
+ startup_sess_.reset();
+ conn_.reset();
+}
+
+void
+NetconfAgent::keaConfig(const CfgServersMapPair& service_pair) {
+ // If the boot-update flag is not set.
+ if (!service_pair.second->getBootUpdate()) {
+ return;
+ }
+ CfgControlSocketPtr ctrl_sock = service_pair.second->getCfgControlSocket();
+ if (!ctrl_sock) {
+ return;
+ }
+ ControlSocketBasePtr comm;
+ try {
+ comm = controlSocketFactory(ctrl_sock);
+ } catch (const std::exception& ex) {
+ ostringstream msg;
+ msg << "createControlSocket failed with " << ex.what();
+ LOG_ERROR(netconf_logger, NETCONF_GET_CONFIG_FAILED)
+ .arg(service_pair.first)
+ .arg(msg.str());
+ return;
+ }
+ ConstElementPtr answer;
+ int rcode;
+ ConstElementPtr config;
+ LOG_INFO(netconf_logger, NETCONF_GET_CONFIG_STARTED)
+ .arg(service_pair.first);
+ try {
+ answer = comm->configGet(service_pair.first);
+ config = parseAnswer(rcode, answer);
+ } catch (const std::exception& ex) {
+ ostringstream msg;
+ msg << "config-get command failed with " << ex.what();
+ LOG_ERROR(netconf_logger, NETCONF_GET_CONFIG_FAILED)
+ .arg(service_pair.first)
+ .arg(msg.str());
+ return;
+ }
+ if (rcode != CONTROL_RESULT_SUCCESS) {
+ ostringstream msg;
+ msg << "config-get command returned " << answerToText(answer);
+ LOG_ERROR(netconf_logger, NETCONF_GET_CONFIG_FAILED)
+ .arg(service_pair.first)
+ .arg(msg.str());
+ return;
+ }
+ if (!config) {
+ LOG_ERROR(netconf_logger, NETCONF_GET_CONFIG_FAILED)
+ .arg(service_pair.first)
+ .arg("config-get command returned an empty configuration");
+ return;
+ }
+ LOG_DEBUG(netconf_logger, NETCONF_DBG_TRACE_DETAIL_DATA,
+ NETCONF_GET_CONFIG)
+ .arg(service_pair.first)
+ .arg(prettyPrint(config));
+}
+
+void
+NetconfAgent::initSysrepo() {
+ try {
+ conn_ = make_shared<Connection>();
+ } catch (const std::exception& ex) {
+ isc_throw(Unexpected, "Can't connect to sysrepo: " << ex.what());
+ }
+
+ try {
+ startup_sess_.reset(new Session(conn_, SR_DS_STARTUP));
+ running_sess_.reset(new Session(conn_, SR_DS_RUNNING));
+ } catch (const std::exception& ex) {
+ isc_throw(Unexpected, "Can't establish a sysrepo session: "
+ << ex.what());
+ }
+
+ // Retrieve names and revisions of installed modules from sysrepo.
+ getModules();
+}
+
+void NetconfAgent::getModules() {
+ vector<S_Module> modules;
+ try {
+ S_Context context(running_sess_->get_context());
+ modules = context->get_module_iter();
+ } catch (const sysrepo_exception& ex) {
+ isc_throw(Unexpected, "can't retrieve available modules: " << ex.what());
+ }
+
+ for (S_Module const& module : modules) {
+ if (!module->name()) {
+ isc_throw(Unexpected, "could not retrieve module name");
+ }
+ string const name(module->name());
+ if (!module->rev() || !module->rev()->date()) {
+ isc_throw(Unexpected,
+ "could not retrieve module revision for module " << name);
+ }
+ string const revision(module->rev()->date());
+ modules_.emplace(name, revision);
+ }
+}
+
+bool
+NetconfAgent::checkModule(const string& module_name) const {
+ auto module = modules_.find(module_name);
+ if (module == modules_.end()) {
+ LOG_ERROR(netconf_logger, NETCONF_MODULE_MISSING_ERR)
+ .arg(module_name);
+ return (false);
+ }
+ auto modrev = YANG_REVISIONS.find(module_name);
+ if (modrev == YANG_REVISIONS.end()) {
+ // Can't check revision?!
+ // It can happen only with a module which is not in
+ // YANG_REVISIONS but installed so likely on purpose.
+ return (true);
+ }
+ if (modrev->second != module->second) {
+ LOG_ERROR(netconf_logger, NETCONF_MODULE_REVISION_ERR)
+ .arg(module_name)
+ .arg(modrev->second)
+ .arg(module->second);
+ return (false);
+ }
+ return (true);
+}
+
+void
+NetconfAgent::checkModules(CfgServersMapPtr const& servers /* = {} */) const {
+ bool faulty_model(false);
+ if (servers) {
+ for (auto pair : *servers) {
+ if (!checkModule(pair.second->getModel())) {
+ faulty_model = true;
+ }
+ }
+ }
+
+ if (faulty_model) {
+ isc_throw(Unexpected, "YANG module is missing or its revision is not "
+ "supported. Check logs for details.");
+ }
+
+ for (auto modrev : YANG_REVISIONS) {
+ auto module = modules_.find(modrev.first);
+ if (module == modules_.end()) {
+ LOG_WARN(netconf_logger, NETCONF_MODULE_MISSING_WARN)
+ .arg(modrev.first);
+ continue;
+ }
+ if (modrev.second != module->second) {
+ LOG_WARN(netconf_logger, NETCONF_MODULE_REVISION_WARN)
+ .arg(modrev.first)
+ .arg(modrev.second)
+ .arg(module->second);
+ }
+ }
+}
+
+void
+NetconfAgent::yangConfig(const CfgServersMapPair& service_pair) {
+ // If we're shutting down, or the boot-update flag is not set or the model
+ // associated with it is not specified.
+ if (!service_pair.second->getBootUpdate() ||
+ service_pair.second->getModel().empty()) {
+ return;
+ }
+
+ // First we need a way to reach the actual servers.
+ CfgControlSocketPtr ctrl_sock = service_pair.second->getCfgControlSocket();
+ if (!ctrl_sock) {
+ return;
+ }
+
+ LOG_INFO(netconf_logger, NETCONF_SET_CONFIG_STARTED)
+ .arg(service_pair.first);
+ ConstElementPtr config;
+ try {
+ // Retrieve configuration from Sysrepo.
+ TranslatorConfig tc(startup_sess_, service_pair.second->getModel());
+ config = tc.getConfig();
+ if (!config) {
+ ostringstream msg;
+ msg << "YANG configuration for "
+ << service_pair.second->getModel()
+ << " is empty";
+ LOG_ERROR(netconf_logger, NETCONF_SET_CONFIG_FAILED)
+ .arg(service_pair.first)
+ .arg(msg.str());
+ return;
+ } else {
+ LOG_DEBUG(netconf_logger, NETCONF_DBG_TRACE_DETAIL_DATA,
+ NETCONF_SET_CONFIG)
+ .arg(service_pair.first)
+ .arg(prettyPrint(config));
+ }
+ } catch (const std::exception& ex) {
+ ostringstream msg;
+ msg << "get YANG configuration for " << service_pair.first
+ << " failed with " << ex.what();
+ LOG_ERROR(netconf_logger, NETCONF_SET_CONFIG_FAILED)
+ .arg(service_pair.first)
+ .arg(msg.str());
+ return;
+ }
+ ControlSocketBasePtr comm;
+ try {
+ comm = controlSocketFactory(ctrl_sock);
+ } catch (const std::exception& ex) {
+ ostringstream msg;
+ msg << "control socket creation failed with " << ex.what();
+ LOG_ERROR(netconf_logger, NETCONF_SET_CONFIG_FAILED)
+ .arg(service_pair.first)
+ .arg(msg.str());
+ return;
+ }
+ ConstElementPtr answer;
+ int rcode;
+ try {
+ answer = comm->configSet(config, service_pair.first);
+ parseAnswer(rcode, answer);
+ } catch (const std::exception& ex) {
+ ostringstream msg;
+ msg << "config-set command failed with " << ex.what();
+ LOG_ERROR(netconf_logger, NETCONF_SET_CONFIG_FAILED)
+ .arg(service_pair.first)
+ .arg(msg.str());
+ return;
+ }
+ if (rcode != CONTROL_RESULT_SUCCESS) {
+ ostringstream msg;
+ msg << "config-set command returned " << answerToText(answer);
+ LOG_ERROR(netconf_logger, NETCONF_SET_CONFIG_FAILED)
+ .arg(service_pair.first)
+ .arg(msg.str());
+ return;
+ }
+ LOG_INFO(netconf_logger, NETCONF_BOOT_UPDATE_COMPLETED)
+ .arg(service_pair.first);
+}
+
+void
+NetconfAgent::subscribeConfig(const CfgServersMapPair& service_pair) {
+ std::string const& model(service_pair.second->getModel());
+
+ // If we're shutting down, or the subscribe-changes flag is not set or
+ // the model associated with it is not specified.
+ if (!service_pair.second->getSubscribeChanges() ||
+ model.empty()) {
+ return;
+ }
+ LOG_INFO(netconf_logger, NETCONF_SUBSCRIBE_CONFIG)
+ .arg(service_pair.first)
+ .arg(model);
+ S_Subscribe subs(new Subscribe(running_sess_));
+ auto callback = [=](sysrepo::S_Session sess, const char* module_name,
+ const char* xpath, sr_event_t event,
+ uint32_t /* request_id */) {
+ NetconfAgentCallback agent(service_pair);
+ return agent.module_change(sess, module_name, xpath, event, nullptr);
+ };
+ try {
+ sr_subscr_options_t options = SR_SUBSCR_DEFAULT;
+ if (!service_pair.second->getValidateChanges()) {
+ options |= SR_SUBSCR_DONE_ONLY;
+ }
+ // Note the API stores the module name so do not put it
+ // in a short lifetime variable!
+ subs->module_change_subscribe(model.c_str(), callback, nullptr, 0,
+ options);
+ } catch (const std::exception& ex) {
+ ostringstream msg;
+ msg << "module change subscribe failed with " << ex.what();
+ msg << "change subscription for model " << model <<
+ " failed with: " << ex.what();
+ LOG_ERROR(netconf_logger, NETCONF_SUBSCRIBE_CONFIG_FAILED)
+ .arg(service_pair.first)
+ .arg(service_pair.second->getModel())
+ .arg(msg.str());
+ return;
+ }
+ subscriptions_.insert(make_pair(service_pair.first, subs));
+}
+
+
+void
+NetconfAgent::subscribeToNotifications(const CfgServersMapPair& service_pair) {
+ std::string const& model(service_pair.second->getModel());
+ // If we're shutting down, or the subscribe-changes flag is not set or
+ // the model associated with it is not specified.
+ if (!service_pair.second->getSubscribeNotifications() ||
+ model.empty()) {
+ return;
+ }
+ LOG_INFO(netconf_logger, NETCONF_SUBSCRIBE_NOTIFICATIONS)
+ .arg(service_pair.first)
+ .arg(model);
+
+ S_Subscribe subscription(std::make_shared<Subscribe>(running_sess_));
+ auto callback = [=](sysrepo::S_Session session,
+ sr_ev_notif_type_t const notification_type,
+ char const* path,
+ sysrepo::S_Vals const vals,
+ time_t timestamp) {
+ NetconfAgentCallback agent(service_pair);
+ return agent.event_notif(session, notification_type, path, vals, timestamp, nullptr);
+ };
+ try {
+ subscription->event_notif_subscribe(model.c_str(), callback);
+ } catch (const std::exception& ex) {
+ ostringstream msg;
+ msg << "event notification subscription for model " << model <<
+ " failed with: " << ex.what();
+ LOG_ERROR(netconf_logger, NETCONF_SUBSCRIBE_NOTIFICATIONS_FAILED)
+ .arg(service_pair.first)
+ .arg(service_pair.second->getModel())
+ .arg(msg.str());
+ return;
+ }
+ subscriptions_.emplace(service_pair.first, subscription);
+}
+
+sr_error_t
+NetconfAgent::change(S_Session sess, const CfgServersMapPair& service_pair) {
+ // If we're shutting down, or the subscribe-changes or the
+ // validate-changes flag is not set or the model associated with
+ // it is not specified.
+ if (!service_pair.second->getSubscribeChanges() ||
+ !service_pair.second->getValidateChanges() ||
+ service_pair.second->getModel().empty()) {
+ return (SR_ERR_OK);
+ }
+ CfgControlSocketPtr ctrl_sock = service_pair.second->getCfgControlSocket();
+ if (!ctrl_sock) {
+ return (SR_ERR_OK);
+ }
+ LOG_INFO(netconf_logger, NETCONF_VALIDATE_CONFIG_STARTED)
+ .arg(service_pair.first);
+ ConstElementPtr config;
+ try {
+ TranslatorConfig tc(sess, service_pair.second->getModel());
+ config = tc.getConfig();
+ if (!config) {
+ ostringstream msg;
+ msg << "YANG configuration for "
+ << service_pair.second->getModel()
+ << " is empty";
+ LOG_ERROR(netconf_logger, NETCONF_VALIDATE_CONFIG_FAILED)
+ .arg(service_pair.first)
+ .arg(msg.str());
+ return (SR_ERR_OPERATION_FAILED);
+ } else {
+ LOG_DEBUG(netconf_logger, NETCONF_DBG_TRACE_DETAIL_DATA,
+ NETCONF_VALIDATE_CONFIG)
+ .arg(service_pair.first)
+ .arg(prettyPrint(config));
+ }
+ } catch (const std::exception& ex) {
+ ostringstream msg;
+ msg << "get YANG configuration for " << service_pair.first
+ << " failed with " << ex.what();
+ LOG_ERROR(netconf_logger, NETCONF_VALIDATE_CONFIG_FAILED)
+ .arg(service_pair.first)
+ .arg(msg.str());
+ return (SR_ERR_VALIDATION_FAILED);;
+ }
+ ControlSocketBasePtr comm;
+ try {
+ comm = controlSocketFactory(ctrl_sock);
+ } catch (const std::exception& ex) {
+ ostringstream msg;
+ msg << "createControlSocket failed with " << ex.what();
+ LOG_ERROR(netconf_logger, NETCONF_VALIDATE_CONFIG_FAILED)
+ .arg(service_pair.first)
+ .arg(msg.str());
+ return (SR_ERR_OK);
+ }
+ ConstElementPtr answer;
+ int rcode;
+ try {
+ answer = comm->configTest(config, service_pair.first);
+ parseAnswer(rcode, answer);
+ } catch (const std::exception& ex) {
+ stringstream msg;
+ msg << "configTest failed with " << ex.what();
+ LOG_ERROR(netconf_logger, NETCONF_VALIDATE_CONFIG_FAILED)
+ .arg(service_pair.first)
+ .arg(msg.str());
+ return (SR_ERR_VALIDATION_FAILED);
+ }
+ if (rcode != CONTROL_RESULT_SUCCESS) {
+ stringstream msg;
+ msg << "configTest returned " << answerToText(answer);
+ LOG_ERROR(netconf_logger, NETCONF_VALIDATE_CONFIG_REJECTED)
+ .arg(service_pair.first)
+ .arg(msg.str());
+ return (SR_ERR_VALIDATION_FAILED);
+ }
+ LOG_INFO(netconf_logger, NETCONF_VALIDATE_CONFIG_COMPLETED)
+ .arg(service_pair.first);
+ return (SR_ERR_OK);
+}
+
+sr_error_t
+NetconfAgent::done(S_Session sess, const CfgServersMapPair& service_pair) {
+ // Check if we should and can process this update.
+ if (!service_pair.second->getSubscribeChanges() ||
+ service_pair.second->getModel().empty()) {
+ return (SR_ERR_OK);
+ }
+ CfgControlSocketPtr ctrl_sock = service_pair.second->getCfgControlSocket();
+ if (!ctrl_sock) {
+ return (SR_ERR_OK);
+ }
+
+ // All looks good, let's get started. Print an info that we're about
+ // to update the configuration.
+ LOG_INFO(netconf_logger, NETCONF_UPDATE_CONFIG_STARTED)
+ .arg(service_pair.first);
+
+ // Retrieve the configuration from SYSREPO first.
+ ConstElementPtr config;
+ try {
+ TranslatorConfig tc(sess, service_pair.second->getModel());
+ config = tc.getConfig();
+ if (!config) {
+ ostringstream msg;
+ msg << "YANG configuration for "
+ << service_pair.second->getModel()
+ << " is empty";
+ LOG_ERROR(netconf_logger, NETCONF_UPDATE_CONFIG_FAILED)
+ .arg(service_pair.first)
+ .arg(msg.str());
+ return (SR_ERR_VALIDATION_FAILED);
+ } else {
+ LOG_DEBUG(netconf_logger, NETCONF_DBG_TRACE_DETAIL_DATA,
+ NETCONF_UPDATE_CONFIG)
+ .arg(service_pair.first)
+ .arg(prettyPrint(config));
+ }
+ } catch (const std::exception& ex) {
+ ostringstream msg;
+ msg << "get YANG configuration for " << service_pair.first
+ << " failed with " << ex.what();
+ LOG_ERROR(netconf_logger, NETCONF_UPDATE_CONFIG_FAILED)
+ .arg(service_pair.first)
+ .arg(msg.str());
+ return (SR_ERR_VALIDATION_FAILED);
+ }
+
+ // Ok, now open the control socket. We need this to send the config to
+ // the server.
+ ControlSocketBasePtr comm;
+ try {
+ comm = controlSocketFactory(ctrl_sock);
+ } catch (const std::exception& ex) {
+ ostringstream msg;
+ msg << "createControlSocket failed with " << ex.what();
+ LOG_ERROR(netconf_logger, NETCONF_UPDATE_CONFIG_FAILED)
+ .arg(service_pair.first)
+ .arg(msg.str());
+ return (SR_ERR_OK);
+ }
+
+ // Now apply the config using config-set command.
+ ConstElementPtr answer;
+ int rcode;
+ try {
+ answer = comm->configSet(config, service_pair.first);
+ parseAnswer(rcode, answer);
+ } catch (const std::exception& ex) {
+ stringstream msg;
+ msg << "configSet failed with " << ex.what();
+ LOG_ERROR(netconf_logger, NETCONF_UPDATE_CONFIG_FAILED)
+ .arg(service_pair.first)
+ .arg(msg.str());
+ return (SR_ERR_VALIDATION_FAILED);
+ }
+
+ // rcode == CONTROL_RESULT_SUCCESS, unless the docs say otherwise :).
+ if (rcode != CONTROL_RESULT_SUCCESS) {
+ stringstream msg;
+ msg << "configSet returned " << answerToText(answer);
+ LOG_ERROR(netconf_logger, NETCONF_UPDATE_CONFIG_FAILED)
+ .arg(service_pair.first)
+ .arg(msg.str());
+ return (SR_ERR_VALIDATION_FAILED);
+ }
+ LOG_INFO(netconf_logger, NETCONF_UPDATE_CONFIG_COMPLETED)
+ .arg(service_pair.first);
+ return (SR_ERR_OK);
+}
+
+void
+NetconfAgent::logChanges(S_Session sess, const string& model) {
+ ostringstream stream;
+ stream << "/" << model << ":*//.";
+ std::string const xpath(stream.str());
+ S_Iter_Change iter = sess->get_changes_iter(xpath.c_str());
+ if (!iter) {
+ LOG_WARN(netconf_logger, NETCONF_LOG_CHANGE_FAIL)
+ .arg("no iterator");
+ return;
+ }
+ for (;;) {
+ S_Change change;
+ ostringstream msg;
+ try {
+ change = sess->get_change_next(iter);
+ } catch (const sysrepo_exception& ex) {
+ msg << "get change iterator next failed: " << ex.what();
+ LOG_WARN(netconf_logger, NETCONF_LOG_CHANGE_FAIL)
+ .arg(msg.str());
+ return;
+ }
+ if (!change) {
+ // End of changes, not an error.
+ return;
+ }
+ S_Val new_val = change->new_val();
+ S_Val old_val = change->old_val();
+ string report;
+ switch (change->oper()) {
+ case SR_OP_CREATED:
+ if (!new_val) {
+ LOG_WARN(netconf_logger, NETCONF_LOG_CHANGE_FAIL)
+ .arg("created but without a new value");
+ break;
+ }
+ msg << "created: " << new_val->to_string();
+ report = msg.str();
+ boost::erase_all(report, "\n");
+ LOG_DEBUG(netconf_logger, NETCONF_DBG_TRACE_DETAIL_DATA,
+ NETCONF_CONFIG_CHANGED_DETAIL)
+ .arg(report);
+ break;
+ case SR_OP_MODIFIED:
+ if (!old_val || !new_val) {
+ LOG_WARN(netconf_logger, NETCONF_LOG_CHANGE_FAIL)
+ .arg("modified but without an old or new value");
+ break;
+ }
+ msg << "modified: " << old_val->to_string()
+ << " => " << new_val->to_string();
+ report = msg.str();
+ boost::erase_all(report, "\n");
+ LOG_DEBUG(netconf_logger, NETCONF_DBG_TRACE_DETAIL_DATA,
+ NETCONF_CONFIG_CHANGED_DETAIL)
+ .arg(report);
+ break;
+ case SR_OP_DELETED:
+ if (!old_val) {
+ LOG_WARN(netconf_logger, NETCONF_LOG_CHANGE_FAIL)
+ .arg("deleted but without an old value");
+ break;
+ }
+ msg << "deleted: " << old_val->to_string();
+ report = msg.str();
+ boost::erase_all(report, "\n");
+ LOG_DEBUG(netconf_logger, NETCONF_DBG_TRACE_DETAIL_DATA,
+ NETCONF_CONFIG_CHANGED_DETAIL)
+ .arg(report);
+ break;
+ case SR_OP_MOVED:
+ if (!new_val) {
+ LOG_WARN(netconf_logger, NETCONF_LOG_CHANGE_FAIL)
+ .arg("moved but without a new value");
+ break;
+ }
+ msg << "moved: " << new_val->xpath();
+ if (!old_val) {
+ msg << " first";
+ } else {
+ msg << " after " << old_val->xpath();
+ }
+ report = msg.str();
+ boost::erase_all(report, "\n");
+ LOG_DEBUG(netconf_logger, NETCONF_DBG_TRACE_DETAIL_DATA,
+ NETCONF_CONFIG_CHANGED_DETAIL)
+ .arg(report);
+ break;
+ default:
+ msg << "unknown operation (" << change->oper() << ")";
+ LOG_WARN(netconf_logger, NETCONF_LOG_CHANGE_FAIL)
+ .arg(msg.str());
+ }
+ }
+}
+
+void
+NetconfAgent::announceShutdown() const {
+ isc::process::DControllerBasePtr& controller(NetconfController::instance());
+ if (controller) {
+ boost::dynamic_pointer_cast<NetconfController>(controller)
+ ->getNetconfProcess()
+ ->setShutdownFlag(true);
+ }
+}
+
+bool NetconfAgent::shouldShutdown() const {
+ return boost::dynamic_pointer_cast<NetconfController>(NetconfController::instance())
+ ->getNetconfProcess()
+ ->shouldShutdown();
+}
+
+} // namespace netconf
+} // namespace isc