27using namespace libyang;
28using namespace sysrepo;
33class NetconfAgentCallback {
39 : service_pair_(service_pair) {
55 sysrepo::ErrorCode module_change(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 event_notif(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";
130 optional<string>
const str(
131 notification_tree->printStr(DataFormat::JSON, PrintFlags::WithDefaultsExplicit));
132 string const tree(str ? *str :
string());
135 .arg(service_pair_.first)
159 cfg_mgr->getNetconfConfig()->getCfgServersMap();
160 for (
auto const& pair : *servers) {
170 for (
auto const& pair : *servers) {
186 string const& server(service_pair.first);
190 if (!configuration->getBootUpdate()) {
200 }
catch (exception
const& ex) {
202 msg <<
"createControlSocket failed with " << ex.what();
214 answer = comm->configGet(server);
216 }
catch (exception
const& ex) {
218 msg <<
"config-get command failed with " << ex.what();
226 msg <<
"config-get command returned " <<
answerToText(answer);
235 .arg(
"config-get command returned an empty configuration");
251 }
catch (exception
const& ex) {
261 vector<Module> modules;
264 modules = context.modules();
265 }
catch (Error
const& ex) {
269 for (Module
const& module : modules) {
270 string const name(module.name());
271 if (!module.revision()) {
273 "could not retrieve module revision for module " << name);
275 string const revision(*module.revision());
282 auto module =
modules_.find(module_name);
288 auto modrev = YANG_REVISIONS.find(module_name);
289 if (modrev == YANG_REVISIONS.end()) {
295 if (modrev->second != module->second) {
299 .arg(module->second);
307 bool faulty_model(
false);
309 for (
auto const& pair : *servers) {
318 "supported. Check logs for details.");
321 for (
auto const& modrev : YANG_REVISIONS) {
322 auto module =
modules_.find(modrev.first);
328 if (modrev.second != module->second) {
332 .arg(module->second);
339 string const& server(service_pair.first);
344 if (!configuration->getBootUpdate() ||
345 configuration->getModel().empty()) {
364 msg <<
"YANG configuration for "
365 << configuration->getModel()
377 }
catch (exception
const& ex) {
379 msg <<
"get YANG configuration for " << server
380 <<
" failed with " << ex.what();
389 }
catch (exception
const& ex) {
391 msg <<
"control socket creation failed with " << ex.what();
400 answer = comm->configSet(config, server);
402 }
catch (exception
const& ex) {
404 msg <<
"config-set command failed with " << ex.what();
412 msg <<
"config-set command returned " <<
answerToText(answer);
424 string const& server(service_pair.first);
426 string const& model(configuration->getModel());
430 if (!configuration->getSubscribeChanges() || model.empty()) {
436 auto callback = [=](Session session,
437 uint32_t subscription_id,
438 string_view module_name,
439 optional<string_view> sub_xpath,
441 uint32_t request_id) {
442 NetconfAgentCallback agent(service_pair);
443 return agent.module_change(session, subscription_id, module_name, sub_xpath, event, request_id);
446 SubscribeOptions options(SubscribeOptions::Default);
447 if (!configuration->getValidateChanges()) {
448 options = options | SubscribeOptions::DoneOnly;
450 Subscription subscription(
451 running_sess_->onModuleChange(model, callback, nullopt, 0, options));
452 subscriptions_.emplace(server, std::forward<Subscription>(subscription));
453 }
catch (exception
const& ex) {
455 msg <<
"module change subscribe failed with " << ex.what();
456 msg <<
"change subscription for model " << model <<
457 " failed with: " << ex.what();
460 .arg(configuration->getModel())
468 string const& server(service_pair.first);
470 string const& model(configuration->getModel());
474 if (!configuration->getSubscribeNotifications() || model.empty()) {
481 auto callback = [=](Session session,
482 uint32_t subscription_id,
483 NotificationType
const notification_type,
484 optional<DataNode>
const notification_tree,
485 NotificationTimeStamp
const timestamp) {
486 NetconfAgentCallback agent(service_pair);
487 return agent.event_notif(session, subscription_id, notification_type, notification_tree, timestamp);
490 Subscription subscription(
running_sess_->onNotification(model, callback));
491 subscriptions_.emplace(server, std::forward<Subscription>(subscription));
492 }
catch (exception
const& ex) {
494 msg <<
"event notification subscription for model " << model <<
495 " failed with: " << ex.what();
498 .arg(configuration->getModel())
506 string const& server(service_pair.first);
512 if (!configuration->getSubscribeChanges() ||
513 !configuration->getValidateChanges() ||
514 configuration->getModel().empty()) {
515 return (sysrepo::ErrorCode::Ok);
519 return (sysrepo::ErrorCode::Ok);
529 msg <<
"YANG configuration for "
530 << configuration->getModel()
535 return (sysrepo::ErrorCode::OperationFailed);
542 }
catch (exception
const& ex) {
544 msg <<
"get YANG configuration for " << server
545 <<
" failed with " << ex.what();
549 return (sysrepo::ErrorCode::ValidationFailed);
554 }
catch (exception
const& ex) {
556 msg <<
"createControlSocket failed with " << ex.what();
560 return (sysrepo::ErrorCode::Ok);
565 answer = comm->configTest(config, server);
567 }
catch (exception
const& ex) {
569 msg <<
"configTest failed with " << ex.what();
573 return (sysrepo::ErrorCode::ValidationFailed);
581 return (sysrepo::ErrorCode::ValidationFailed);
585 return (sysrepo::ErrorCode::Ok);
590 string const& server(service_pair.first);
594 if (!configuration->getSubscribeChanges() ||
595 configuration->getModel().empty()) {
596 return (sysrepo::ErrorCode::Ok);
600 return (sysrepo::ErrorCode::Ok);
615 msg <<
"YANG configuration for "
616 << configuration->getModel()
621 return (sysrepo::ErrorCode::ValidationFailed);
628 }
catch (exception
const& ex) {
630 msg <<
"get YANG configuration for " << server
631 <<
" failed with " << ex.what();
635 return (sysrepo::ErrorCode::ValidationFailed);
643 }
catch (exception
const& ex) {
645 msg <<
"createControlSocket failed with " << ex.what();
649 return (sysrepo::ErrorCode::Ok);
656 answer = comm->configSet(config, server);
658 }
catch (exception
const& ex) {
660 msg <<
"configSet failed with " << ex.what();
664 return (sysrepo::ErrorCode::ValidationFailed);
674 return (sysrepo::ErrorCode::ValidationFailed);
678 return (sysrepo::ErrorCode::Ok);
683 ostringstream stream;
684 stream <<
"/" << model <<
":*//.";
685 string const xpath(stream.str());
686 ChangeCollection
const changes(sess.getChanges(xpath));
687 for (Change
const&
change : changes) {
689 switch (
change.operation) {
690 case sysrepo::ChangeOperation::Created:
693 case sysrepo::ChangeOperation::Deleted:
696 case sysrepo::ChangeOperation::Modified:
699 case sysrepo::ChangeOperation::Moved:
703 msg <<
"unknown operation (" <<
change.operation <<
"): ";
705 string const path(
change.node.path());
707 SchemaNode
const& schema(
change.node.schema());
708 NodeType
const node_type(schema.nodeType());
709 if (node_type == NodeType::Container) {
710 msg <<
" (container)";
711 }
else if (node_type == NodeType::List) {
714 optional<string>
const str(
716 LeafBaseType::Unknown));
718 msg <<
" = " << *str;
732 boost::dynamic_pointer_cast<NetconfController>(controller)
733 ->getNetconfProcess()
734 ->setShutdownFlag(
true);
740 ->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.
ConstElementPtr parseAnswer(int &rcode, const ConstElementPtr &msg)
const int CONTROL_RESULT_SUCCESS
Status code indicating a successful operation.
std::string answerToText(const ConstElementPtr &msg)
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
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< CfgServersMap > CfgServersMapPtr
Defines a pointer to map of CfgServers.
const isc::log::MessageID NETCONF_CONFIG_CHANGED_DETAIL
std::pair< std::string, CfgServerPtr > CfgServersMapPair
Defines a iterator pairing of name and CfgServer.
const isc::log::MessageID NETCONF_SET_CONFIG_FAILED
std::shared_ptr< CfgServer > CfgServerPtr
Defines a pointer for CfgServer instances.
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< CfgControlSocket > CfgControlSocketPtr
Defines a pointer for CfgControlSocket instances.
const isc::log::MessageID NETCONF_NOTIFICATION_RECEIVED
const isc::log::MessageID NETCONF_UPDATE_CONFIG_COMPLETED
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< ControlSocketBase > ControlSocketBasePtr
Type definition for the pointer to the ControlSocketBase.
const isc::log::MessageID NETCONF_MODULE_MISSING_WARN
const isc::log::MessageID NETCONF_NOT_SUBSCRIBED_TO_NOTIFICATIONS
boost::shared_ptr< NetconfCfgMgr > NetconfCfgMgrPtr
Defines a shared pointer to NetconfCfgMgr.
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.