Template_merge is a perl program that allows you to output text based files that are the result of a parameterized template that is merged with various anchor types. It is useful for creating generic templates that can be merged to create site or job specific files to be used by downstream processes.
The classic case would be to create a template for your Cisco IOS configuration. You could start by doing a “show run” on a device. You would then determine each portion of the config that will change from device to device. Those portions would be replaced with an appropriate anchor that will get its value from another location such as an upstream database. This would now allow you to execute a template_merge that can output a device specific configuration for any number of similarly configured IOS devices.
Take the following IOS config example:
! This config was created on $[cmdx:date] by $[env:LOGNAME] ! hostname $[parm:ROUTER_NAME] ! ip name-server $[dns:ns1.yoursite.com] ip name-server $[dns:ns2.yoursite.com] ip ssh version 2 ! no ip http server ! logging $[parm:LOGSERVER1] logging $[parm:LOGSERVER2] ! interface FastEthernet0/1 description $[parm:DESKTOP_VLAN_DESC] ip address $[parm:DESKTOP_VLAN_GATEWAY_IP] $[parm:DESKTOP_VLAN_MASK]
This would output the following once merged, assuming an appropriate parm file had been extracted from a database providing these values:
! This config was created on Wed Dec 20 11:16:07 MST 2006 by root ! hostname router-a ! ip name-server 10.1.1.1 ip name-server 10.2.2.2 ip ssh version 2 ! no ip http server ! logging 10.4.4.1 logging 10.4.4.2 ! interface FastEthernet0/1 description VLAN_DESKTOP_USERS_001 ip address 10.10.10.1 255.255.255.0
Using the -o option the output could be written to /tftpboot/router-a.cfg and then tftped into the device on bootup or used by an config update script.
Requirements
The unix host utility is used to do dns lookups when using the $[dns:xxx] option. It must be in your path and a proper /etc/resolv.conf file for the names you intend to look up.
template_merge is a perl script/program, and only needs to be copied to a directory in your path to make it accessible. Most likely the following steps will be sufficient:
tar -zxvf template_merge-v1.XX.tar.gz
cp template_merge-v1.XX/template_merge /usr/local/bin
chmod +x /usr/local/bin/template_merge
template_merge or /usr/local/bin/template_merge
NOTES:
Running template_merge by itself will give you the usage synopsis. It only requires one option, which is -t to specify a template. It is possible to have a template without the need for the -p option if it does not use the “parm” anchor. This is usualy not the case however.
Failures:
Each anchor should put some sort of UNKNOWN_XXXXX message in place of the anchor. You should be able to search through the output to determine what failed by searching for the key word “UNKNOWN”
Return codes can be tested via the shell variable $? and should be 0 on success or anything greater than 1 on failure. The return code should be a count of how many things failed.
A template is simply a text file containing the raw text with anchors to be replaced during a template_merge. Anchors are specific tags that are replaced with valid data based on the type of anchor used.
Details of each anchor:
$[parm:MY_PARAMETER]
-- Uses the value of MY_PARMAMETER from the specified parm file
$[include:/tmp/file]
-- Uses the contents of /tmp/file as additional template data
If you use the -d option you can set a default directory for
include files to be located. This helps one not have to
fully qualify the path to each file included. Include paths
can also be executables that output to STDOUT. For example:
$[include:/bin/extract_site_template my_template] could be a
program that gets the template named my_template from a database
table containing templates
$[env:HOSTNAME]
-- Uses the value of the environment var $HOSTNAME
$[dns:www.google.com]
-- Uses the first IP address returned by the gethostbyname function
$[cmd:who]
or
$[cmdx:who]
-- Uses the output of specified command with a trailing newline (cmd) or
no trailing newline (cmdx). When using cmd or cmdx commands all entries
in any specified parm file will be placed into the environment with
MERGE_ appended to them. So a parm entry of SITE_NAME=my_cool_site
becomes MERGE_SITE_NAME=my_cool_site in the environment. This allows
one to utilize parm entries from within their command line tools.
$[if:{type}question:answer]
...
$[endif]
-- Displays the contents between the if and endif statements when the
condition matches. The available types are parm, env, and dns. The
result of "{type}question" should match the "answer". You can use the
"notif" operator to negate the match. For the parm and env types you
can leave the "answer" blank to test for the simple existence of the
"question". You can also use simple regular expression matching in
the "answer" field.
Examples:
$[if:{parm}SOME_NUM:1234] -- matches if the parm SOME_NUM was set to 1234
$[if:{env}USER:mdp] -- matches if the environment var USER is set to mdp
$[if:{dns}google.com:64.233.187.99] -- matches if google.com resolves to
ip address 64.233.187.99. (Will match any, if multiple IPs are returned)
$[notif:{parm}ABCD:1234] -- matches if the parm ABCD was NOT set to 1234
$[if:{parm}ABCD:] -- matches if the parm ABCD exists, no matter its value
$[if:{env}PATH:.*:/usr/bin:.*] -- Checks that /usr/bin/ is in the PATH
NOTE: When using cmd or cmdx commands all entries in any specified parm file will be placed into the environment with MERGE_ appended to them. So a parm entry of SITE_NAME=my_cool_site becomes MERGE_SITE_NAME=my_cool_site in the environment. This allows one to utilize parm entries from within their command line tools.
A parm file is simply a file containing key equals value pairs similar to an .INI file. A parm file is usually something that is generated from a database or is maintained manually in the context of a /etc/master.parm file. This could contain settings that are global to a particular system such as an enable password or dns server IP address.
Parm file format is a simple KEY=value. Each key name SHOULD be in uppercase. Key names MUST be one word. Parm values can be any string, with spaces. You can put comments in using the # character but they must be the first character of the line. You can use the standard include anchor to include other parm files into this parm. The include anchor can be a file or an executable that outputs parm file format to STDOUT. Include statements MUST be the first lines of the parm file to ensure the are read first. If you have multiple include file statements, they will be read in order listed in the parm file. This means that any included file could overwrite the value of a duplicate key listed in another included file specified previously. This is useful for including higher level global parm files.
An example:
$[include:/etc/master.parm] $[include:cat /tmp/test.parm] USER_NAME=Matt Pascoe LOGIN=mdp # Just a fake hire date HIRE_DATE=1/1/20 HOST_IP=10.1.2.4
The -s option is for adding a “suffix” to the generated template. It can be used as a general signature or in the case of templating Cisco configurations you can put ”!\nend” to ensure that there is always an end statement on the config.
The -o option is used to output the generated results to the specified file.
The -v option will increase the debug output verbosity the more times it is used. -v for debug level 1 or -vvv for debug level 3.
The -d option sets the working directory so that include anchors dont have to have full file paths listed in them.
EXAMPLES:
There are some included test parm files and template files in the directory “examples”. To execute them simply run the following command while in that directory:
template_merge -t template.tmpl -p parm.parm
Running this should give you an example of that each option does. It shows both a successfull invocation as well as what happens when each anchor fails due to bad information or syntax.
Can't exec "template.tmpl": No such file or directory at ./template_merge line 749. Can't exec "parm.parm": No such file or directory at ./template_merge line 713.
This means that the files specified by -p or -t or that have been defined by $[include:blah] are not found or are not executable. Due to the use of backticks to execute files, I'm not able to test properly without forcing ALL executed commands to be fully qualified. I chose to just let things error out this way as it is sufficiently describing the problem.
The team at my prior employer who helped come up with many ideas for this tool.
Brandon Zehm <http://caspian.dotconf.net> – Who I have patterned a lot of this code from.
Eric Woerner – Thanks for all the testing and code breaking.