cancel
Showing results for 
Search instead for 
Did you mean: 
cancel
1429
Views
5
Helpful
4
Replies

Python DataCallback daemon

tsiemers1
Spotlight
Spotlight

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

1 Accepted Solution

Accepted Solutions

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

 

View solution in original post

4 Replies 4

tsiemers1
Spotlight
Spotlight

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')

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

 

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?

There are no real "plans" for experimental.DataCallbacks at this time. We usually put things we think might be useful in the experimental module for customers to test it out. Whether one should use Java or Python for DPs is really up to the user to decide, so we don't have any recommendations here. It's still fairly easy to create a DP in Python without the help you get from experimental.DataCallbacks.
Getting Started

Find answers to your questions by entering keywords or phrases in the Search bar above. New here? Use these resources to familiarize yourself with the NSO Developer community: