cancel
Showing results for 
Search instead for 
Did you mean: 
cancel
4566
Views
0
Helpful
15
Replies

Python code to reference yang objects

rocrowe
Cisco Employee
Cisco Employee

Anyone have an example on how to reference nested yang objects in python ? For example, if my yang model is:

container a

  container b

    leaf c

container d

   list e

What would be the the python service or root path ?

service.???

root.???

1 Accepted Solution

Accepted Solutions

In you service callback:

def cb_create(self, tctx, root, service, proplist):

You have a paremeter named service, that is what we're referring to above with e.g. service.templates.template_content.routermap_name.

So let's say you create an instance of your service named foo.

That means that the "service" parameter in cb_create "points" to /device-template{foo}

So if you do self.log.info(service.name) you can expect to see "foo" in the logs.

Now, the root object "points" to /.

To get to your service instance, you have to do root.device_template["foo"] (this is basically root.device_template[service.name]).

So self.log.info(root.device_template["foo"].name) will also print "foo" to the logs.

View solution in original post

15 Replies 15

Jan Lindblad
Cisco Employee
Cisco Employee

Assuming your service model looks something like this:

module myservice {

  prefix mysvc;

  container myservices {

    list myservice {

       ncs:servicepoint myservice;

      ...

      key name;

      leaf name { type string; }

      container a {

        container b {

          leaf c { type string; }

        }

      }

      container d {

        list e {

          ...

        }

      }

    }

  }

}

Your python copde would access the leaf c in the particular service instance like this:

class ServiceCallbacks(ncs.application.Service):

    @ncs.application.Service.create

    def cb_create(self, tctx, root, service, proplist):

      the_c = service.a.b.c


      # When navigating from the root, we need to specify which service instance

      # we're talking about. Assuming we want to access the current service instance

      # it has key service.name

      c_from_the_root = root.myservices.myservice[service.name].a.b.c


When you cross over from one namespace to another, or if your YANG model uses names that are keywords in Python, you will need to specify the namespace prefix as well. Let's pretend that b is in one of these cases, then you can always add the namespace name and two underscore characters in front of each name, like this:


      the_c = service.a.mysvc__b.c


Hope this helps.

One addition questions. If the container/leaf/list has a hyphen in the yang, it needs to be replaced with an underscore (_) correct ?

Yup... but that's just because python sees "-" as "minus"....

(if you use underscore in yang it requires no change in Python)

cheers

gregg

I am having troubles getting past the first list in the yang model. Could you provide the path to get to leaf "routemap-name" ?

I have tried below with no luck:

root.services.device_template.templates.template_contents.routemap_name

root.device_template.templates.template_contents.routemap_name

services.device_template.templates.template_contents.routemap_name

root.services.device_template__device_template.device_template__templates.device_template__template_contents.device_template__routemap_name

root.device_template__device_template.device_template__templates.device_template__template_contents.device_template__routemap_name

services.device_template__device_template.device_template__templates.device_template__template_contents.device_template__routemap_name

YANG:

list device-template

  leaf name

  uses ncs:service-data;

  ncs:servicepoint device-template-servicepoint;

  leaf-list device

  ...

  container templates

    container template-contents

      leaf routemap-name

If you have a device-template list instance called "foo", you access it in python:

root.device_template["foo"].templates.template_content.routermap_name

Thanks. I guess I should have mentioned what exactly I was trying to do as it looks like this method wont work, as it requires you to know the list key value. All that I am trying to do is capture the values entered into the yang model, assign them to a variable and the ship them off to the xml template. Is there a better method/function I should be using ?

aha, so in your service callback, you have two options, I'd prefer the first:

service.templates.template_content.routermap_name

-or-

root.device_template[service.name].templates.template_content.routermap_name

Thanks. Do you know what the difference between these two calls are ?

The first one is 30% shorter to write.

Seriously, you'd start from service if the data you're looking for is inside the service instance being created/edited. If you need to navigate to something else, e.g. into another service instance, then start at the root.

I'm all for shorter code

But what is said is exactly what I was wondering.

Jan Lindblad wrote:

.......If you need to navigate to something else, e.g. into another service instance, then start at the root.

Also I tried both ways, but the plain 'services.' way without 'root.' before it doesnt seem to work. I also dont see 'services.' in ipython whereas I do see 'root.'

[Doesnt work] service.templates.template_content.routermap_name

[Works] root.device_template[service.name].templates.template_content.routermap_name


==> ncs-python-vm-test.log <==

2018-02-15 15:27:35 - test - DEBUG - TransactionCallback.cb_init()

2018-02-15 15:27:35 - test - INFO - Service create(service=/ncs:services/test:test{1})

2018-02-15 15:27:35 - test - ERROR - global name 'services' is not defined

2018-02-15 15:27:35 - test - ERROR - Traceback (most recent call last):

  File "/home/cisco/nso-4.4.4.1/src/ncs/pyapi/ncs/application.py", line 340, in wrapper

    pl = fn(self, tctx, root, service, proplist)

  File "/home/cisco/nso-4.4.4.1/ncs-run/state/packages-in-use/1/test/python/test/main.py", line 17, in cb_create

    tportchannel1_number = services.test__test[service.name]. \

NameError: global name 'services' is not defined

You have a typo there though, "services" => "service"

with "service."

admin@ncs(config-template-contents)# commit dry-run

Aborted: Python cb_create error. 'ListElement' object has no attribute 'test__test'

==> ncs-python-vm-test.log <==

2018-02-15 18:13:29 - test - ERROR - Traceback (most recent call last):

  File "/home/cisco/nso-4.4.4.1/src/ncs/pyapi/ncs/application.py", line 340, in wrapper

    pl = fn(self, tctx, root, service, proplist)

  File "/home/cisco/nso-4.4.4.1/ncs-run/state/packages-in-use/1/test/python/test/main.py", line 17, in cb_create

    tportchannel1_number = service.test__test[service.name]. \

  File "/home/cisco/nso-4.4.4.1/src/ncs/pyapi/ncs/maagic.py", line 438, in __getattr__

    super(Node, self).__getattribute__(name)

AttributeError: 'ListElement' object has no attribute 'test__test'

ipython

In [1]: print(root)

(root)

In [2]: print(root.services)

services

In [3]: print(service)

---------------------------------------------------------------------------

NameError                                 Traceback (most recent call last)

<ipython-input-3-eb3457d971bb> in <module>()

----> 1 print(service)

NameError: name 'service' is not defined

In you service callback:

def cb_create(self, tctx, root, service, proplist):

You have a paremeter named service, that is what we're referring to above with e.g. service.templates.template_content.routermap_name.

So let's say you create an instance of your service named foo.

That means that the "service" parameter in cb_create "points" to /device-template{foo}

So if you do self.log.info(service.name) you can expect to see "foo" in the logs.

Now, the root object "points" to /.

To get to your service instance, you have to do root.device_template["foo"] (this is basically root.device_template[service.name]).

So self.log.info(root.device_template["foo"].name) will also print "foo" to the logs.

Robert,

If there is a list in your Service instance yang, presumably it is there to handle the case of multiple entries of that list type per service instance, you'll need to loop through all those entries to get the service data to provide to the template in variables.

If all you are doing in python is reading the service instance data and providing to template via variables, it is better (more efficient) to pass the service instance data directly to the template without invoking the python create code at all.

-Larry