Templating

Warewulf uses the text/template engine to convert dynamic content into static content and auto-populate files with the appropriate data on demand.

In Warewulf, you can find templates both for the provisioning services (e.g. /etc/warewulf/ipxe/, /etc/warewulf/dhcp/, and /etc/warewulf/hosts.tmpl) as well as within many of the provided overlays.

(more documentation coming soon)

Examples

Comment

{{  /* This comment won't show up in file, but an empty line /* }}
{{  /* No empty line, line break removed at end of this line /* -}}
{{-  /* No empty line, line break removed at front of this line /* }}

Range

iterate over elements of an array

{{ range $devname, $netdev := .NetDevs }}
    # netdev = {{ $netdev.Hwaddr }}
{{ end }}

Increment Variable In Loop

iterate over elements of an array and increment i each loop cycle

{{ $i := 0 }}
{{ range $devname, $netdev := .NetDevs }}
    # netdev{{$i}} = {{ $netdev.Hwaddr }}
    {{ $i = inc $i }}
{{ end }}

Decrement

iterate over elements of an array and decrement i each loop cycle

{{ $i := 10 }}
{{ range $devname, $netdev := .NetDevs }}
    # netdev{{$i}} = {{ $netdev.Hwaddr }}
    {{ $i = dec $i }}
{{ end }}

Access Tag

Acces the value of an individual tag foo

foo: {{ index .Tags "foo" }}
{{ if eq (index .Tags "foo") "baar" -}}
foo: {{ index .Tags "foo" }}
{{ end -}}

Create Multiple Files

The following template will create a file called ifcfg-NETWORKNAME.xml for every network present on the node

{{- $host := .BuildHost }}
{{- $time := .BuildTime }}
{{- $source := .BuildSource }}
{{range $devname, $netdev := .NetDevs -}}
{{- $filename := print "ifcfg-" $devname ".xml" }}
{{ file $filename }}
<!--
This file is autogenerated by warewulf
Host:   {{ $host }}
Time:   {{ $time }}
Source: {{ $source }}
-->
<interface origin="static generated warewulf config">
  <name>{{$netdev.Device}}</name>
  {{ if $netdev.Type -}}
  <link-type>{{ $netdev.Type }}</link-type>
  {{ end -}}
  <control>
    <mode>boot</mode>
  </control>
  <firewall/>
  <link/>
  <ipv4>
    <enabled>true</enabled>
    <arp-verify>true</arp-verify>
  </ipv4>
  <ipv4:static>
    <address>
      <local>{{$netdev.Ipaddr}}/{{$netdev.Ipmask}}</local>
    </address>
{{ if $netdev.Gateway -}}
    <route>
      <nexthop>
        <gateway>{{$netdev.Gateway}}</gateway>
      </nexthop>
    </route>
{{ end -}}
  </ipv4:static>
  <ipv6>
    <enabled>true</enabled>
    <privacy>prefer-public</privacy>
    <accept-redirects>false</accept-redirects>
  </ipv6>
{{ if $netdev.Ipaddr6 -}}
  <ipv6:static>
    <address>
      <local>{{ $netdev.Ipaddr6 }}</local>
    </address>
  </ipv6:static>
{{ end -}}
</interface>
{{ end -}}

Special Functions

Include

A file from the host can be included with following template

{{ Include file }}

IncludeFrom

With following snippet a file from a given image can be included

{{ IncludeFrom image file }}

IncludeBlock

Includes a file up to the line where a abort string is found. This is useful, e.g., for the hosts file, which can have local modifications which are not controlled by warewulf. For this example the abort string is “# Do not edit after this line”

{{ IncludeBlock "/etc/hosts" "# Do not edit after this line" }}
# This block is autogenerated by warewulf
# Host:   {{.BuildHost}}
# Time:   {{.BuildTime}}
# Source: {{.BuildSource}}


# Warewulf Server
{{$.Ipaddr}} warewulf {{$.BuildHost}}

{{- range $node := $.AllNodes}}                  {{/* for each node */}}
# Entry for {{$node.Id}}
{{- range $devname, $netdev := $node.NetDevs}} {{/* for each network device on the node */}}
{{- if $netdev.Ipaddr}}                {{/* if we have an ip address on this network device */}}
{{- /* emit the node name as hostname if this is the primary */}}
{{$netdev.Ipaddr}} {{$node.Id}}-{{$devname}}
{{- if $netdev.Device}} {{$node.Id()}}-{{$netdev.Device}}{{end}}
{{- if $netdev.Primary}} {{$node.Id()}}{{end}}
{{- end}} {{/* end if ip */}}
{{- end}} {{/* end for each network device */}}
{{- end}} {{/* end for each node */}}

Abort

If {{ abort }} is found in a template, the resulting file isn’t written.

Nobackup

If a file exists on the target, a backup file is written with the suffix .wwbackup. This only happens for the host overlay, as e.g. the /etc/hosts exists on the host. If this is not the intended behavior, the {{ nobackup }} flag can be added to a template.

Split

A given string can be split into substrings.

{{ $x := "a:b:c" -}}
{{ $y := (split $x ":") -}}
{{ range $y }} {{.}} {{ end }}

UniqueField

UniqueField returns a filtered version of a multi-line input string. input is expected to be a field-separated format with one record per line (terminated by n). Order of lines is preserved, with the first matching line taking precedence.

For example, the following template snippet has been used in the syncuser overlay to generate a combined /etc/passwd.

{{
    printf "%s\n%s"
        (IncludeFrom $.ImageName "/etc/passwd" | trim)
        (Include (printf "%s/%s" .Paths.Sysconfdir "passwd") | trim)
    | UniqueField ":" 0 | trim
}}

Node specific files

Sometimes there is the need to have specific files for every node which can’t be generated by a template. You can include these files with following template:

{{- $filename := print "/root/" .Id "-payload" }}
{{ Include $filename }}