cancel
Showing results for 
Search instead for 
Did you mean: 
cancel
224
Views
3
Helpful
2
Replies

NSO Action python instances

MikeG3
Level 1
Level 1

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

1 Accepted Solution

Accepted Solutions

mmollber
Cisco Employee
Cisco Employee

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

View solution in original post

2 Replies 2

mmollber
Cisco Employee
Cisco Employee

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

MikeG3
Level 1
Level 1

Thank you for clearing that up and the detailed example!

Polls
AI-powered tools for network troubleshooting are likely to be part of everyone’s workflow sooner or later. What is the single biggest challenge or concern you see with adopting these tools in your organization?