cancel
Showing results for 
Search instead for 
Did you mean: 
cancel
1274
Views
1
Helpful
15
Replies

Concatenate evaluated values before pushing conf to device

Noel Cantenot
Level 1
Level 1


Hello,

I'm writing a very basic service in order to configure an interface vlan on a list of switch. Le vlan number is the same for all switches, and each switch has its own IP address for this vlan interface.

When I create the service one switch, it works fine. But when I create the service with 2 switches, I have a behavior I don't understand. The evaluation of ip-add is correct for each switch, but it concatenates both ip-add to push on the device !


admin@ncs(config)# services int-vlan-cfs TEST-CFS-57 vlan-id 57 switch nx1 ip-add 10.10.10.1/24
admin@ncs(config)# services int-vlan-cfs TEST-CFS-57 vlan-id 57 switch nx2 ip-add 10.10.10.2/24
admin@ncs(config-switch-nx2)# commit dry-run | debug template
Evaluating "/switch/ip-add" (from file "int-vlan-cfs-template.xml", line 11)
.../...

Context node: /services/int-vlan-cfs:int-vlan-cfs[cfs-name='TEST-CFS-57']
Result:
For /services/int-vlan-cfs:int-vlan-cfs[cfs-name='TEST-CFS-57']/switch[name='nx1'], it evaluates to "10.10.10.1/24"
For /services/int-vlan-cfs:int-vlan-cfs[cfs-name='TEST-CFS-57']/switch[name='nx2'], it evaluates to "10.10.10.2/24"
Aborted: int-vlan-cfs-template.xml:11 Expression '{/switch/ip-add}' resulted in an incompatible value '10.10.10.1/2410.10.10.2/24' for /ncs:devices/device{nx1}/config/nx:interface/Vlan{57}/ip/address

I joined the xml, yang and full commit result files.

Regards.

1 Accepted Solution

Accepted Solutions

Hi

The reason for "{./ip-add}" evaluating to empty node is that the this previous line triggers a context change:

<name tags="create">{../vlan-id}</name>

If you change it to:

<name tags="create">{string(../vlan-id)}</name>

You will avoid that context switch, and still have direct access the the ip-add intended for that switch. E.g. have a look in the  "Development Guide" section "XPath context in templates" last paragraph and example.

br.

Kristoffer Larsen

View solution in original post

15 Replies 15

Marcel Zehnder
Spotlight
Spotlight

Hi 

It's been a while since I last used NSO. However, based on your files and your YANG module, I think you need to put some logic in your config-template, something like this:

 

<config-template xmlns="http://tail-f.com/ns/config/1.0" servicepoint="int-vlan-cfs">
  <devices xmlns="http://tail-f.com/ns/ncs">
      <?foreach {/switch}?>
        <device>
          <name>{./name}</name>
          <config>
                <interface xmlns="http://tail-f.com/ned/cisco-nx">
                    <Vlan>
                          <name tags="create">{../vlan-id}</name>
                          <ip>
                            <address tags="replace">{./ip-add}</address>
                          </ip>
                          <description>Ma description de test</description>
                    </Vlan>
                </interface>
          </config>
        </device>
      <?end?>
   </devices>
</config-template>

 

It's out of my head, so I'm not sure if it works.

Noel Cantenot
Level 1
Level 1

Hi Marcel,

Thanks for your reply.

I tested with foreach, but the result is the same.

On your example, I had to replace

<address tags="replace">{./ip-add}</address>

with

<address tags="replace">{/switch/ip-add}</address>

otherwise result of evaluating "./ip-add" is "empty node".

But again, same concatenation...

Regards.

Hi @Noel Cantenot /switch/ip-add resolves to all ip-add in your list, that's expected. Therefore by using the loop it should be possible to evaluate all list entries one by one. Does this work or do you also get empty node?

 

<config-template xmlns="http://tail-f.com/ns/config/1.0" servicepoint="int-vlan-cfs">
  <devices xmlns="http://tail-f.com/ns/ncs">
      <?foreach {/switch}?>
        <device>
          <name>{name}</name>
          <config>
                <interface xmlns="http://tail-f.com/ned/cisco-nx">
                    <Vlan>
                          <name tags="create">{../vlan-id}</name>
                          <ip>
                            <address tags="replace">{ip-add}</address>
                          </ip>
                          <description>Ma description de test</description>
                    </Vlan>
                </interface>
          </config>
        </device>
      <?end?>
   </devices>
</config-template>

 

 

With this code I have empty node, so we can see that ip-add is not pushed in the configuration.

 

Evaluating "ip-add" (from file "int-vlan-cfs-template.xml", line 11)
Context node: /services/int-vlan-cfs:int-vlan-cfs[cfs-name='TEST-CFS-57']
Result: empty node
Operation 'merge' on non-existing node: /devices/device[name='nx2']/config/nx:interface/Vlan[name='57']/description (from file "int-vlan-cfs-template.xml", line 13)
Fetching literal "Ma description de test" (from file "int-vlan-cfs-template.xml", line 13)
Setting /devices/device[name='nx2']/config/nx:interface/Vlan[name='57']/description to "Ma description de test"
cli {
local-node {
data devices {
device nx1 {
config {
interface {
+ Vlan 57 {
+ description "Ma description de test";
+ }
}
}
}
device nx2 {
config {
interface {
+ Vlan 57 {
+ description "Ma description de test";
+ }
}
}
}
}
services {
+ int-vlan-cfs TEST-CFS-57 {
+ vlan-id 57;
+ switch nx1 {
+ ip-add 10.10.10.1/24;
+ }
+ switch nx2 {
+ ip-add 10.10.10.2/24;
+ }
+ }
}
}
}
admin@ncs(config-switch-nx2)#

Hi

The reason for "{./ip-add}" evaluating to empty node is that the this previous line triggers a context change:

<name tags="create">{../vlan-id}</name>

If you change it to:

<name tags="create">{string(../vlan-id)}</name>

You will avoid that context switch, and still have direct access the the ip-add intended for that switch. E.g. have a look in the  "Development Guide" section "XPath context in templates" last paragraph and example.

br.

Kristoffer Larsen

Thanks, @radioman I did not realize there is again a context switch with ../vlan-id. 

Marcel Zehnder
Spotlight
Spotlight

Okay two more ideas (without using loops):

What if you try to get the string value instead of the xpath-list:

<config-template xmlns="http://tail-f.com/ns/config/1.0" servicepoint="int-vlan-cfs">
  <devices xmlns="http://tail-f.com/ns/ncs">
      <device>
        <name>{/switch/name}</name>
        <config>
              <interface xmlns="http://tail-f.com/ned/cisco-nx">
                   <Vlan>
                        <name tags="create">{/vlan-id}</name>
                        <ip>
                          <address tags="replace">{string(/switch/ip-add)}</address>
                        </ip>
                        <description>Ma description de test</description>
                   </Vlan>
              </interface>
        </config>
      </device>
   </devices>
</config-template>

If the above does not work, the context-switch mechanism should kick in, so the following may work:

<config-template xmlns="http://tail-f.com/ns/config/1.0" servicepoint="int-vlan-cfs">
  <devices xmlns="http://tail-f.com/ns/ncs">
      <device>
        <name>{/switch/name}</name>
       <!-- context should change to current /switch -->
        <config>
              <interface xmlns="http://tail-f.com/ned/cisco-nx">
                   <Vlan>
                        <name tags="create">{../vlan-id}</name>
                        <!-- with current /switch as context ../vlan-id should return the vlan -->
                        <ip>
                          <address tags="replace">{ip-add}</address>
                         <!-- with current /switch as context ip-add should return the IP of the current switch -->
                        </ip>
                        <description>Ma description de test</description>
                   </Vlan>
              </interface>
        </config>
      </device>
   </devices>
</config-template>

 

The first solution change the result, but it seams only the first ip-add is evaluated (so both switches are configured with the first ip-add.

Evaluating "string(/switch/ip-add)" (from file "int-vlan-cfs-template.xml", line 10)
Context node: /services/int-vlan-cfs:int-vlan-cfs[cfs-name='TEST-CFS-57']
Result: "10.10.10.1/24"
Setting /devices/device[name='nx2']/config/nx:interface/Vlan[name='57']/ip/address to "10.10.10.1/24"
Operation 'merge' on non-existing node: /devices/device[name='nx2']/config/nx:interface/Vlan[name='57']/description (from file "int-vlan-cfs-template.xml", line 12)
Fetching literal "Ma description de test" (from file "int-vlan-cfs-template.xml", line 12)
Setting /devices/device[name='nx2']/config/nx:interface/Vlan[name='57']/description to "Ma description de test"
cli {
local-node {
data devices {
device nx1 {
config {
interface {
+ Vlan 57 {
+ description "Ma description de test";
+ ip {
+ address 10.10.10.1/24;
+ }
+ }
}
}
}
device nx2 {
config {
interface {
+ Vlan 57 {
+ description "Ma description de test";
+ ip {
+ address 10.10.10.1/24;
+ }
+ }
}
}
}
}
services {
+ int-vlan-cfs TEST-CFS-57 {
+ vlan-id 57;
+ switch nx1 {
+ ip-add 10.10.10.1/24;
+ }
+ switch nx2 {
+ ip-add 10.10.10.2/24;
+ }
+ }
}
}
}

For the second solution, the evaluation of vlan is OK, but evaluation of ip-add is : empty node. I agree with you, my understanding of the context is that {ip-add} should work, but it doesn't... I testes with {./ip-add}, same result.

Evaluating "ip-add" (from file "int-vlan-cfs-template.xml", line 10)
Context node: /services/int-vlan-cfs:int-vlan-cfs[cfs-name='TEST-CFS-57']
Result: empty node

On ly {/switch/ip-add} evaluates both IPs, but concatenates...

Marcel Zehnder
Spotlight
Spotlight

Maybe switch from Template only to a Python based service. If you do the logic in Python things get easier.

Marcel Zehnder
Spotlight
Spotlight

Python Code

# -*- mode: python; python-indent: 4 -*-
import ncs
from ncs.application import Service

class ServiceCallbacks(Service):
    @service.create
    def cb_create(self, tctx, root, service, proplist):
        self.log.info('Service create(service=', service._path, ')')
        template = ncs.template.Template(service)

        for sw in service.switch:
            vars = ncs.template.Variables()
            self.log.info(f"{sw.name} / {service.vlanid} / {sw.ipadd}")
            vars.add('vlanid', service.vlanid)
            vars.add('swname', sw.name)
            vars.add('swipadd', sw.ipadd)
            template.apply('int-vlan-cfs-template', vars)
    
class Main(ncs.application.Application):
    def setup(self):
        self.log.info('Main RUNNING')
        self.register_service('int-vlan-cfs', ServiceCallbacks)

    def teardown(self):
        self.log.info('Main FINISHED')

Also change the node names in the YANG module:

module int-vlan-cfs {

  namespace "http://example.com/int-vlan-cfs";
  prefix int-vlan-cfs;

  import ietf-inet-types {
    prefix inet;
  }
  import tailf-common {
    prefix tailf;
  }
  import tailf-ncs {
    prefix ncs;
  }

  description
    "CFS configure plusieurs interfaces d'un vlan avec: Noms devices, Id interface, IP/mask";

  revision 2016-01-01 {
    description
      "Initial revision.";
  }

  augment /ncs:services {
        list int-vlan-cfs {
            description "CFS skeleton service pour configurer les interfaces d'un vlan";
            tailf:info "CFS interfaces d'un vlan";
            key cfs-name;

            leaf cfs-name {
              tailf:info "-------- Nom du CFS";
              type string;
            }

            uses ncs:service-data;
            ncs:servicepoint "int-vlan-cfs";

            leaf vlanid {
              tailf:info "-------- Vlan number";
              type uint32;
            }

            list switch {
              key name;
              tailf:info "Liste des devices";
              unique "ipadd";

              leaf name {
                type leafref {
                  tailf:info "-------- Nom du switch";
                  path "/ncs:devices/ncs:device/ncs:name";
                }
              }

              leaf ipadd {
                tailf:info "-------- adresse IP";
                type string;
              }
            }
        }
  }
}

And the template:

<config-template xmlns="http://tail-f.com/ns/config/1.0" servicepoint="int-vlan-cfs">
  <devices xmlns="http://tail-f.com/ns/ncs">
      <device>
        <name>{$swname}</name>
        <config>
              <interface xmlns="http://tail-f.com/ned/cisco-nx">
                   <Vlan>
                        <name tags="create">{$vlanid}</name>
                        <ip>
                          <address tags="replace">{$swipadd}</address>
                        </ip>
                        <description>Ma description de test</description>
                   </Vlan>
              </interface>
        </config>
      </device>
   </devices>
</config-template>

For such a simple service, I wanted to avoid using Python. Furthermore, Python doesn't seams to work on my free trial NSO copy (I don't want to go to the prod version for now). But I keep your Python code for further investigation

Thanks for you help.

I‘m back in the office next week with access to NSO and will try it there. 

Hello Noel,

The evaluation NSO is exactly the same as a normal NSO. Python should work, it's probably something like the python in the PATH is not a python3 version.


You are right that python is not needed for this. Did the answer from @radioman not resolve your question?

Hello snovello,

I switched from 5.7 to 6.3, and no more python MV issue (I took care about default python version).

The answer of  @radioman explains the context behaviour, but do not resolve the agragation issue.