cancel
Showing results for 
Search instead for 
Did you mean: 
cancel
1669
Views
5
Helpful
9
Replies

NSO: Switching between context nodes

indirakas
Level 1
Level 1

Hi, all!

I am trying to construct an xml template for the service. Here is an example of the YANG module I constructed:

 

...
      leaf-list device-group {
        tailf:info "Device group to use";
        type leafref {
          path "/ncs:devices/ncs:device-group/ncs:name";
        }
      }
      list some-server {
        key name;
        leaf name {
          type string;
        }
      }
...

So in XML template I am looping though each device in the device-group, then looping through the some-server list. But while it is inside the some-server loop, I want to reference device name again:

 

<config-template xmlns="http://tail-f.com/ns/config/1.0"
                 servicepoint="some-point">
  <devices xmlns="http://tail-f.com/ns/ncs">
    <?foreach {/device-group}?>
      <device>
        <name>{deref(.)/../member}</name>
          <config>
             <configuration xmlns="http://xml.juniper.net/xnm/1.1/xnm">
               <system>
                <ntp>
                    <?foreach {/some-server}?>
                      <server>
                          <name>{deref(.)/../member}</name>
                      </server>
...

Of course, just placing {deref(.)/../member} will not work. I need to reference the device name, while still in the foreach loop.

I have tried using <?set device-name={deref(.)/../member} ?> (didn't work), tried using {deref(/device-group)/../member}, but it printed devices in the current device-group, instead of a particular device-name.

Does anyone know how to make it work?

 

1 Accepted Solution

Accepted Solutions

lmanor
Cisco Employee
Cisco Employee

As yfherzog suggested, with the advanced template control features, it is quite simple to save the name in a variable that can later be used in your nested loop.

Something like this - devname:

 

    <?foreach {/device-group}?>
      <device>
        <name>{deref(.)/../member}</name>
<?set devname = {.}?> <config> <configuration xmlns="http://xml.juniper.net/xnm/1.1/xnm"> <system> <ntp> <?foreach {/some-server}?> <server> <name>{$devname}</name> </server>  

 

View solution in original post

9 Replies 9

yfherzog
Cisco Employee
Cisco Employee

I think it might be possible to find a way to do it using the more advanced template options (if you haven't already, take a look at the development guide under "Processing instructions"), but honestly, I'd advise moving at least some of the iterations from template to Python.

Even if you get it figured out, this template is going to get harder and harder to troubleshoot and to maintain.

 

It will be cool to get it solved, but it will become this sort of write-only code, which won't be as cool to maintain :)

I would have done it through python main.py file, but the problem is how to use deref here?

So I can loop through device-groups, but then I have a problem accessing device members of that group:

        for group in service.device_group:
            self.log.info('Group name is ',group)
            for member_name in devices.device_group[group].member:
                someserver['device_name'] = member_name

Aborted: Python cb_create error. global name 'devices' is not defined

Well, don’t know what ‘devices’ is set to in your create_cb above this code, but apparently it may not be set at all.

Use the root input parameter to point to the devices list:
@service.create
def cb_create(self, tctx, root, service, proplist):

devGrp = root.ncs__devices.device_group['nx-dev']
print ("members")
for member in devGrp.member :
print (member)

members
nx-0
nx-1
nx-2
nx-3

 

        some_service = {
            'device_group':{},
        }
        for group in service.device_group:
            syslog["device_group"][group]={}
            syslog["device_group"][group]["member"] = {}
            for member_name in root.devices.device_group[group].member:
                some_service["device_group"][group]["member"].append(member_name)
                
        some_service_tvars=ncs.template.Variables()
        some_service_tvars.add('DEVICE_GROUP',some_service['device_group'])
        some_service_template = ncs.template.Template(service)
        some_service_template.apply('some-service-template', some_service_tvars)

So in the python main.py file I will be able to pull the device-group list, members on that list. But then should I wrap them into a new dictionary, add that dictionary as a template variable and then call it from the xml file?

<config-template xmlns="http://tail-f.com/ns/config/1.0"
                 servicepoint="some-service">
  <devices xmlns="http://tail-f.com/ns/ncs">
    <?foreach {$DEVICE_GROUP}?>
      <device>
        <name>{$member}</name>

Previously I saw examples of creating new global variables in main.py file:

        foo = {
            'v6_flag':'false',
            'v4_flag':'false',
        }
        bar_tvars=ncs.template.Variables()
        bar_tvars.add('V6_FLAG',foo['v6_flag'])
        bar_tvars.add('V4_FLAG',foo['v4_flag'])

Can you give an example how to add nested dictionary variable via python?

 

Apparently the solution is to move some_service_tvars under the member loop.

 

        for group in service.device_group:
            for member_name in root.devices.device_group[group].member:
                some_service["device_name"]=member_name
                some_service_tvars=ncs.template.Variables()
		some_service_tvars.add('DEVICE_NAME',some_service['device_name'])
        	some_service_template = ncs.template.Template(service)
        	some_service_template.apply('some-service-template', some_service_tvars)

Correct, you would need to loop in your code and repetitively call the template with a single set of variables.

Just to elaborate here a bit, the variables passed to the template from your code (be it python, java, erlang..) to the template when the template is applied are passed as _strings_ not in some construct of the code language, such as lists, dictionaries, etc. (this would be quite difficult to implement given the variability of constructs in the different supported languages)

vleijon
Cisco Employee
Cisco Employee

Try another foreach over the members.

 

lmanor
Cisco Employee
Cisco Employee

As yfherzog suggested, with the advanced template control features, it is quite simple to save the name in a variable that can later be used in your nested loop.

Something like this - devname:

 

    <?foreach {/device-group}?>
      <device>
        <name>{deref(.)/../member}</name>
<?set devname = {.}?> <config> <configuration xmlns="http://xml.juniper.net/xnm/1.1/xnm"> <system> <ntp> <?foreach {/some-server}?> <server> <name>{$devname}</name> </server>  

 

Thank you! That worked! I used <?set devname = {deref(.)/../member} ?>, but apparently I should have used <?set devname = {.} ?> instead.