cancel
Showing results for 
Search instead for 
Did you mean: 
cancel
6495
Views
30
Helpful
7
Replies

foreach in template

ian.scheidler1
Level 4
Level 4

Hi,

 

I have a problem which I really dont understand. In one case it works, in another it doesnt.

 

Let's start with what works:

 

YANG:

 

leaf-list iphelper {
       type inet:ipv4-address {
       }
    }

 

The Leaf List is filled with data from WebUI:

iphelperlist.PNG

Template:

 

<ip>
    <address>
        <primary>
            <address>{/gw_ip}</address>
            <mask>{/subnetmask_usernet}</mask>
        </primary>
    </address>
    <?foreach {/iphelper}?>
        <helper-address><!--for each, input list-->
            <helper-address-list>
	        <address>{/iphelper}</address>
	    </helper-address-list>
            </helper-address>
     <?end?>

This correctly generates multiple iphelper entries in the config for every item in the list. Like this:

 

interface Vlan975
 ip vrf forwarding db-sus-ri
 ip address 192.168.2.1 255.255.255.0
 ip helper-address 192.168.33.3
 ip helper-address 192.168.44.4
 ip helper-address 192.168.55.5
 no shutdown
 no autostate
exit

So far so good. This all works. :)

I am now trying to apply the same pattern to a List of IP prefixes:

 

YANG:

leaf-list prefixlist {
      type inet:ipv4-address { //TODO: type here should be inet:ipv4-prefix (e.g. xxx.xxx.xxx.xxx/xx)
      }
    }

Here is actually my first problem. If I use the inet:ipv4-prefix data type it is somehow impossible to fill the list from the WebUI (when i click on + an input field opens up and I can enter a prefix but when I click OK the input field closes but it just doesnt show up in the list in the WebUI). Haven't actually tried yet from CLI as customer wants the WebUI and I also believe it should be possible from both. Any ideas on this?

 

Template:

 

<ip>
<!-- ...here is some vrf config which I left out here... -->
<prefix-list><!--foreach seq 50--> <prefixes> <name>PL_{$vrf_name}_bgp2ebgp</name> <seq> <no>5</no> <permit> <ip>0.0.0.0/0</ip> </permit> </seq>
</prefixes> <prefixes> <name>PL_{$vrf_name}_ebgp2bgp</name> <seq> <no>5</no> <permit> <ip>10.242.0.0/16</ip> <le>30</le> </permit> </seq> <seq> <no>10</no> <permit> <ip>10.243.0.0/16</ip> <le>30</le> </permit> </seq> <seq> <no>15</no> <permit> <ip>10.244.0.0/16</ip> <le>30</le> </permit> </seq> <seq> <no>20</no> <permit> <ip>10.245.0.0/16</ip> <le>30</le> </permit> </seq> <seq> <no>25</no> <permit> <ip>10.246.0.0/16</ip> <le>30</le> </permit> </seq> <seq> <no>30</no> <permit> <ip>10.247.0.0/16</ip> <le>30</le> </permit> </seq> <seq> <no>35</no> <permit> <ip>10.248.0.0/16</ip> <le>30</le> </permit> </seq> <seq> <no>40</no> <permit> <ip>10.249.0.0/16</ip> <le>30</le> </permit> </seq> <seq> <no>45</no> <permit> <ip>10.250.0.0/16</ip> <le>30</le> </permit> </seq>
<?foreach {/prefixlist}?>
<seq>
<no>50</no>
<permit>
<ip>{/prefixlist}</ip>
<le>30</le>
</permit>
</seq>
<?end?> </prefixes> </prefix-list> </ip>

As you can hopefully see the prefix data up to line (<no>) 45 is static and everything after that should be added from the leaf list entries.

I get the following error:

 

Python cb_create error. Unknown error (66): DBS_VPN-template.xml:103 Expression '{/prefixlist}' resulted in an incompatible value '192.168.44.4192.168.55.5192.168.66.6' for /ncs:devices/device{IOSdev0}/config/ios:ip/prefix-list/prefixes{PL_db-sus-ri_ebgp2bgp}/seq{50}/permit/ip

 

I understand that NSO tries to apply the entire list (concatenated together) as one value, which doesn't work.

But why does this work for IP helper? And how else do I access the single items of the list otherwise?

 

2 Accepted Solutions

Accepted Solutions

Doing the second bit in templates-only might not be that trivial, but can be done in python.

 

This can be part of your template:

 

<config-template xmlns="http://tail-f.com/ns/config/1.0">

  <devices xmlns="http://tail-f.com/ns/ncs">

    <device>

      <name>{/device}</name>

      <config>

        <interface xmlns="urn:ios">

          <Vlan>

            <name>975</name>

            <ip>

              <helper-address>

                <helper-address-list>

                  <address>{/iphelper}</address>

                </helper-address-list>

              </helper-address>

            </ip>

          </Vlan>

        </interface>

        

        <ip xmlns="urn:ios">

          <prefix-list>

            <prefixes>

              <name>PL_foo</name>

              <seq>

                <no>5</no>

                <permit>

                  <ip>1.1.1.1/32</ip>

                </permit>

              </seq>

              <seq>

                <no>{$SEQ}</no>

                <permit>

                  <ip>{$PREFIX}</ip>

                </permit>

              </seq>

            </prefixes>

          </prefix-list>

        </ip>

      </config>

    </device>

  </devices>

</config-template>

 

and this can be the python code to apply the template:

 

   @service.create

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

        self.log.info('Service create(service=', service._path, ')')

 

        vars = ncs.template.Variables()

        

        seq = 50

        for prefix in service.prefixlist:

            vars.add('SEQ', seq)

            vars.add('PREFIX', prefix + '/32')

            template = ncs.template.Template(service)

            template.apply('ian-template', vars)

            seq = seq + 10

        

It might have been cleaner to split the template into static part and dynamic part and only apply the dynamic part multiple times, but the result is the same.

View solution in original post

Hi Ian,

 

I think, as you already discussed, you don't always need the foreach. The template will loop automatically. But if you want to explicitly loop, you can refer to current interation using current() or .

 

You could set a variable to store the counter. So something like:

 

<?set counter={0}?>
<?foreach {/prefixlist}?>
<seq>
  <no>{$counter}</no>
  <permit>
    <ip>{current()}</ip>
    <le>30</le>
  </permit>
  <?set counter={$counter + 5}?>
</seq>
<?end?>

View solution in original post

7 Replies 7

yfherzog
Cisco Employee
Cisco Employee

Hi,

 

 

On the helper address list, you're pushing configs into multiple list entries, as 'address' is a key in a list called helper-address-list.

On the second example, 'permit' is a container, so it can only take one value, and you provide it with the complete content of your list of prefixes.

You might want to create multiple entries of the 'seq' list, providing a running sequence to 'no' (currently you use 50 for all iterations).

 

Also, be careful when you use the '/' inside your templates (e.g. /prefixlist).

It actually resets the execution context in the template engine.

Use 'commit dry-run | debug template' to debug the process.

 

Finally, I tend to think that your first example will work even if you remove the foreach statement (give it a try).

Hi thanks.

OK I will try the first example without foreach and see if it works (from your explanatin I also believe it will work).

 

As for the second example

OK...I also need some sort of counter which increments in steps of 5. How can I have such a counter in the template (or rather in the foreach)?

And how do I tell NSO to only use the current entry of the leaf list in each iteration?

 

If you could provide me with a (small) code example I would be very grateful!

 

Doing the second bit in templates-only might not be that trivial, but can be done in python.

 

This can be part of your template:

 

<config-template xmlns="http://tail-f.com/ns/config/1.0">

  <devices xmlns="http://tail-f.com/ns/ncs">

    <device>

      <name>{/device}</name>

      <config>

        <interface xmlns="urn:ios">

          <Vlan>

            <name>975</name>

            <ip>

              <helper-address>

                <helper-address-list>

                  <address>{/iphelper}</address>

                </helper-address-list>

              </helper-address>

            </ip>

          </Vlan>

        </interface>

        

        <ip xmlns="urn:ios">

          <prefix-list>

            <prefixes>

              <name>PL_foo</name>

              <seq>

                <no>5</no>

                <permit>

                  <ip>1.1.1.1/32</ip>

                </permit>

              </seq>

              <seq>

                <no>{$SEQ}</no>

                <permit>

                  <ip>{$PREFIX}</ip>

                </permit>

              </seq>

            </prefixes>

          </prefix-list>

        </ip>

      </config>

    </device>

  </devices>

</config-template>

 

and this can be the python code to apply the template:

 

   @service.create

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

        self.log.info('Service create(service=', service._path, ')')

 

        vars = ncs.template.Variables()

        

        seq = 50

        for prefix in service.prefixlist:

            vars.add('SEQ', seq)

            vars.add('PREFIX', prefix + '/32')

            template = ncs.template.Template(service)

            template.apply('ian-template', vars)

            seq = seq + 10

        

It might have been cleaner to split the template into static part and dynamic part and only apply the dynamic part multiple times, but the result is the same.

How would you do the python for loop to a yang 'list' with few leaf instead of 'leaf-list' as mentioned in your example?

For example :

list prefixlist {
      key prefix;
      leaf prefix {
        type string;
      }
      leaf id {
        type string;
      }
      leaf length {
        type string;
      }
    }

Something like that:

 for prefix in service.prefixlist:

           self.log.info('DEBUG:(interface=', prefix, ')')

           self.log.info('DEBUG:(id=', service.prefixlist.id, ')')

           self.log.info('DEBUG:(length=', service.prefixlist.length, ')')

 

I can't make it work.

Thanks

Not sure exactly what's not working, but your prints should look something like this:

 

 for prefix in service.prefixlist:
           self.log.info('DEBUG:(interface=', prefix.prefix, ')')
           self.log.info('DEBUG:(id=', prefix.id, ')')
           self.log.info('DEBUG:(length=', prefix.length, ')')

Thanks a lot it works now! I see my mistake now.

Hi Ian,

 

I think, as you already discussed, you don't always need the foreach. The template will loop automatically. But if you want to explicitly loop, you can refer to current interation using current() or .

 

You could set a variable to store the counter. So something like:

 

<?set counter={0}?>
<?foreach {/prefixlist}?>
<seq>
  <no>{$counter}</no>
  <permit>
    <ip>{current()}</ip>
    <le>30</le>
  </permit>
  <?set counter={$counter + 5}?>
</seq>
<?end?>