18 #include <boost/algorithm/string.hpp>
26 #ifndef HAVE_PRE_0_7_6_SYSREPO
33 class NetconfAgentCallback :
public Callback {
39 : service_pair_(service_pair) {
55 int module_change(S_Session sess,
57 sr_notif_event_t event,
59 if (NetconfProcess::shut_down) {
60 return (SR_ERR_DISCONNECT);
62 ostringstream event_type;
65 event_type <<
"VERIFY";
68 event_type <<
"APPLY";
71 event_type <<
"ABORT";
74 event_type <<
"ENABLED";
77 event_type <<
"UNKNOWN (" <<
event <<
")";
81 .arg(event_type.str());
82 string xpath =
"/" + service_pair_.second->getModel() +
":";
83 NetconfAgent::logChanges(sess, xpath +
"config");
84 if (NetconfProcess::shut_down) {
85 return (SR_ERR_DISCONNECT);
89 return (NetconfAgent::validate(sess, service_pair_));
91 return (NetconfAgent::update(sess, service_pair_));
99 class NetconfAgentInstallCallback :
public Callback {
109 void module_install(
const char* module_name,
110 const char* revision,
119 .arg(revision ? revision :
"unknown");
128 NetconfAgent::NetconfAgent() {
131 NetconfAgent::~NetconfAgent() {
138 if (!cfg_mgr || NetconfProcess::shut_down) {
142 cfg_mgr->getNetconfConfig()->getCfgServersMap();
143 for (
auto pair : *servers) {
144 if (NetconfProcess::shut_down) {
150 if (NetconfProcess::shut_down) {
154 if (NetconfProcess::shut_down) {
160 if (NetconfProcess::shut_down) {
165 bool can_start =
true;
166 for (
auto pair : *servers) {
167 can_start = can_start && checkModule(pair.second->getModel());
168 if (NetconfProcess::shut_down) {
173 cerr <<
"An essential YANG module / revision is missing."
175 <<
"The environment is not suitable for running kea-netconf."
179 if (NetconfProcess::shut_down) {
185 if (NetconfProcess::shut_down) {
189 for (
auto pair : *servers) {
190 if (NetconfProcess::shut_down) {
194 if (NetconfProcess::shut_down) {
197 subscribeConfig(pair);
198 if (NetconfProcess::shut_down) {
205 NetconfAgent::clear() {
207 NetconfProcess::shut_down =
true;
208 for (
auto subs : subscriptions_) {
211 subscriptions_.clear();
212 running_sess_.reset();
213 startup_sess_.reset();
220 if (!service_pair.second->getBootUpdate()) {
230 }
catch (
const std::exception& ex) {
232 msg <<
"createControlSocket failed with " << ex.what();
234 .arg(service_pair.first)
242 .arg(service_pair.first);
244 answer = comm->configGet(service_pair.first);
246 }
catch (
const std::exception& ex) {
248 msg <<
"config-get command failed with " << ex.what();
250 .arg(service_pair.first)
254 if (NetconfProcess::shut_down) {
259 msg <<
"config-get command returned " <<
answerToText(answer);
261 .arg(service_pair.first)
267 .arg(service_pair.first)
268 .arg(
"config-get command returned an empty configuration");
273 .arg(service_pair.first)
278 NetconfAgent::initSysrepo() {
280 conn_.reset(
new Connection(NetconfController::netconf_app_name_,
281 SR_CONN_DAEMON_REQUIRED));
282 }
catch (
const std::exception& ex) {
285 if (NetconfProcess::shut_down) {
290 startup_sess_.reset(
new Session(conn_, SR_DS_STARTUP));
291 if (NetconfProcess::shut_down) {
294 running_sess_.reset(
new Session(conn_, SR_DS_RUNNING));
295 }
catch (
const std::exception& ex) {
299 if (NetconfProcess::shut_down) {
304 S_Yang_Schemas schemas = startup_sess_->list_schemas();
305 for (
size_t i = 0; i < schemas->schema_cnt(); ++i) {
306 if (NetconfProcess::shut_down) {
309 if (!schemas->schema(i) ||
310 !schemas->schema(i)->module_name()) {
314 string module = schemas->schema(i)->module_name();
315 if (!schemas->schema(i)->revision() ||
316 !schemas->schema(i)->revision()->revision()) {
320 string revision = schemas->schema(i)->revision()->revision();
321 modules_.insert(make_pair(module, revision));
323 }
catch (
const sysrepo_exception& ex) {
326 if (NetconfProcess::shut_down) {
334 S_Subscribe subs(
new Subscribe(startup_sess_));
335 S_Callback cb(
new NetconfAgentInstallCallback());
336 subs->module_install_subscribe(cb);
337 subscriptions_.insert(make_pair(
"__install__", subs));
338 }
catch (
const sysrepo_exception& ex) {
345 NetconfAgent::checkModule(
const string& module_name)
const {
346 if (module_name.empty()) {
349 auto module = modules_.find(module_name);
350 if (module == modules_.end()) {
355 auto modrev = YANG_REVISIONS.find(module_name);
356 if (modrev == YANG_REVISIONS.end()) {
362 if (modrev->second != module->second) {
366 .arg(module->second);
373 NetconfAgent::checkModules()
const {
374 for (
auto modrev : YANG_REVISIONS) {
375 if (NetconfProcess::shut_down) {
378 auto module = modules_.find(modrev.first);
379 if (module == modules_.end()) {
384 if (modrev.second != module->second) {
388 .arg(module->second);
397 if (NetconfProcess::shut_down ||
398 !service_pair.second->getBootUpdate() ||
399 service_pair.second->getModel().empty()) {
410 .arg(service_pair.first);
418 msg <<
"YANG configuration for "
419 << service_pair.second->getModel()
422 .arg(service_pair.first)
428 .arg(service_pair.first)
431 }
catch (
const std::exception& ex) {
433 msg <<
"get YANG configuration for " << service_pair.first
434 <<
" failed with " << ex.what();
436 .arg(service_pair.first)
440 if (NetconfProcess::shut_down) {
446 }
catch (
const std::exception& ex) {
448 msg <<
"control socket creation failed with " << ex.what();
450 .arg(service_pair.first)
454 if (NetconfProcess::shut_down) {
460 answer = comm->configSet(config, service_pair.first);
462 }
catch (
const std::exception& ex) {
464 msg <<
"config-set command failed with " << ex.what();
466 .arg(service_pair.first)
472 msg <<
"config-set command returned " <<
answerToText(answer);
474 .arg(service_pair.first)
479 .arg(service_pair.first);
486 if (NetconfProcess::shut_down ||
487 !service_pair.second->getSubscribeChanges() ||
488 service_pair.second->getModel().empty()) {
492 .arg(service_pair.first)
493 .arg(service_pair.second->getModel());
494 S_Subscribe subs(
new Subscribe(running_sess_));
495 S_Callback cb(
new NetconfAgentCallback(service_pair));
497 sr_subscr_options_t options = SR_SUBSCR_DEFAULT;
498 if (!service_pair.second->getValidateChanges()) {
499 options |= SR_SUBSCR_APPLY_ONLY;
503 subs->module_change_subscribe(service_pair.second->getModel().c_str(),
505 }
catch (
const std::exception& ex) {
507 msg <<
"module change subscribe failed with " << ex.what();
509 .arg(service_pair.first)
510 .arg(service_pair.second->getModel())
515 subscriptions_.insert(make_pair(service_pair.first, subs));
523 if (NetconfProcess::shut_down ||
524 !service_pair.second->getSubscribeChanges() ||
525 !service_pair.second->getValidateChanges() ||
526 service_pair.second->getModel().empty()) {
534 .arg(service_pair.first);
541 msg <<
"YANG configuration for "
542 << service_pair.second->getModel()
545 .arg(service_pair.first)
547 return (SR_ERR_DISCONNECT);
551 .arg(service_pair.first)
554 }
catch (
const std::exception& ex) {
556 msg <<
"get YANG configuration for " << service_pair.first
557 <<
" failed with " << ex.what();
559 .arg(service_pair.first)
561 return (SR_ERR_VALIDATION_FAILED);;
563 if (NetconfProcess::shut_down) {
564 return (SR_ERR_DISCONNECT);
569 }
catch (
const std::exception& ex) {
571 msg <<
"createControlSocket failed with " << ex.what();
573 .arg(service_pair.first)
580 answer = comm->configTest(config, service_pair.first);
582 }
catch (
const std::exception& ex) {
584 msg <<
"configTest failed with " << ex.what();
586 .arg(service_pair.first)
588 return (SR_ERR_VALIDATION_FAILED);
594 .arg(service_pair.first)
596 return (SR_ERR_VALIDATION_FAILED);
599 .arg(service_pair.first);
606 if (NetconfProcess::shut_down ||
607 !service_pair.second->getSubscribeChanges() ||
608 service_pair.second->getModel().empty()) {
619 .arg(service_pair.first);
628 msg <<
"YANG configuration for "
629 << service_pair.second->getModel()
632 .arg(service_pair.first)
634 return (SR_ERR_VALIDATION_FAILED);
638 .arg(service_pair.first)
641 }
catch (
const std::exception& ex) {
643 msg <<
"get YANG configuration for " << service_pair.first
644 <<
" failed with " << ex.what();
646 .arg(service_pair.first)
648 return (SR_ERR_VALIDATION_FAILED);
650 if (NetconfProcess::shut_down) {
659 }
catch (
const std::exception& ex) {
661 msg <<
"createControlSocket failed with " << ex.what();
663 .arg(service_pair.first)
672 answer = comm->configSet(config, service_pair.first);
674 }
catch (
const std::exception& ex) {
676 msg <<
"configSet failed with " << ex.what();
678 .arg(service_pair.first)
680 return (SR_ERR_VALIDATION_FAILED);
688 .arg(service_pair.first)
690 return (SR_ERR_VALIDATION_FAILED);
693 .arg(service_pair.first);
698 NetconfAgent::logChanges(S_Session sess,
const string& model) {
699 if (NetconfProcess::shut_down) {
702 S_Iter_Change iter = sess->get_changes_iter(model.c_str());
709 if (NetconfProcess::shut_down) {
715 change = sess->get_change_next(iter);
716 }
catch (
const sysrepo_exception& ex) {
717 msg <<
"get change iterator next failed: " << ex.what();
726 if (NetconfProcess::shut_down) {
729 S_Val new_val = change->new_val();
730 S_Val old_val = change->old_val();
732 switch (change->oper()) {
736 .arg(
"created but without a new value");
739 msg <<
"created: " << new_val->to_string();
741 boost::erase_all(report,
"\n");
747 if (!old_val || !new_val) {
749 .arg(
"modified but without an old or new value");
752 msg <<
"modified: " << old_val->to_string()
753 <<
" => " << new_val->to_string();
755 boost::erase_all(report,
"\n");
763 .arg(
"deleted but without an old value");
766 msg <<
"deleted: " << old_val->to_string();
768 boost::erase_all(report,
"\n");
776 .arg(
"moved but without a new value");
779 msg <<
"moved: " << new_val->xpath();
783 msg <<
" after " << old_val->xpath();
786 boost::erase_all(report,
"\n");
792 msg <<
"unknown operation (" << change->oper() <<
")";
const isc::log::MessageID NETCONF_SUBSCRIBE_CONFIG
#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
const isc::log::MessageID NETCONF_LOG_CHANGE_FAIL
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.
virtual const char * what() const
Returns a C-style character string of the cause of the exception.
const isc::log::MessageID NETCONF_SUBSCRIBE_CONFIG_FAILED
ConstElementPtr parseAnswer(int &rcode, const ConstElementPtr &msg)
Defines the logger used by the top-level component of kea-dhcp-ddns.
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
const isc::log::MessageID NETCONF_MODULE_INSTALL
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_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_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.