01-14-2026 12:35 PM
NSO behavior running a python NSO action seems to be to create one python ExecuteAction instance that handles all callbacks from NSO. For example, this single instance is what handles two users invoking the action.
Is there a way to get one python instance for each action invocation?
Is there a mechanism for sharing information between invocations (similar to the proplist available with NSO services)?
Thank you,
Mike
Solved! Go to Solution.
01-15-2026 05:41 AM
NSO creates one instance of the action when the application starts. There is no way to get one python instance for each action invocation unless you create your own handler.
There is no proplist provided with actions, but what you can do is use variables that are shared in the application.
Shared Python Variables
import ncs
from ncs.dp import Action
import threading
_counter = 0
_lock = threading.Lock()
class Increment(Action):
@Action.action
def cb_action(self, uinfo, name, kp, input, output, trans):
global _counter
with _lock:
_counter += 1
count = _counter
local_data = {'user': uinfo.username, 'count': count}
output.result = str(local_data)
class Double(Action):
@Action.action
def cb_action(self, uinfo, name, kp, input, output, trans):
global _counter
with _lock:
_counter *= 2
count = _counter
local_data = {'user': uinfo.username, 'count': count}
output.result = str(local_data)
class Main(ncs.application.Application):
def setup(self):
self.log.info('Main RUNNING')
self.register_action('increment', Increment)
self.register_action('double', Double)
def teardown(self):
self.log.info('Main FINISHED')
In this example, there's a global _counter variable for the entire file, which means that other actions can also use it. The _lock ensures that updates are synchronized when the action is called in parallel.
CDB Shared Data
If the information must be persisted and survive package reloads or NSO restarts, it must be saved in CDB. That is how the services property list works. The following example instead writes and reads the value from CDB.
container counter {
leaf counter {
type uint32;
default "0";
}
}import ncs
from ncs.dp import Action
import threading
_lock = threading.Lock()
class Increment(Action):
@Action.action
def cb_action(self, uinfo, name, kp, input, output, trans):
with _lock, ncs.maapi.single_write_trans('admin', 'python') as t:
root = ncs.maagic.get_root(t)
count = root.counter.counter + 1
root.counter.counter = count
t.apply()
local_data = {'user': uinfo.username, 'count': count}
output.result = str(local_data)
class Double(Action):
@Action.action
def cb_action(self, uinfo, name, kp, input, output, trans):
with _lock, ncs.maapi.single_write_trans('admin', 'python') as t:
root = ncs.maagic.get_root(t)
count = root.counter.counter * 2
root.counter.counter = count
t.apply()
local_data = {'user': uinfo.username, 'count': count}
output.result = str(local_data)
class Main(ncs.application.Application):
def setup(self):
self.log.info('Main RUNNING')
self.register_action('increment', Increment)
self.register_action('double', Double)
def teardown(self):
self.log.info('Main FINISHED')
If you don't want to add a leaf in the model for each data, you can create a list similar to the property list and store what is needed in there.
container property-list {
list property {
key name;
leaf name {
type string;
}
leaf value {
type string;
}
}
}
It's worth nothing that this approach might require something that cleans up the data the action writes. Compared to services, where the property list will be deleted if the service is the deleted, there is nothing cleaning this up automatically.
Also see this section in the documentation about handling retries in actions: https://nso-docs.cisco.com/guides/development/core-concepts/nso-concurrency-model#d5e8569
01-15-2026 05:41 AM
NSO creates one instance of the action when the application starts. There is no way to get one python instance for each action invocation unless you create your own handler.
There is no proplist provided with actions, but what you can do is use variables that are shared in the application.
Shared Python Variables
import ncs
from ncs.dp import Action
import threading
_counter = 0
_lock = threading.Lock()
class Increment(Action):
@Action.action
def cb_action(self, uinfo, name, kp, input, output, trans):
global _counter
with _lock:
_counter += 1
count = _counter
local_data = {'user': uinfo.username, 'count': count}
output.result = str(local_data)
class Double(Action):
@Action.action
def cb_action(self, uinfo, name, kp, input, output, trans):
global _counter
with _lock:
_counter *= 2
count = _counter
local_data = {'user': uinfo.username, 'count': count}
output.result = str(local_data)
class Main(ncs.application.Application):
def setup(self):
self.log.info('Main RUNNING')
self.register_action('increment', Increment)
self.register_action('double', Double)
def teardown(self):
self.log.info('Main FINISHED')
In this example, there's a global _counter variable for the entire file, which means that other actions can also use it. The _lock ensures that updates are synchronized when the action is called in parallel.
CDB Shared Data
If the information must be persisted and survive package reloads or NSO restarts, it must be saved in CDB. That is how the services property list works. The following example instead writes and reads the value from CDB.
container counter {
leaf counter {
type uint32;
default "0";
}
}import ncs
from ncs.dp import Action
import threading
_lock = threading.Lock()
class Increment(Action):
@Action.action
def cb_action(self, uinfo, name, kp, input, output, trans):
with _lock, ncs.maapi.single_write_trans('admin', 'python') as t:
root = ncs.maagic.get_root(t)
count = root.counter.counter + 1
root.counter.counter = count
t.apply()
local_data = {'user': uinfo.username, 'count': count}
output.result = str(local_data)
class Double(Action):
@Action.action
def cb_action(self, uinfo, name, kp, input, output, trans):
with _lock, ncs.maapi.single_write_trans('admin', 'python') as t:
root = ncs.maagic.get_root(t)
count = root.counter.counter * 2
root.counter.counter = count
t.apply()
local_data = {'user': uinfo.username, 'count': count}
output.result = str(local_data)
class Main(ncs.application.Application):
def setup(self):
self.log.info('Main RUNNING')
self.register_action('increment', Increment)
self.register_action('double', Double)
def teardown(self):
self.log.info('Main FINISHED')
If you don't want to add a leaf in the model for each data, you can create a list similar to the property list and store what is needed in there.
container property-list {
list property {
key name;
leaf name {
type string;
}
leaf value {
type string;
}
}
}
It's worth nothing that this approach might require something that cleans up the data the action writes. Compared to services, where the property list will be deleted if the service is the deleted, there is nothing cleaning this up automatically.
Also see this section in the documentation about handling retries in actions: https://nso-docs.cisco.com/guides/development/core-concepts/nso-concurrency-model#d5e8569
01-16-2026 04:15 AM
Thank you for clearing that up and the detailed example!
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