10-31-2019 08:09 AM
Trying to figure out how to use DataCallbacks within python. I am trying to rework this example from the forums
https://community.cisco.com/t5/nso-developer-hub-discussions/callback-python-code/td-p/3510805
I am not making the links to get the daemon to create in the Main class. According to the API example:
Example handler and registration: class Handler(object): def get_object(self, tctx, kp, args): return {'leaf1': 'value', 'leaf2': 'value'} def get_next(self, tctx, kp, args, next): return None def count(self): return 0 dcb = DataCallbacks(log) dcb.register('/namespace:container', Handler()) _confd.dp.register_data_cb(dd.ctx(), example_ns.callpoint_handler, dcb)
To register you use the dp.register_data_cb.
I am trying to do this in the
class Main(ncs.application.Application):
which states:
# If we registered any callback(s) above, the Application class
# took care of creating a daemon (related to the service/action point).
Code below.
What am I missing that will get the callback to register correctly and start the daemon for it? Newbie here when it comes to dealing with this, I tried to follow the guide but wasn't able to make the translation from Java to python.
I tried to use self.dd.ctx() in the register_data_cb but dd doesn't have the ctx like in the api guide.
Yang:
module pydp {
namespace "http://tail-f.com/ns/example/pydp";
prefix pydp;
yang-version 1.1;
import tailf-common {
prefix tailf;
}
import ietf-inet-types {
prefix inet;
}
container interface {
list GigabitEthernet {
key "name";
leaf name {
type string;
}
leaf shutdown {
tailf:info "Shutdown the selected interface";
type empty;
}
container stats {
config false;
tailf:callpoint "stats";
leaf sent {
type uint32;
}
leaf received {
type uint32;
}
}
action clear {
tailf:actionpoint "clear";
output {
leaf received {
type uint32;
}
leaf sent {
type uint32;
}
}
}
}
}
}
Python
#!/usr/bin/env python
import logging
import time
import socket
import select
#from config_ns import ns
import ncs.maapi as maapi
import _ncs
import ncs
from ncs.application import Service
from ncs.experimental import DataCallbacks
class DataCbs(object):
def __init__(self, log):
self.log = log
def get_object(self, tctx, keypath, _args):
self.log.info("cb_get_elem: " + str(keypath))
if_name = str(keypath[2][0])
# (sent, recv) = get_counters(if_name)
return {
"stats": {
"sent": 10,
"received": 20
}
}
class Main(ncs.application.Application):
def setup(self):
# The application class sets up logging for us. It is accessible
# through 'self.log' and is a ncs.log.Log instance.
self.log.info('Main RUNNING')
# Service callbacks require a registration for a 'service point',
# as specified in the corresponding data model.
#
handler = DataCbs(self.log)
dcb = DataCallbacks(self.log)
dcb.register('/pydp:interface/pydp:GigabitEthernet', handler)
_ncs.dp.register_data_cb(self.dd.ctx(), 'stats', dcb)
#d.start()
# If we registered any callback(s) above, the Application class
# took care of creating a daemon (related to the service/action point).
# When this setup method is finished, all registrations are
# considered done and the application is 'started'.
def teardown(self):
# When the application is finished (which would happen if NCS went
# down, packages were reloaded or some error occurred) this teardown
# method will be called.
self.log.info('Main FINISHED')
When starting the package I get the following logs related to trying to use the self.dd.ctx()
<ERROR> 31-Oct-2019::15:00:20.577 pydp ComponentThread:main: - Traceback (most recent call last): File "/opt/ncs/ncs-4.7.4.3/src/ncs/pyapi/ncs_pyvm/ncsthreads.py", line 173, in run self.main._run() File "/opt/ncs/ncs-4.7.4.3/src/ncs/pyapi/ncs/application.py", line 216, in _run self.setup() File "/var/opt/ncs/state/packages-in-use/1/pydp/python/pydp/main.py", line 45, in setup _ncs.dp.register_data_cb(self.dd, 'stats', dcb) TypeError: dx argument must be a _ncs.dp.DaemonCtxRef
Thanks
Solved! Go to Solution.
10-31-2019 01:31 PM
Well, the ncs.Application class creates a single dp.Daemon but that is not yet done when the 'setup' method is called. It is correct that you can create another Daemon an register the callbacks there.
There is however a more elegant solution which will make use the Daemon created by ncs.Application. The trick is to use the 'register_fun' method of Application. Look at the example below how it can be done.
Please remember that 'experimental.DataCallbacks' is, eh.. experimental(!) and far from complete and shouldn't be used in production.
/Tomas
import ncs
import _ncs
from ncs.experimental import DataCallbacks
# The argument 'state' is a ncs.dp.Daemon.State instance
def dcb_start(state):
dcb = DataCallbacks(state.log)
dcb.register('/mydp:container', Handler())
_ncs.dp.register_data_cb(state.ctx, 'mydp-cp', dcb)
return state
# The argument 'start_state' is whatever is returned
# from the dcb_start function.
def dcb_stop(start_state):
pass
class Handler(object):
def get_object(self, tctx, kp, args):
return {'leaf1': 'value', 'leaf2': 'value'}
def get_next(self, tctx, kp, args, next):
return None
def count(self):
return 0
class Main(ncs.application.Application):
def setup(self):
# Register function that will be called when the
# package starts/stops
self.register_fun(dcb_start, dcb_stop)
def teardown(self):
pass
10-31-2019 12:29 PM
Appears that the Application class doesn't create the daemons for the Data Providers? Is that correct?
Added the following lines to get the DP to register and work.
from ncs.dp import Daemon d = Daemon("pydb-callback-daemon", log=self.log) d.start()
Further reading of the python API docs related to the _ncs.dp.register_data_cb led me to the following which worked to get the Data Provider working in python.
register_data_cb(...) register_data_cb(dx, callpoint, data, flags) -> None Registers data manipulation callback functions. Keyword arguments: dx -- a daemon context acquired through a call to init_daemon() callpoint -- name of a tailf:callpoint in the data model data -- the callback instance (see below) flags -- data callbacks flags, dp.DATA_* (optional)
Updated Code:
#!/usr/bin/env python import logging import time import socket import select from ncs.dp import Daemon import ncs.maapi as maapi import _ncs import ncs from ncs.application import Service from ncs.experimental import DataCallbacks class DataCbs(object): def __init__(self, log): self.log = log def get_object(self, tctx, keypath, _args): self.log.info("cb_get_elem: " + str(keypath)) #if_name = str(keypath[2][0]) # (sent, recv) = get_counters(if_name) return { "stats": { "sent": 10, "received": 20 } } class Main(ncs.application.Application): def setup(self): self.log.info('Main RUNNING') d = Daemon("pydb-callback-daemon", log=self.log) handler = DataCbs(self.log) dcb = DataCallbacks(self.log) dcb.register('/pydp:interface/pydp:GigabitEthernet', handler) _ncs.dp.register_data_cb(d.ctx(), 'stats', dcb) d.start() def teardown(self): self.log.info('Main FINISHED')
10-31-2019 01:31 PM
Well, the ncs.Application class creates a single dp.Daemon but that is not yet done when the 'setup' method is called. It is correct that you can create another Daemon an register the callbacks there.
There is however a more elegant solution which will make use the Daemon created by ncs.Application. The trick is to use the 'register_fun' method of Application. Look at the example below how it can be done.
Please remember that 'experimental.DataCallbacks' is, eh.. experimental(!) and far from complete and shouldn't be used in production.
/Tomas
import ncs
import _ncs
from ncs.experimental import DataCallbacks
# The argument 'state' is a ncs.dp.Daemon.State instance
def dcb_start(state):
dcb = DataCallbacks(state.log)
dcb.register('/mydp:container', Handler())
_ncs.dp.register_data_cb(state.ctx, 'mydp-cp', dcb)
return state
# The argument 'start_state' is whatever is returned
# from the dcb_start function.
def dcb_stop(start_state):
pass
class Handler(object):
def get_object(self, tctx, kp, args):
return {'leaf1': 'value', 'leaf2': 'value'}
def get_next(self, tctx, kp, args, next):
return None
def count(self):
return 0
class Main(ncs.application.Application):
def setup(self):
# Register function that will be called when the
# package starts/stops
self.register_fun(dcb_start, dcb_stop)
def teardown(self):
pass
10-31-2019 04:22 PM
Thanks. Is it in the plans for a future version for this to be supported in the python API's? Otherwise is it recommended to use java for Data providers at this time?
10-31-2019 11:59 PM
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