19 #include <boost/algorithm/string.hpp> 30 using libyang::S_Context;
31 using libyang::S_Module;
36 class NetconfAgentCallback {
42 : service_pair_(service_pair) {
58 sr_error_t module_change(S_Session sess,
59 const char* module_name,
63 ostringstream event_type;
68 event_type <<
"SR_EV_UPDATE";
71 event_type <<
"SR_EV_CHANGE";
74 event_type <<
"SR_EV_DONE";
77 event_type <<
"SR_EV_ABORT";
80 event_type <<
"SR_EV_ENABLED";
83 event_type <<
"SR_EV_RPC";
86 event_type <<
"UNKNOWN (" <<
event <<
")";
90 .arg(event_type.str());
91 NetconfAgent::logChanges(sess, module_name);
94 return (NetconfAgent::change(sess, service_pair_));
96 return (NetconfAgent::done(sess, service_pair_));
102 void event_notif(sysrepo::S_Session ,
103 sr_ev_notif_type_t
const notification_type,
105 sysrepo::S_Vals
const vals,
109 switch (notification_type) {
110 case SR_EV_NOTIF_REALTIME:
111 n =
"SR_EV_NOTIF_REALTIME";
113 case SR_EV_NOTIF_REPLAY:
114 n =
"SR_EV_NOTIF_REPLAY";
116 case SR_EV_NOTIF_REPLAY_COMPLETE:
117 n =
"SR_EV_NOTIF_REPLAY_COMPLETE";
119 case SR_EV_NOTIF_STOP:
120 n =
"SR_EV_NOTIF_STOP";
122 case SR_EV_NOTIF_SUSPENDED:
123 n =
"SR_EV_NOTIF_SUSPENDED";
125 case SR_EV_NOTIF_RESUMED:
126 n =
"SR_EV_NOTIF_RESUMED";
131 for (
size_t i(0); i < vals->val_cnt(); ++i) {
135 s << vals->val(i)->to_string();
140 .arg(service_pair_.first)
150 NetconfAgent::~NetconfAgent() {
164 cfg_mgr->getNetconfConfig()->getCfgServersMap();
165 for (
auto const& pair : *servers) {
173 checkModules(servers);
175 for (
auto const& pair : *servers) {
177 subscribeConfig(pair);
178 subscribeToNotifications(pair);
183 NetconfAgent::clear() {
184 for (
auto subs : subscriptions_) {
187 subscriptions_.clear();
188 running_sess_.reset();
189 startup_sess_.reset();
196 if (!service_pair.second->getBootUpdate()) {
206 }
catch (
const std::exception& ex) {
208 msg <<
"createControlSocket failed with " << ex.what();
210 .arg(service_pair.first)
218 .arg(service_pair.first);
220 answer = comm->configGet(service_pair.first);
222 }
catch (
const std::exception& ex) {
224 msg <<
"config-get command failed with " << ex.what();
226 .arg(service_pair.first)
232 msg <<
"config-get command returned " <<
answerToText(answer);
234 .arg(service_pair.first)
240 .arg(service_pair.first)
241 .arg(
"config-get command returned an empty configuration");
246 .arg(service_pair.first)
251 NetconfAgent::initSysrepo() {
253 conn_ = make_shared<Connection>();
254 }
catch (
const std::exception& ex) {
259 startup_sess_.reset(
new Session(conn_, SR_DS_STARTUP));
260 running_sess_.reset(
new Session(conn_, SR_DS_RUNNING));
261 }
catch (
const std::exception& ex) {
270 void NetconfAgent::getModules() {
271 vector<S_Module> modules;
273 S_Context context(running_sess_->get_context());
274 modules = context->get_module_iter();
275 }
catch (
const sysrepo_exception& ex) {
279 for (S_Module
const& module : modules) {
280 if (!module->name()) {
283 string const name(module->name());
284 if (!module->rev() || !module->rev()->date()) {
286 "could not retrieve module revision for module " << name);
288 string const revision(module->rev()->date());
289 modules_.emplace(name, revision);
294 NetconfAgent::checkModule(
const string& module_name)
const {
295 auto module = modules_.find(module_name);
296 if (module == modules_.end()) {
301 auto modrev = YANG_REVISIONS.find(module_name);
302 if (modrev == YANG_REVISIONS.end()) {
308 if (modrev->second != module->second) {
312 .arg(module->second);
320 bool faulty_model(
false);
322 for (
auto pair : *servers) {
323 if (!checkModule(pair.second->getModel())) {
331 "supported. Check logs for details.");
334 for (
auto modrev : YANG_REVISIONS) {
335 auto module = modules_.find(modrev.first);
336 if (module == modules_.end()) {
341 if (modrev.second != module->second) {
345 .arg(module->second);
354 if (!service_pair.second->getBootUpdate() ||
355 service_pair.second->getModel().empty()) {
366 .arg(service_pair.first);
374 msg <<
"YANG configuration for " 375 << service_pair.second->getModel()
378 .arg(service_pair.first)
384 .arg(service_pair.first)
387 }
catch (
const std::exception& ex) {
389 msg <<
"get YANG configuration for " << service_pair.first
390 <<
" failed with " << ex.what();
392 .arg(service_pair.first)
399 }
catch (
const std::exception& ex) {
401 msg <<
"control socket creation failed with " << ex.what();
403 .arg(service_pair.first)
410 answer = comm->configSet(config, service_pair.first);
412 }
catch (
const std::exception& ex) {
414 msg <<
"config-set command failed with " << ex.what();
416 .arg(service_pair.first)
422 msg <<
"config-set command returned " <<
answerToText(answer);
424 .arg(service_pair.first)
429 .arg(service_pair.first);
434 std::string
const& model(service_pair.second->getModel());
438 if (!service_pair.second->getSubscribeChanges() ||
443 .arg(service_pair.first)
445 S_Subscribe subs(
new Subscribe(running_sess_));
446 auto callback = [=](sysrepo::S_Session sess,
const char* module_name,
447 const char* xpath, sr_event_t event,
449 NetconfAgentCallback agent(service_pair);
450 return agent.module_change(sess, module_name, xpath, event,
nullptr);
453 sr_subscr_options_t options = SR_SUBSCR_DEFAULT;
454 if (!service_pair.second->getValidateChanges()) {
455 options |= SR_SUBSCR_DONE_ONLY;
459 subs->module_change_subscribe(model.c_str(), callback,
nullptr, 0,
461 }
catch (
const std::exception& ex) {
463 msg <<
"module change subscribe failed with " << ex.what();
464 msg <<
"change subscription for model " << model <<
465 " failed with: " << ex.what();
467 .arg(service_pair.first)
468 .arg(service_pair.second->getModel())
472 subscriptions_.insert(make_pair(service_pair.first, subs));
478 std::string
const& model(service_pair.second->getModel());
481 if (!service_pair.second->getSubscribeNotifications() ||
486 .arg(service_pair.first)
489 S_Subscribe subscription(std::make_shared<Subscribe>(running_sess_));
490 auto callback = [=](sysrepo::S_Session session,
491 sr_ev_notif_type_t
const notification_type,
493 sysrepo::S_Vals
const vals,
495 NetconfAgentCallback agent(service_pair);
496 return agent.event_notif(session, notification_type, path, vals, timestamp,
nullptr);
499 subscription->event_notif_subscribe(model.c_str(), callback);
500 }
catch (
const std::exception& ex) {
502 msg <<
"event notification subscription for model " << model <<
503 " failed with: " << ex.what();
505 .arg(service_pair.first)
506 .arg(service_pair.second->getModel())
510 subscriptions_.emplace(service_pair.first, subscription);
518 if (!service_pair.second->getSubscribeChanges() ||
519 !service_pair.second->getValidateChanges() ||
520 service_pair.second->getModel().empty()) {
528 .arg(service_pair.first);
535 msg <<
"YANG configuration for " 536 << service_pair.second->getModel()
539 .arg(service_pair.first)
541 return (SR_ERR_OPERATION_FAILED);
545 .arg(service_pair.first)
548 }
catch (
const std::exception& ex) {
550 msg <<
"get YANG configuration for " << service_pair.first
551 <<
" failed with " << ex.what();
553 .arg(service_pair.first)
555 return (SR_ERR_VALIDATION_FAILED);;
560 }
catch (
const std::exception& ex) {
562 msg <<
"createControlSocket failed with " << ex.what();
564 .arg(service_pair.first)
571 answer = comm->configTest(config, service_pair.first);
573 }
catch (
const std::exception& ex) {
575 msg <<
"configTest failed with " << ex.what();
577 .arg(service_pair.first)
579 return (SR_ERR_VALIDATION_FAILED);
585 .arg(service_pair.first)
587 return (SR_ERR_VALIDATION_FAILED);
590 .arg(service_pair.first);
597 if (!service_pair.second->getSubscribeChanges() ||
598 service_pair.second->getModel().empty()) {
609 .arg(service_pair.first);
618 msg <<
"YANG configuration for " 619 << service_pair.second->getModel()
622 .arg(service_pair.first)
624 return (SR_ERR_VALIDATION_FAILED);
628 .arg(service_pair.first)
631 }
catch (
const std::exception& ex) {
633 msg <<
"get YANG configuration for " << service_pair.first
634 <<
" failed with " << ex.what();
636 .arg(service_pair.first)
638 return (SR_ERR_VALIDATION_FAILED);
646 }
catch (
const std::exception& ex) {
648 msg <<
"createControlSocket failed with " << ex.what();
650 .arg(service_pair.first)
659 answer = comm->configSet(config, service_pair.first);
661 }
catch (
const std::exception& ex) {
663 msg <<
"configSet failed with " << ex.what();
665 .arg(service_pair.first)
667 return (SR_ERR_VALIDATION_FAILED);
675 .arg(service_pair.first)
677 return (SR_ERR_VALIDATION_FAILED);
680 .arg(service_pair.first);
685 NetconfAgent::logChanges(S_Session sess,
const string& model) {
686 ostringstream stream;
687 stream <<
"/" << model <<
":*//.";
688 std::string
const xpath(stream.str());
689 S_Iter_Change iter = sess->get_changes_iter(xpath.c_str());
699 change = sess->get_change_next(iter);
700 }
catch (
const sysrepo_exception& ex) {
701 msg <<
"get change iterator next failed: " << ex.what();
710 S_Val new_val = change->new_val();
711 S_Val old_val = change->old_val();
713 switch (change->oper()) {
717 .arg(
"created but without a new value");
720 msg <<
"created: " << new_val->to_string();
722 boost::erase_all(report,
"\n");
728 if (!old_val || !new_val) {
730 .arg(
"modified but without an old or new value");
733 msg <<
"modified: " << old_val->to_string()
734 <<
" => " << new_val->to_string();
736 boost::erase_all(report,
"\n");
744 .arg(
"deleted but without an old value");
747 msg <<
"deleted: " << old_val->to_string();
749 boost::erase_all(report,
"\n");
757 .arg(
"moved but without a new value");
760 msg <<
"moved: " << new_val->xpath();
764 msg <<
" after " << old_val->xpath();
767 boost::erase_all(report,
"\n");
773 msg <<
"unknown operation (" << change->oper() <<
")";
781 NetconfAgent::announceShutdown()
const {
785 ->getNetconfProcess()
786 ->setShutdownFlag(
true);
790 bool NetconfAgent::shouldShutdown()
const {
791 return boost::dynamic_pointer_cast<
NetconfController>(NetconfController::instance())
792 ->getNetconfProcess()
const isc::log::MessageID NETCONF_SUBSCRIBE_CONFIG
boost::shared_ptr< DControllerBase > DControllerBasePtr
#define LOG_WARN(LOGGER, MESSAGE)
Macro to conveniently test warn output and log it.
const isc::log::MessageID NETCONF_GET_CONFIG
const isc::log::MessageID NETCONF_SET_CONFIG_FAILED
const isc::log::MessageID NETCONF_GET_CONFIG_FAILED
DHCP configuration translation between YANG and JSON.
#define LOG_INFO(LOGGER, MESSAGE)
Macro to conveniently test info output and log it.
const int CONTROL_RESULT_SUCCESS
Status code indicating a successful operation.
boost::shared_ptr< ControlSocketBase > ControlSocketBasePtr
Type definition for the pointer to the ControlSocketBase.
const isc::log::MessageID NETCONF_BOOT_UPDATE_COMPLETED
const int NETCONF_DBG_TRACE_DETAIL_DATA
Additional information.
void prettyPrint(ConstElementPtr element, std::ostream &out, unsigned indent, unsigned step)
Pretty prints the data into stream.
std::pair< std::string, CfgServerPtr > CfgServersMapPair
Defines a iterator pairing of name and CfgServer.
const isc::log::MessageID NETCONF_VALIDATE_CONFIG_COMPLETED
#define LOG_ERROR(LOGGER, MESSAGE)
Macro to conveniently test error output and log it.
const isc::log::MessageID NETCONF_UPDATE_CONFIG
const isc::log::MessageID NETCONF_SET_CONFIG_STARTED
virtual const char * what() const
Returns a C-style character string of the cause of the exception.
const isc::log::MessageID NETCONF_LOG_CHANGE_FAIL
const isc::log::MessageID NETCONF_NOTIFICATION_RECEIVED
const isc::log::MessageID NETCONF_SET_CONFIG
const isc::log::MessageID NETCONF_VALIDATE_CONFIG_STARTED
isc::data::ElementPtr getConfig()
Get and translate the whole DHCP server configuration from YANG to JSON.
#define isc_throw(type, stream)
A shortcut macro to insert known values into exception arguments.
std::string answerToText(const ConstElementPtr &msg)
const isc::log::MessageID NETCONF_VALIDATE_CONFIG
A generic exception that is thrown when an unexpected error condition occurs.
const isc::log::MessageID NETCONF_CONFIG_CHANGE_EVENT
boost::shared_ptr< const Element > ConstElementPtr
const isc::log::MessageID NETCONF_MODULE_REVISION_ERR
isc::log::Logger netconf_logger(NETCONF_LOGGER_NAME)
Base logger for the netconf agent.
const isc::log::MessageID NETCONF_SUBSCRIBE_CONFIG_FAILED
ConstElementPtr parseAnswer(int &rcode, const ConstElementPtr &msg)
Process Controller for Netconf Process.
Defines the logger used by the top-level component of kea-lfc.
const isc::log::MessageID NETCONF_UPDATE_CONFIG_STARTED
const isc::log::MessageID NETCONF_MODULE_REVISION_WARN
boost::shared_ptr< NetconfCfgMgr > NetconfCfgMgrPtr
Defines a shared pointer to NetconfCfgMgr.
const isc::log::MessageID NETCONF_UPDATE_CONFIG_FAILED
const isc::log::MessageID NETCONF_MODULE_MISSING_ERR
This file contains several functions and constants that are used for handling commands and responses ...
ControlSocketBasePtr createControlSocket(CfgControlSocketPtr ctrl_sock)
Factory template for control sockets.
const isc::log::MessageID NETCONF_SUBSCRIBE_NOTIFICATIONS
const isc::log::MessageID NETCONF_GET_CONFIG_STARTED
#define LOG_DEBUG(LOGGER, LEVEL, MESSAGE)
Macro to conveniently test debug output and log it.
Contains declarations for loggers used by the Kea netconf agent.
const isc::log::MessageID NETCONF_CONFIG_CHANGED_DETAIL
const isc::log::MessageID NETCONF_SUBSCRIBE_NOTIFICATIONS_FAILED
const isc::log::MessageID NETCONF_VALIDATE_CONFIG_FAILED
const isc::log::MessageID NETCONF_UPDATE_CONFIG_COMPLETED
const isc::log::MessageID NETCONF_MODULE_MISSING_WARN
const isc::log::MessageID NETCONF_VALIDATE_CONFIG_REJECTED
boost::shared_ptr< CfgControlSocket > CfgControlSocketPtr
Defines a pointer for CfgControlSocket instances.
boost::shared_ptr< CfgServersMap > CfgServersMapPtr
Defines a pointer to map of CfgServers.