Earlier blog posts in this Cisco DNA Center series gave an overview of PnP, how to use the API and an introduction to the configuration template editor. This blog post explores creating templates in velocity. Velocity is a template programming language, used in Cisco Prime Infrastructure and APIC-EM.
Template Engine
The template engine can be used for either Day-0 (PnP) or Day-N (provisioning) changes. There are a couple of subtle implications of this.
- All templates are rendered on DNAC. This means the velocity language does not have access to the current configuration of the device.
- PnP templates are pulled from DNAC as a single configuration file and copied to running configuration. Note that composite templates are not currently supported.
- Day-N configuration pushes the rendered configuration to the device. This mode supports interactive commands as well as exec commands. EEM scripts can be used to address this limitation for PnP.
Basic Variables
At the heart of any template is the concept of a “variable”. Variables allow parts of a configuration to be customized for a specific device, while ensuring other parts are standardized across all devices. A single configuration template is required, but it can be applied to many devices. In velocity, variables begin with “$”. A trivial example follows.
hostname $hname
To configure a hostname for a network device, the cli command “hostname adam-router” is used. “adam-router” is the name of the device. When applying this template to a set of devices, the only thing that changes is the variable ( $hname ).
Advanced Variables
In the example above, there may be a requirement to always end the hostname with “-router”. The “{}” can be used to specify which part of a string is a variable and which is a string. In the example below, the suffix will always be “-router” and $hname would be set to “adam” to achieve a similar result to the first example.
hostname ${hname}-router
When the variable $hname is set to “adam” the template renders as
hostname adam-router
Escaping Variables
Occasionally “$” is required to be used in the configuration for something other than a variable. This is often seen in encrypted passwords for example. It is possible to mark these as “not a variable” in the UI.
It is also possible to create a dummy variable for “$” and use that instead.
#set ($d = "$") enable secret 5 werw${d}2dsf3423${d}dd${d}
In the example above, a variable “d” is defined as “$”. The “${d}” syntax is used where ever a “$” is required. The template above renders as
enable secret 5 werw$2dsf3423$dd$
Macros
Macros are a great way of encapsulating a block of CLI. For example, define a configuration block for an uplink. For example, define a configuration block for an uplink. Once the macro “uplink_interface” has been defined, it can be referenced as #uplink_interface
#macro(uplink_interface) switchport trunk native vlan 999 switchport mode trunk channel-group 1 mode active #end interface Ten1/0/1 #uplink_interface
This will render as:
interface Ten1/0/1 switchport trunk native vlan 999 switchport mode trunk channel-group 1 mode active
Loops
Loops are a powerful control construct that can be used in templates. One common application is when combined with macros. The macro “uplink_interface” was defined above, and it needs to be applied to every uplink in a switch stack. A variable called stack_count is provided to the template which contains the number of switches in the stack.
#set ($stack_member_count = $stack_count) #foreach($switch in [1..${stack_member_count}]) interface Ten${switch}/0/1 #uplink_interface #end
When the variable “stack_count” is set to “2”, the template renders as shown:
interface Ten1/0/1 switchport trunk native vlan 999 switchport mode trunk channel-group 1 mode active interface Ten2/0/1 switchport trunk native vlan 999 switchport mode trunk channel-group 1 mode active
It is important to define the data type of $stack_count as an integer. An integer will be treated as a number so the range [1..4] is shorthand for [1,2,3,4] for example. The earlier blog showed how to change the type of a variable. Here is a reminder
Manipulating IP Addresses
The final example shows how to manipulate IP addresses. A common example is supplying an IP address and needing to create the default gateway in a configuration file. The variable $ip has been defined as a string with IP address syntax. The split command breaks the string up on octet boundaries and returns an array. This is assigned to the variable $octets.
#set ($octets = $ip.split('\.'))
gateway $octets[0].$octets[1].$octets[2].1
If $ip is set to “10.11.12.240”, the template will render as
gateway 10.11.12.1
Optionally the data type of “$ip” can be set to ipAddress. This will do input validation in the UI of the template. If left as Data Type string, then any text could be entered.
What Next?
This blog shows more detail on the template language – velocity. The next blog in the series will cover some advanced use cases
In the meantime, if you would like to learn more about this, you could visit Cisco DevNet. DevNet has further explanations about this. In addition, we have a Github repository where you can get examples related to PnP.
Thanks for reading!
We’d love to hear what you think. Ask a question or leave a comment below.
And stay connected with Cisco DevNet on social!
Twitter @CiscoDevNet | Facebook | LinkedIn
Visit the new Developer Video Channel
Looking for more advanced examples
These are just to get people started. What types of things were you looking for?
Looking forward for advanced use cases. I am very new to VTL and DNAC, but am familiar with Python and Jinja (I was a bit disappointed that DNAC doesn't support Jinja templates). What I am trying to understand is this…
Let's say I have a number of Regular Templates defined, such as AAA-TACACS and AAA-LocalCredentials… both templates have if/elseif/else block that use the same variable ($region). In my understanding, once I combine both into a Composite template, I will have to define region value TWICE…
Is there a way to use variables that SPAN across multiple templates, bu tonly define their values ONCE? Thank you!
at this point there is no concept of global variables.
There is the concept of system variables, where i can get access to site specific settings. This is not available in PnP as the device has not been assigned to a site.
new to working with dnac and python etc
with Mgig cards and non mgig cards specifying int ranges makes it more difficult. Is there a way to automate this with running commands against the switch or without user input?
Is there a way to call on a csv file for variables so I do not have to input them every time I bring up a switch in my environment?
hi Eric, i have a script using the API that allows a CSV file to populate variables. This was in the https://blogs.cisco.com/developer/plug-and-play-api-in-dna-center-part-2 blog.