10-04-2018 02:05 AM - edited 03-01-2019 04:13 AM
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:
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?
Solved! Go to Solution.
10-04-2018 07:39 AM - edited 10-04-2018 07:40 AM
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.
10-04-2018 08:28 AM
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?>
10-04-2018 02:33 AM
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).
10-04-2018 03:43 AM
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!
10-04-2018 07:39 AM - edited 10-04-2018 07:40 AM
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.
12-03-2018 02:08 PM
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
12-03-2018 10:26 PM
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, ')')
12-04-2018 07:08 AM
Thanks a lot it works now! I see my mistake now.
10-04-2018 08:28 AM
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?>
Discover and save your favorite ideas. Come back to expert answers, step-by-step guides, recent topics, and more.
New here? Get started with these tips. How to use Community New member guide