Configuration Templating Language
Tags: python, devops, showdev, discuss
Published: 2019-08-19 19:52:04

Have you ever been in a situation where you need to write multiple configuration files manually? I have been managing multiple router configurations. The routers were following the same pattern, but each one of them had to have different opened / filtered ports, Network Address Translation rules, different routes, etc.

This could be resolved by using a configuration file template and some scripting to push variable data into the template. While jinja templates and python scripts may be a fairly good solution, I was still struggling with two questions:

* Does a simpler and more powerful templating language for re-writing configurations exist out there?
* Is there a simple command line tool for templating?

As I could not find satisfactory answers to my questions, I decided to start this project called [conftl - Configuration Templating Language](https://github.com/ttt-fifo/conftl). Let me give some examples how to use conftl.

## Example 1: Open Network Ports on Firewall

I do not want to bother you with all the router details, so I will show only simple Linux iptables firewall example.
The template file firewall.tmpl is created with the following content:

```
# ... other iptables rules ...

{{for dport in dports:}}
-A INPUT -i eth0 -p tcp --dport {{=dport}} -m state --state NEW,ESTABLISHED -j ACCEPT
{{pass}}

# ... other iptables rules ...

```

What do we have in this template? We see that we can embed python code, wrapped with ```{{...}}``` So what this means? - everyone who has some idea of the Python syntax (and the Python syntax is a really clean one), could embed Python constructs into the template. Possibilities are endless: conditional expressions if - else, while and for loops, assigning values to variables, even function definitions are possible in template.

What else we see in there? The construct ```{{=dport}}``` is somehow weird. This is how we print a variable value to the output configuration file.

The other funny thing is the special keyword ```{{pass}}``` which indicates where our for loop block ends.

Generally we just templated one Python for loop, which prints to the output config file one iptables rule per each port in dports. Now we need a way to render the template and specify which network ports to open. This can be done using the command line utility 'render':

```
$ render -i firewall.tmpl -c "dports=[22, 80, 8080, 443]"

-i firewall.tmpl where to get the template from
-c "dports=[...]" gives the list with the ports to open
```

The output will be printed to the screen for debug:

```
# ... other iptables rules ...

-A INPUT -i eth0 -p tcp --dport 22 -m state --state NEW,ESTABLISHED -j ACCEPT
-A INPUT -i eth0 -p tcp --dport 80 -m state --state NEW,ESTABLISHED -j ACCEPT
-A INPUT -i eth0 -p tcp --dport 8080 -m state --state NEW,ESTABLISHED -j ACCEPT
-A INPUT -i eth0 -p tcp --dport 443 -m state --state NEW,ESTABLISHED -j ACCEPT

# ... other iptables rules ...
```

In real situation you would want to give an output file also:

```
$ render -i firewall.tmpl -o firewall.conf -c "dports=[22, 80, 8080, 443]"

-o firewall.conf where to write the configuration file
```

## Example 2: Nginx Proxy

Imagine you need to dynamically configure your reverse proxy. The example template would be:

```
# ... other nginx configurations

<%for loc in locations:%>
<%server_port = locations[loc]%>
        location /<%=loc%> {
            rewrite ^/<%=loc%>(.*) /$1 break;
            proxy_pass http://<%=server_port%>;
        }
<%pass%>

# ... other nginx configurations
```

Here you do not see the ```{{...}}``` pattern. Nginx configuration file uses ```{}``` brackets for opening/closing the blocks, so we may use different delimiters ```"<% %>"``` for better visibility.

In this example we will not give the values of the 'locations' variable at the command line. I will create another text file containing the json context. Context means assigning values to the template variables. Lets create the file 'nginx.ctx':

```
{"locations": {"blog": "192.168.43.15:8081",
               "mail": "192.168.43.15:8082"}}
```

After running the command line tool:

```
$ render -i nginx.tmpl -j nginx.ctx -d "<% %>"

-j tells render where to get json context from
-d "<% %>" instructs render for the delimiters we are currently using
```

we will receive the output:

```
# ... other nginx configurations

        location /blog {
            rewrite ^/blog(.*) /$1 break;
            proxy_pass http://192.168.43.15:8081;
        }
        location /mail {
            rewrite ^/mail(.*) /$1 break;
            proxy_pass http://192.168.43.15:8082;
        }

# ... other nginx configurations
```

## Installation, Advanced Usage, More Examples

I presented two examples just for taste what conftl is. More information and advanced usage cases may be found at the project repository:

[https://github.com/ttt-fifo/conftl](https://github.com/ttt-fifo/conftl)


## Conclusion

I was searching for a simple, but powerful solution to template my configuration files. I find delightful the possibility to embed Python code into the template. I also find it convenient to have a command line tool for rendering templates. I would be happy if other people also like this approach and start using [conftl](https://github.com/ttt-fifo/conftl)

Love to hear your opinion and comments.