03-15-2024 03:53 AM - edited 03-15-2024 03:55 AM
Hi,
I try to create a custom netconf notification from RFS to CFS in a LSA environment. But I could not understand some parts and stucked. In the notification I just want to send device names in the messages. I try to write all my questions in my head as below?
1. Why I need this yang part at RFS level as my understanding just to create a netconf subscription from CFS to RFS that CFS is using as an endpoint?
<sri-event-base-sync xlmns='http://ses.com/ctn-rfs-custom-notifications'>
<device>rtr-x</device>
</sri-event-base-sync>
4. I saw that this payload is creating automatically. Is has to be created programmatically like this or we can create a string xml and use it in the code with proper formatting?
def get_action_tag_value(device):
"""Create xml tag and value."""
action_tag = _ncs.XmlTag(ns_ncs.hash, ns_ncs.ncs_action) # pylint: disable=no-member
action_val = _ncs.Value(device, _ncs.C_BUF) # pylint: disable=no-member
return _ncs.TagValue(xmltag=action_tag, v=action_val) # pylint: disable=no-member
5. Custom notifications are handled at CFS level with device-notifications? Should I put this stream in RFS ncs.conf if yes where to put it?
6. Is there any good documentation for it as a reference?
Thanks and Best regards ...
Solved! Go to Solution.
03-18-2024 03:08 AM
Hi Mustafa,
the issue here is clear you can't use _ncs.str2hash(...) call in global context. If you do so the hash value will be == 0 what is obviously wrong
You must call _ncs.str2hash(...) i.e. inside your Action callback method like so:
class SendNotification(Action):
@Action.action
def cb_action(self, uinfo, name, kp, action_input, action_output, trans):
NS_HASH = _ncs.str2hash("http://ses.com/rfs-ctn-custom-notifications")
self.log.debug(f"Action={name} was called with kp={kp}")
send_nso_sri_sync_notification(NS_HASH, get_device_name_from_kpath(), self.log)
This worked for me without any issue.
Let me know if it works.
Best Regards
Jakub
03-15-2024 04:49 AM - edited 03-15-2024 04:52 AM
Hi Mustafa,
I will try to answer your questions as good as I can.
1. You need YANG definition of the notification on your RFS node and on your CFS node as well. For CFS you need to generate NED package out of your RFS package containing YANG definition of the notification.
2. Notification name has nothing to do with the stream name. You can use any stream name. BUT you need to extend your RFS and CFS ncs.conf file with the definition of new stream that you want to use.
Example:
<?xml version="1.0"?>
<!-- -*- nxml -*- -->
<!-- Example configuration file for ncs. -->
<ncs-config xmlns="http://tail-f.com/yang/tailf-ncs-config">
<notifications>
<event-streams>
<!-- Your custom stream definition -->
<stream>
<name>my-example-stream</name>
<description>Your description</description>
<replay-support>false</replay-support>
<builtin-replay-store>
<enabled>false</enabled>
<dir>${NCS_RUN_DIR}/state</dir>
<max-size>S10M</max-size>
<max-files>50</max-files>
</builtin-replay-store>
</stream>
</event-streams>
</notifications>
</ncs-config>
3. Data that will be send with notification must follow the YANG schema definition.
4. You need to use _ncs methods to create your payload because it will be internally converted to C-API calls of confd/ncs. You can create abstraction on your own that will convert xml string into _ncs calls if you want.
5. It depends you can try to use device-notifications but I had some problems with it I would recommend to use custom streams and yes you need to specify them in ncs.conf like described above. You can find your ncs.conf typically under /etc/ncs/ncs.conf but it depends on your NSO installation.
6. Unfortunately there is no documentation (as far as I know) on this specific topic.
Tip: Using Java API seems to be a bit easier but I haven't try it. Here is example but in japanese
Some NSO java docs: Link 1 Link 2
Best Regards
Jakub
03-15-2024 05:51 AM
Hi Jakub,
Thanks a lot for the explanation.
Where are we correlating these notification yang and stream xml. Because what I understood is, we are creating a daemon in python and register the stream like below.
ctx = self.register_notification_stream(state, None, ncs.dp.take_worker_socket(state, "ctn-nso-sri-notif-name", "ctn-nso-sri-notif-key"), "sri-event-base-sync-stream")
But in anywhere we have to correlate the notification in the yang and stream in python? Or just xml that we are creating should be align with the YANG is enough?
03-15-2024 06:15 AM - edited 03-15-2024 06:17 AM
Hi Mustafa,
Step 1.
Register stream in your python code with following call (you need to import _ncs):
# stream_name is the name you defined in your ncs.conf file
lctx = _ncs.dp.register_notification_stream(daemon_ctx, None, notif_socket, stream_name)
Step 2.
Send the notification with following call:
# message contains all needed info (notificaton name + data following YANG schema)
_ncs.dp.notification_send(lctx, time, message)
Tip: while using _ncs (low level API) package it is useful to take a look at C-API docs because _ncs package is just a wrapper around the original C-API
Best Regards
Jakub
03-17-2024 05:59 AM - edited 03-17-2024 08:08 AM
Hi Jakub,
I tried to create the logic. What I see is I can subsribe the stream via netconf-console but I can not send the stream to north-bound.
I am suspicious about it can be related with my notification XML. But I have a very simple one now and could not find where is wrong. Could you please check my code that you can realize a problem. I am creating the xml via get_nso_sri_sync_notif_message function. I can see that I am sending the stream without any error in package log but I can not see in netconf.log.
root@cd5b869559bc:/# netconf-console --host 127.0.0.1 --port 2022 --create-subscription sri-event-base-sync
<?xml version="1.0" encoding="UTF-8"?>
<rpc-reply xmlns="urn:ietf:params:xml:ns:netconf:base:1.0" message-id="1">
<ok/>
</rpc-reply>
03-17-2024 06:13 AM - edited 03-17-2024 08:12 AM
root@cd5b869559bc:/# tail -f /var/log/ncs/ncs-python-vm-rfs-ctn-custom-notifications.log
<INFO> 17-Mar-2024::12:53:35.429 rfs-ctn-custom-notifications ncs-dp-103617-rfs-ctn-custom-notifications:main:0-1-usid-75-sri-event-base-sync-action: - kp=/ncs:devices/device{rtr-vpe01.rig}/config/cisco-ios-xr:interface/GigabitEthernet-subinterface/GigabitEthernet{0/0/0/1.2}/ethernet/cfm/mep/domain{SES-L6}/service, op=MOP_ATTR_SET, newv=(<_ncs.Value type=C_UINT32(12) value='2147483650'>, <_ncs.Value type=C_NOEXISTS(1) value='NOEXISTS'>)
<INFO> 17-Mar-2024::12:53:35.429 rfs-ctn-custom-notifications ncs-dp-103617-rfs-ctn-custom-notifications:main:0-1-usid-75-sri-event-base-sync-action: - kp=/ncs:devices/device{rtr-vpe01.rig}/config/cisco-ios-xr:interface/GigabitEthernet-subinterface/GigabitEthernet{0/0/0/1.2}/ethernet/cfm/mep/domain{SES-L6}/service, op=MOP_ATTR_SET, newv=(<_ncs.Value type=C_UINT32(12) value='2147483653'>, <_ncs.Value type=C_NOEXISTS(1) value='NOEXISTS'>)
<INFO> 17-Mar-2024::12:53:35.430 rfs-ctn-custom-notifications ncs-dp-103617-rfs-ctn-custom-notifications:main:0-1-usid-75-sri-event-base-sync-action: - kp=/ncs:devices/device{rtr-vpe01.rig}/config/cisco-ios-xr:interface/GigabitEthernet-subinterface/GigabitEthernet{0/0/0/1.2}/ethernet/cfm/mep/domain{SES-L6}/mep-id, op=MOP_ATTR_SET, newv=(<_ncs.Value type=C_UINT32(12) value='2147483650'>, <_ncs.Value type=C_NOEXISTS(1) value='NOEXISTS'>)
<INFO> 17-Mar-2024::12:53:35.430 rfs-ctn-custom-notifications ncs-dp-103617-rfs-ctn-custom-notifications:main:0-1-usid-75-sri-event-base-sync-action: - kp=/ncs:devices/device{rtr-vpe01.rig}/config/cisco-ios-xr:interface/GigabitEthernet-subinterface/GigabitEthernet{0/0/0/1.2}/ethernet/cfm/mep/domain{SES-L6}/mep-id, op=MOP_ATTR_SET, newv=(<_ncs.Value type=C_UINT32(12) value='2147483653'>, <_ncs.Value type=C_NOEXISTS(1) value='NOEXISTS'>)
<INFO> 17-Mar-2024::12:53:35.430 rfs-ctn-custom-notifications ncs-dp-103617-rfs-ctn-custom-notifications:main:0-1-usid-75-sri-event-base-sync-action: - kp=/ncs:devices/device{rtr-vpe01.rig}/config/cisco-ios-xr:interface/GigabitEthernet-subinterface/GigabitEthernet{0/0/0/1.2}/mtu, op=MOP_ATTR_SET, newv=(<_ncs.Value type=C_UINT32(12) value='2147483650'>, <_ncs.Value type=C_NOEXISTS(1) value='NOEXISTS'>)
<INFO> 17-Mar-2024::12:53:35.430 rfs-ctn-custom-notifications ncs-dp-103617-rfs-ctn-custom-notifications:main:0-1-usid-75-sri-event-base-sync-action: - kp=/ncs:devices/device{rtr-vpe01.rig}/config/cisco-ios-xr:interface/GigabitEthernet-subinterface/GigabitEthernet{0/0/0/1.2}/mtu, op=MOP_ATTR_SET, newv=(<_ncs.Value type=C_UINT32(12) value='2147483653'>, <_ncs.Value type=C_NOEXISTS(1) value='NOEXISTS'>)
<INFO> 17-Mar-2024::12:53:35.430 rfs-ctn-custom-notifications ncs-dp-103617-rfs-ctn-custom-notifications:main:0-1-usid-75-sri-event-base-sync-action: - Generating notification message...
<INFO> 17-Mar-2024::12:53:35.431 rfs-ctn-custom-notifications ncs-dp-103617-rfs-ctn-custom-notifications:main:0-1-usid-75-sri-event-base-sync-action: - Notification message is set to=[_ncs.TagValue(tag=1870939232, ns=0), _ncs.TagValue(tag=669279939, ns=0), _ncs.TagValue(tag=1870939232, ns=0)]
<INFO> 17-Mar-2024::12:53:35.431 rfs-ctn-custom-notifications ncs-dp-103617-rfs-ctn-custom-notifications:main:0-1-usid-75-sri-event-base-sync-action: - Sending notification with=ctx=_ncs.dp.NotificationCtxRef, notif_time=_ncs.DateTime(year=2024, month=3, day=17, hour=12, min=53, sec=35, micro=431065, timezone=0, timezone_minutes=0), msg=[_ncs.TagValue(tag=1870939232, ns=0), _ncs.TagValue(tag=669279939, ns=0), _ncs.TagValue(tag=1870939232, ns=0)]...
<INFO> 17-Mar-2024::12:53:35.431 rfs-ctn-custom-notifications ncs-dp-103617-rfs-ctn-custom-notifications:main:0-1-usid-75-sri-event-base-sync-action: - Notification sent...
03-17-2024 06:24 AM
Hi Jakub,
I think I did the necessary things but it is not working for me. What I see is I am sending notification without any issue in package logs. But I can not see that I am sending in netconf logs. Also I can subscribe the stream, but as expected it can not get any notification. I have a very simple notification but may be I am doing a mistake when I am preparing the payload. Can you have a look at my code. I am preparing XML at get_nso_sri_sync_notif_message function.
module rfs-ctn-custom-notifications {
yang-version 1.1;
namespace "http://ses.com/rfs-ctn-custom-notifications";
prefix rfs-ctn-custom-notifications;
...
notification sri-event-base-sync {
leaf device-id {
description
"Name of the device that has a sub-interface resource change.";
type string;
}
}
...
}
"""Main ctn-nso-sri-notification code."""
import socket
from datetime import datetime, timezone
from typing import List
import _ncs
import ncs
from ncs.dp import Action
NOTIFCTX = None
OPER = {
1: "MOP_CREATED",
2: "MOP_DELETED",
3: "MOP_MODIFIED",
4: "MOP_VALUE_SET",
5: "MOP_MOVED_AFTER",
6: "MOP_ATTR_SET",
}
NS_HASH = _ncs.str2hash("http://ses.com/rfs-ctn-custom-notifications")
def get_device_name_from_kpath(kpath: str) -> str:
"""Get device name from kpath."""
kpath = str(kpath)
device = kpath[kpath.find("{") + 1:kpath.find("}")]
return device
def send_nso_sri_sync_notification(device: str, log: ncs.log.Log) -> None:
"""Send netconf-notification to CFS node."""
try:
notif_socket = socket.socket()
notif_daemon = _ncs.dp.init_daemon("netconf-notification-sri-event-base-sync")
_ncs.dp.connect(dx=notif_daemon, sock=notif_socket, type=_ncs.dp.CONTROL_SOCKET, ip="127.0.0.1", port=_ncs.PORT)
live_ctx = _ncs.dp.register_notification_stream(notif_daemon, None, notif_socket, "sri-event-base-sync")
log.info("Generating notification message...")
notif_message = get_nso_sri_sync_notif_message(device)
log.info(f"Notification message is set to={notif_message}")
send_notifation(live_ctx, notif_message, log)
except Exception as err: # pylint: disable=broad-except
log.error(err)
finally:
notif_socket.close()
def get_nso_sri_sync_notif_message(device_name: str):
"""Create notification payload."""
notif_values = [get_device_tag_value(device_name)]
return create_nso_sri_sync_notif_message(notif_values)
def get_device_tag_value(device_name) -> _ncs.TagValue:
"""Create xml tag and value."""
device_tag_hash = _ncs.str2hash("device-id")
device_tag = _ncs.XmlTag(NS_HASH, device_tag_hash)
device_val = _ncs.Value(device_name, _ncs.C_BUF)
return _ncs.TagValue(xmltag=device_tag, v=device_val)
def create_nso_sri_sync_notif_message(values) -> List[_ncs.TagValue]:
"""Create sri-event-base-sync notification payload xml."""
notification_tag_hash = _ncs.str2hash("sri-event-base-sync")
xml_begin = _ncs.Value((notification_tag_hash, NS_HASH), _ncs.C_XMLBEGIN)
xml_end = _ncs.Value((notification_tag_hash, NS_HASH), _ncs.C_XMLEND)
notif_tag = _ncs.XmlTag(NS_HASH, notification_tag_hash)
start = _ncs.TagValue(xmltag=notif_tag, v=xml_begin)
end = _ncs.TagValue(xmltag=notif_tag, v=xml_end)
return [start] + values + [end]
def send_notifation(ctx, msg, log) -> None:
"""Send netconf notification stream."""
current_time = datetime.now(timezone.utc)
notif_time = _ncs.DateTime(current_time.year, current_time.month, current_time.day, current_time.hour,
current_time.minute, current_time.second, current_time.microsecond,
int(current_time.strftime("%z")[:3]), int(current_time.strftime("%z")[3:]))
log.info(f"Sending notification with={ctx=}, {notif_time=}, {msg=}...")
_ncs.dp.notification_send(ctx, notif_time, msg)
log.info("Notification sent...")
# ---------------
# Action CALLBACK
# ---------------
class CtnNsoSriNotification(Action):
"""SRI notification action based on changes in device resources."""
def iterator(self, kp, op, oldv, newv):
"""Diff-iterate function."""
self.log.info(f"kp={kp}, op={OPER[op]}, newv={newv}")
return ncs.ITER_RECURSE
@Action.action
def cb_action(self, uinfo, name, kp, input, output, trans):
"""Create and send notification to cfs nso."""
self.log.info('action name: ', name)
_ncs.dp.action_set_timeout(uinfo, 1800)
self.log.info(f"Triggering kicker {input.kicker_id}, for path {input.path}, tid: {input.tid}")
with ncs.maapi.Maapi() as _maapi:
trans = _maapi.attach(input.tid)
trans.diff_iterate(self.iterator, ncs.ITER_WANT_ATTR)
_maapi.detach(input.tid)
send_nso_sri_sync_notification(get_device_name_from_kpath(kp), self.log)
class Main(ncs.application.Application):
"""ctn-nso-sri-notification main class."""
def setup(self):
"""Register service and actions."""
self.log.info("Main RUNNING")
self.register_action("sri-event-base-sync-action", CtnNsoSriNotification)
def teardown(self):
"""Teardown service and actions."""
self.log.info("Main FINISHED")
<INFO> 17-Mar-2024::12:53:35.430 rfs-ctn-custom-notifications ncs-dp-103617-rfs-ctn-custom-notifications:main:0-1-usid-75-sri-event-base-sync-action: - Generating notification message...
<INFO> 17-Mar-2024::12:53:35.431 rfs-ctn-custom-notifications ncs-dp-103617-rfs-ctn-custom-notifications:main:0-1-usid-75-sri-event-base-sync-action: - Notification message is set to=[_ncs.TagValue(tag=1870939232, ns=0), _ncs.TagValue(tag=669279939, ns=0), _ncs.TagValue(tag=1870939232, ns=0)]
<INFO> 17-Mar-2024::12:53:35.431 rfs-ctn-custom-notifications ncs-dp-103617-rfs-ctn-custom-notifications:main:0-1-usid-75-sri-event-base-sync-action: - Sending notification with=ctx=_ncs.dp.NotificationCtxRef, notif_time=_ncs.DateTime(year=2024, month=3, day=17, hour=12, min=53, sec=35, micro=431065, timezone=0, timezone_minutes=0), msg=[_ncs.TagValue(tag=1870939232, ns=0), _ncs.TagValue(tag=669279939, ns=0), _ncs.TagValue(tag=1870939232, ns=0)]...
<INFO> 17-Mar-2024::12:53:35.431 rfs-ctn-custom-notifications ncs-dp-103617-rfs-ctn-custom-notifications:main:0-1-usid-75-sri-event-base-sync-action: - Notification sent...
03-17-2024 06:43 AM - edited 03-17-2024 08:12 AM
NS_HASH = _ncs.str2hash("http://ses.com/rfs-ctn-custom-notifications")
def get_nso_sri_sync_notif_message(device_name: str):
"""Create notification payload."""
notif_values = [get_device_tag_value(device_name)]
return create_nso_sri_sync_notif_message(notif_values)
def get_device_tag_value(device_name) -> _ncs.TagValue:
"""Create xml tag and value."""
device_tag_hash = _ncs.str2hash("device-id")
device_tag = _ncs.XmlTag(NS_HASH, device_tag_hash)
device_val = _ncs.Value(device_name, _ncs.C_BUF)
return _ncs.TagValue(xmltag=device_tag, v=device_val)
def create_nso_sri_sync_notif_message(values) -> List[_ncs.TagValue]:
"""Create sri-event-base-sync notification payload xml."""
notification_tag_hash = _ncs.str2hash("sri-event-base-sync")
xml_begin = _ncs.Value((notification_tag_hash, NS_HASH), _ncs.C_XMLBEGIN)
xml_end = _ncs.Value((notification_tag_hash, NS_HASH), _ncs.C_XMLEND)
notif_tag = _ncs.XmlTag(NS_HASH, notification_tag_hash)
start = _ncs.TagValue(xmltag=notif_tag, v=xml_begin)
end = _ncs.TagValue(xmltag=notif_tag, v=xml_end)
return [start] + values + [end]
03-18-2024 12:54 AM - edited 03-18-2024 12:56 AM
Hi Mustafa,
as far as I can see your "def create_nso_sri_sync_notif_message(value)" function does not follow the documentation
The part that is wrong is where you specify "_ncs.Value(...)" for xml_begin and xml_end variables. According to documentation the first argument of this call should be a tuple of namespace hash and notification tag hash in this order (ns_hash, notifi_hash) but you changed the order :D.
Quote from docs: "For types C_XMLTAG, C_XMLBEGIN and C_XMLEND the init argument must be a 2-tuple which specifies the ns and tag values like this: (ns, tag)." Link
Correct implementation would be:
def create_nso_sri_sync_notif_message(values) -> List[_ncs.TagValue]:
"""Create sri-event-base-sync notification payload xml."""
# notifi_tag_hash
notification_tag_hash = _ncs.str2hash("sri-event-base-sync")
# common xml tag
notif_tag = _ncs.XmlTag(NS_HASH, notification_tag_hash)
# begin and end tag values
xml_begin = _ncs.Value((NS_HASH, notification_tag_hash), _ncs.C_XMLBEGIN)
xml_end = _ncs.Value((NS_HASH, notification_tag_hash), _ncs.C_XMLEND)
# generate tags value pairs
start = _ncs.TagValue(notif_tag, xml_begin)
end = _ncs.TagValue(notif_tag, xml_end)
return [start] + values + [end]
Let me know if this works for you.
Best Regards
Jakub
03-18-2024 02:14 AM
Hi Jakub,
Thanks a lot but unfortunately not worked.
def get_nso_sri_sync_notif_message(device_name: str):
"""Create notification payload."""
notif_values = [get_device_tag_value(device_name)]
return create_nso_sri_sync_notif_message(notif_values)
def get_device_tag_value(device_name) -> _ncs.TagValue:
"""Create xml tag and value."""
device_tag_hash = _ncs.str2hash("device-id")
device_tag = _ncs.XmlTag(NS_HASH, device_tag_hash)
device_val = _ncs.Value(device_name, _ncs.C_BUF)
return _ncs.TagValue(xmltag=device_tag, v=device_val)
def create_nso_sri_sync_notif_message(values) -> List[_ncs.TagValue]:
"""Create sri-event-base-sync notification payload xml."""
# notif_tag_hash
notification_tag_hash = _ncs.str2hash("sri-event-base-sync")
# notification xml tag
notif_tag = _ncs.XmlTag(NS_HASH, notification_tag_hash)
# begin and end tag values
xml_begin = _ncs.Value((NS_HASH, notification_tag_hash), _ncs.C_XMLBEGIN)
xml_end = _ncs.Value((NS_HASH, notification_tag_hash), _ncs.C_XMLEND)
# generate tag-value pairs
start = _ncs.TagValue(notif_tag, xml_begin)
end = _ncs.TagValue(notif_tag, xml_end)
return [start] + values + [end]
def send_notifation(ctx, msg, log) -> None:
"""Send netconf notification stream."""
current_time = datetime.now(timezone.utc)
notif_time = _ncs.DateTime(current_time.year, current_time.month, current_time.day, current_time.hour,
current_time.minute, current_time.second, current_time.microsecond,
int(current_time.strftime("%z")[:3]), int(current_time.strftime("%z")[3:]))
log.info(f"Sending notification with={ctx=}, {notif_time=}, {msg=}")
_ncs.dp.notification_send(ctx, notif_time, msg)
log.info("Notification sent...")
Best Regards ...
03-18-2024 03:08 AM
Hi Mustafa,
the issue here is clear you can't use _ncs.str2hash(...) call in global context. If you do so the hash value will be == 0 what is obviously wrong
You must call _ncs.str2hash(...) i.e. inside your Action callback method like so:
class SendNotification(Action):
@Action.action
def cb_action(self, uinfo, name, kp, action_input, action_output, trans):
NS_HASH = _ncs.str2hash("http://ses.com/rfs-ctn-custom-notifications")
self.log.debug(f"Action={name} was called with kp={kp}")
send_nso_sri_sync_notification(NS_HASH, get_device_name_from_kpath(), self.log)
This worked for me without any issue.
Let me know if it works.
Best Regards
Jakub
03-18-2024 03:53 AM
Thanks for all the helps Jakub worked.
Best Regards ...
Discover and save your favorite ideas. Come back to expert answers, step-by-step guides, recent topics, and more.
New here? Get started with these tips. How to use Community New member guide