27using namespace libyang;
28using namespace sysrepo;
33class NetconfAgentCallback {
39 : service_pair_(service_pair) {
55 sysrepo::ErrorCode moduleChange(Session sess,
57 string_view module_name,
58 optional<string_view> ,
61 ostringstream event_type;
66 event_type <<
"Event::Update";
69 event_type <<
"Event::Change";
72 event_type <<
"Event::Done";
75 event_type <<
"Event::Abort";
78 event_type <<
"Event::Enabled";
81 event_type <<
"Event::RPC";
84 event_type <<
"UNKNOWN (" <<
event <<
")";
88 .arg(event_type.str());
96 return (sysrepo::ErrorCode::Ok);
100 void eventNotif(Session ,
102 NotificationType
const notification_type,
103 optional<DataNode>
const notification_tree,
104 NotificationTimeStamp
const ) {
106 switch (notification_type) {
107 case NotificationType::Realtime:
108 n =
"NotificationType::Realtime";
110 case NotificationType::Replay:
111 n =
"NotificationType::Replay";
113 case NotificationType::ReplayComplete:
114 n =
"NotificationType::ReplayComplete";
116 case NotificationType::Terminated:
117 n =
"NotificationType::Terminated";
119 case NotificationType::Modified:
120 n =
"NotificationType::Modified";
122 case NotificationType::Suspended:
123 n =
"NotificationType::Suspended";
125 case NotificationType::Resumed:
126 n =
"NotificationType::Resumed";
131 if (notification_tree) {
132 optional<string>
const str(
133 notification_tree->printStr(DataFormat::JSON, PrintFlags::WithDefaultsExplicit));
140 .arg(service_pair_.first)
164 cfg_mgr->getNetconfConfig()->getCfgServersMap();
165 for (
auto const& pair : *servers) {
175 for (
auto const& pair : *servers) {
191 string const& server(service_pair.first);
195 if (!configuration->getBootUpdate()) {
205 }
catch (exception
const& ex) {
207 msg <<
"createControlSocket failed with " << ex.what();
219 answer = comm->configGet(server);
221 }
catch (exception
const& ex) {
223 msg <<
"config-get command failed with " << ex.what();
231 msg <<
"config-get command returned " <<
answerToText(answer);
240 .arg(
"config-get command returned an empty configuration");
256 }
catch (exception
const& ex) {
266 vector<Module> modules;
269 modules = context.modules();
270 }
catch (Error
const& ex) {
274 for (Module
const& module : modules) {
275 string const name(module.name());
276 if (!module.revision()) {
279 string const revision(*module.revision());
286 auto module = modules_.find(module_name);
292 auto modrev = YANG_REVISIONS.find(module_name);
293 if (modrev == YANG_REVISIONS.end()) {
299 if (modrev->second != module->second) {
303 .arg(module->second);
311 bool faulty_model(
false);
313 for (
auto const& pair : *servers) {
322 "supported. Check logs for details.");
325 for (
auto const& modrev : YANG_REVISIONS) {
326 auto module = modules_.find(modrev.first);
332 if (modrev.second != module->second) {
336 .arg(module->second);
343 string const& server(service_pair.first);
348 if (!configuration->getBootUpdate() ||
349 configuration->getModel().empty()) {
368 msg <<
"YANG configuration for "
369 << configuration->getModel()
381 }
catch (exception
const& ex) {
383 msg <<
"get YANG configuration for " << server
384 <<
" failed with " << ex.what();
393 }
catch (exception
const& ex) {
395 msg <<
"control socket creation failed with " << ex.what();
404 answer = comm->configSet(config, server);
406 }
catch (exception
const& ex) {
408 msg <<
"config-set command failed with " << ex.what();
416 msg <<
"config-set command returned " <<
answerToText(answer);
428 string const& server(service_pair.first);
430 string const& model(configuration->getModel());
434 if (!configuration->getSubscribeChanges() || model.empty()) {
440 auto callback = [=](Session session,
441 uint32_t subscription_id,
442 string_view module_name,
443 optional<string_view> sub_xpath,
445 uint32_t request_id) {
446 NetconfAgentCallback agent(service_pair);
447 return agent.moduleChange(session, subscription_id, module_name, sub_xpath, event,
451 SubscribeOptions options(SubscribeOptions::Default);
452 if (!configuration->getValidateChanges()) {
453 options = options | SubscribeOptions::DoneOnly;
455 auto exception_handler = [model](std::exception& ex) {
460 Subscription subscription(
461 running_sess_->onModuleChange(model, callback, nullopt, 0, options, exception_handler));
462 subscriptions_.emplace(server, std::forward<Subscription>(subscription));
463 }
catch (exception
const& ex) {
465 msg <<
"module change subscribe failed with " << ex.what();
466 msg <<
"change subscription for model " << model <<
" failed with: " << ex.what();
469 .arg(configuration->getModel())
477 string const& server(service_pair.first);
479 string const& model(configuration->getModel());
483 if (!configuration->getSubscribeNotifications() || model.empty()) {
490 auto callback = [=](Session session,
491 uint32_t subscription_id,
492 NotificationType
const notification_type,
493 optional<DataNode>
const notification_tree,
494 NotificationTimeStamp
const timestamp) {
495 NetconfAgentCallback agent(service_pair);
496 return agent.eventNotif(session, subscription_id, notification_type, notification_tree,
500 auto exception_handler = [model](std::exception& ex) {
505 Subscription subscription(
running_sess_->onNotification(model, callback, nullopt, nullopt,
506 nullopt, SubscribeOptions::Default,
508 subscriptions_.emplace(server, std::forward<Subscription>(subscription));
509 }
catch (exception
const& ex) {
511 msg <<
"event notification subscription for model " << model <<
512 " failed with: " << ex.what();
515 .arg(configuration->getModel())
523 string const& server(service_pair.first);
529 if (!configuration->getSubscribeChanges() ||
530 !configuration->getValidateChanges() ||
531 configuration->getModel().empty()) {
532 return (sysrepo::ErrorCode::Ok);
536 return (sysrepo::ErrorCode::Ok);
546 msg <<
"YANG configuration for "
547 << configuration->getModel()
552 return (sysrepo::ErrorCode::OperationFailed);
559 }
catch (exception
const& ex) {
561 msg <<
"get YANG configuration for " << server
562 <<
" failed with " << ex.what();
566 return (sysrepo::ErrorCode::ValidationFailed);
571 }
catch (exception
const& ex) {
573 msg <<
"createControlSocket failed with " << ex.what();
577 return (sysrepo::ErrorCode::Ok);
582 answer = comm->configTest(config, server);
584 }
catch (exception
const& ex) {
586 msg <<
"configTest failed with " << ex.what();
590 return (sysrepo::ErrorCode::ValidationFailed);
598 return (sysrepo::ErrorCode::ValidationFailed);
602 return (sysrepo::ErrorCode::Ok);
607 string const& server(service_pair.first);
611 if (!configuration->getSubscribeChanges() ||
612 configuration->getModel().empty()) {
613 return (sysrepo::ErrorCode::Ok);
617 return (sysrepo::ErrorCode::Ok);
632 msg <<
"YANG configuration for "
633 << configuration->getModel()
638 return (sysrepo::ErrorCode::ValidationFailed);
645 }
catch (exception
const& ex) {
647 msg <<
"get YANG configuration for " << server
648 <<
" failed with " << ex.what();
652 return (sysrepo::ErrorCode::ValidationFailed);
660 }
catch (exception
const& ex) {
662 msg <<
"createControlSocket failed with " << ex.what();
666 return (sysrepo::ErrorCode::Ok);
673 answer = comm->configSet(config, server);
675 }
catch (exception
const& ex) {
677 msg <<
"configSet failed with " << ex.what();
681 return (sysrepo::ErrorCode::ValidationFailed);
691 return (sysrepo::ErrorCode::ValidationFailed);
695 return (sysrepo::ErrorCode::Ok);
700 ostringstream stream;
701 stream <<
"/" << model <<
":*//.";
702 string const xpath(stream.str());
703 ChangeCollection
const changes(sess.getChanges(xpath));
704 for (Change
const&
change : changes) {
706 switch (
change.operation) {
707 case sysrepo::ChangeOperation::Created:
710 case sysrepo::ChangeOperation::Deleted:
713 case sysrepo::ChangeOperation::Modified:
716 case sysrepo::ChangeOperation::Moved:
720 msg <<
"unknown operation (" <<
change.operation <<
"): ";
722 string const path(
change.node.path());
724 SchemaNode
const& schema(
change.node.schema());
725 NodeType
const node_type(schema.nodeType());
726 if (node_type == NodeType::Container) {
727 msg <<
" (container)";
728 }
else if (node_type == NodeType::List) {
731 optional<string>
const str(
733 LeafBaseType::Unknown));
735 msg <<
" = " << *str;
749 boost::dynamic_pointer_cast<NetconfController>(controller)
750 ->getNetconfProcess()
751 ->setShutdownFlag(
true);
758 ->getNetconfProcess()
virtual const char * what() const
Returns a C-style character string of the cause of the exception.
A generic exception that is thrown when an unexpected error condition occurs.
void subscribeToDataChanges(const CfgServersMapPair &service_pair)
Subscribe changes for a module in YANG datastore.
std::optional< sysrepo::Session > running_sess_
Sysrepo running datastore session.
void getModules()
Retrieve names and revisions of installed modules through the sysrepo API.
static void logChanges(sysrepo::Session sess, std::string_view const &model)
Log changes.
void yangConfig(const CfgServersMapPair &service_pair)
Retrieve Kea server configuration from the YANG startup datastore and applies it to servers.
virtual ~NetconfAgent()
Destructor (call clear).
void initSysrepo()
Initialize sysrepo sessions.
void keaConfig(const CfgServersMapPair &service_pair)
Get and display Kea server configuration.
void announceShutdown() const
Set the shutdown flag of the process to true so that it can exit at the earliest convenient time.
void checkModules(CfgServersMapPtr const &servers={}) const
Check module availability.
std::map< const std::string, sysrepo::Subscription > subscriptions_
Subscription map.
bool shouldShutdown() const
Check the shutdown flag of the process.
static sysrepo::ErrorCode change(sysrepo::Session sess, const CfgServersMapPair &service_pair)
Event::Change callback.
std::optional< sysrepo::Session > startup_sess_
Sysrepo startup datastore session.
static sysrepo::ErrorCode done(sysrepo::Session sess, const CfgServersMapPair &service_pair)
Event::Done callback.
void init(NetconfCfgMgrPtr cfg_mgr)
Initialization.
void subscribeToNotifications(const CfgServersMapPair &service_pair)
Subscribe to notifications for a given YANG module.
bool checkModule(const std::string &module_name) const
Check essential module availability.
std::map< const std::string, const std::string > modules_
Available modules and revisions in Sysrepo.
static process::DControllerBasePtr & instance()
Static singleton instance method.
DHCP configuration translation between YANG and JSON.
isc::data::ElementPtr getConfig()
Translate the whole DHCP server configuration from YANG to JSON.
static isc::data::ElementPtr translateFromYang(std::optional< libyang::DataNode > data_node)
Translate basic value from the given YANG data node to JSON element.
static std::optional< std::string > translateToYang(isc::data::ConstElementPtr const &elem, libyang::LeafBaseType const type)
Translate basic value from JSON to YANG.
This file contains several functions and constants that are used for handling commands and responses ...
#define isc_throw(type, stream)
A shortcut macro to insert known values into exception arguments.
#define LOG_ERROR(LOGGER, MESSAGE)
Macro to conveniently test error output and log it.
#define LOG_INFO(LOGGER, MESSAGE)
Macro to conveniently test info output and log it.
#define LOG_WARN(LOGGER, MESSAGE)
Macro to conveniently test warn output and log it.
#define LOG_DEBUG(LOGGER, LEVEL, MESSAGE)
Macro to conveniently test debug output and log it.
std::string answerToText(const ConstElementPtr &msg)
Converts answer to printable text.
ConstElementPtr parseAnswer(int &rcode, const ConstElementPtr &msg)
Parses a standard config/command level answer and returns arguments or text status code.
const int CONTROL_RESULT_SUCCESS
Status code indicating a successful operation.
void prettyPrint(ConstElementPtr element, std::ostream &out, unsigned indent, unsigned step)
Pretty prints the data into stream.
boost::shared_ptr< const Element > ConstElementPtr
boost::shared_ptr< Element > ElementPtr
std::pair< std::string, CfgServerPtr > CfgServersMapPair
Defines a iterator pairing of name and CfgServer.
const isc::log::MessageID NETCONF_VALIDATE_CONFIG_FAILED
const int NETCONF_DBG_TRACE_DETAIL_DATA
Additional information.
const isc::log::MessageID NETCONF_GET_CONFIG_STARTED
const isc::log::MessageID NETCONF_VALIDATE_CONFIG_REJECTED
const isc::log::MessageID NETCONF_VALIDATE_CONFIG_COMPLETED
const isc::log::MessageID NETCONF_CONFIG_CHANGE_EVENT
isc::log::Logger netconf_logger(NETCONF_LOGGER_NAME)
Base logger for the netconf agent.
const isc::log::MessageID NETCONF_VALIDATE_CONFIG
const isc::log::MessageID NETCONF_GET_CONFIG_FAILED
const isc::log::MessageID NETCONF_MODULE_REVISION_WARN
const isc::log::MessageID NETCONF_GET_CONFIG
std::shared_ptr< ControlSocketBase > ControlSocketBasePtr
Type definition for the pointer to the ControlSocketBase.
const isc::log::MessageID NETCONF_CONFIG_CHANGED_DETAIL
const isc::log::MessageID NETCONF_NOTIFICATION_INTERNAL_ERROR
const isc::log::MessageID NETCONF_SET_CONFIG_FAILED
const isc::log::MessageID NETCONF_VALIDATE_CONFIG_STARTED
const isc::log::MessageID NETCONF_SUBSCRIBE_CONFIG
const isc::log::MessageID NETCONF_MODULE_MISSING_ERR
std::shared_ptr< CfgServer > CfgServerPtr
Defines a pointer for CfgServer instances.
boost::shared_ptr< NetconfCfgMgr > NetconfCfgMgrPtr
Defines a shared pointer to NetconfCfgMgr.
const isc::log::MessageID NETCONF_NOTIFICATION_RECEIVED
const isc::log::MessageID NETCONF_MODULE_CHANGE_INTERNAL_ERROR
const isc::log::MessageID NETCONF_UPDATE_CONFIG_COMPLETED
std::shared_ptr< CfgControlSocket > CfgControlSocketPtr
Defines a pointer for CfgControlSocket instances.
const isc::log::MessageID NETCONF_MODULE_REVISION_ERR
const isc::log::MessageID NETCONF_SUBSCRIBE_NOTIFICATIONS
const isc::log::MessageID NETCONF_SET_CONFIG_STARTED
const isc::log::MessageID NETCONF_SUBSCRIBE_CONFIG_FAILED
const isc::log::MessageID NETCONF_UPDATE_CONFIG_FAILED
const isc::log::MessageID NETCONF_BOOT_UPDATE_COMPLETED
const isc::log::MessageID NETCONF_SET_CONFIG
const isc::log::MessageID NETCONF_UPDATE_CONFIG
const isc::log::MessageID NETCONF_UPDATE_CONFIG_STARTED
ControlSocketBasePtr controlSocketFactory(CfgControlSocketPtr ctrl_sock)
Factory function for control sockets.
std::shared_ptr< CfgServersMap > CfgServersMapPtr
Defines a pointer to map of CfgServers.
const isc::log::MessageID NETCONF_MODULE_MISSING_WARN
const isc::log::MessageID NETCONF_NOT_SUBSCRIBED_TO_NOTIFICATIONS
boost::shared_ptr< DControllerBase > DControllerBasePtr
Defines the logger used by the top-level component of kea-lfc.
Contains declarations for loggers used by the Kea netconf agent.