Wer sich schon mal mit Puppet und XML-Konfigurationsdateien beschäftigt hat, weiß das dies ein schwer umzusetzendes Vorhaben in Puppet ist. Es gibt zwar eine Linse für Augeas, wer aber eine komplette XML-Datei mit Puppet verwalten möchte, wird damit schnell wahnsinnig.
Ich hatte vor einigen Wochen das Glück, mich bei einem Kunden, näher mit der Konfiguration vom Tomcat beschäftigen zu dürfen. Dabei stand ich vor dem Problem, z.B. folgende Struktur als server.xml mit Puppet erzeugen zu müssen.

<Server>
  <Service name=Catalina>
    <Engine name=Catalina>
      <Host name=...>
        ...
      </Host>
      <Host name=...>
        ...
      </Host>
    </Engine>
  </Service>
</Server>

Wobei in den Host-Sektionen natürlich auch nochmals Schachtelungen vorkommen. Mein erster Lösungsansatz war, mit XML Referenzen auf eigenständige Dateien zu verweisen, die damit eingebunden werden. Dies führt nun aber zu einem recht unübersichtlichen Datei-Tohuwabohu.
Die jetzige Lösung macht sich die Eigenschaften des Puppet concat-Moduls zu nutze. Die einzelnen Fragmente werden auf dem Puppet-Agent jeweils als separate Datei in ein temporäres Verzeichnis geschrieben, z.B. nach /var/lib/puppet/concat/_etc_tomcat_server.xml/fragments. Das Attribut order von concat::fragment dient hierbei als Prefix für den Dateiname des entsprechenden Fragments. Sind alle Fragmente geschrieben, liest ein Skript alle Dateien mittels find, sortiert lexikalisch nach Dateinamen und fügt die Fragmente abschließend mittels cat in die Zieldatei zusammen, hier /etc/tomcat/server.xml.
Schaut man sich dieses find näher an, stellt man fest, dass es nicht tiefenbeschränkt ist, d.h. es durchsucht auch weitere Unterverzeichnisse. Diese können vorab mit einer files-Resource angelegt werden und im order-Attribut mit angegeben werden.

concat { '/etc/tomcat/server.xml':
  mode => '0644',
} ->
file { '/var/lib/puppet/concat/_etc_tomcat_server.xml/50_host_A':
  ensure => directory,
}
concat::fragment { 'engine-header':
  target => '/etc/tomcat/server.xml',
  content => "\n",
  order => '40',
}
concat::fragment { 'engine-footer':
  target => '/etc/tomcat/server.xml',
  content => "\n",
  order => '60',
}
concat::fragment { 'host_A-header':
  target => '/etc/tomcat/server.xml',
  content => "\n",
  order => '50_host_A/00',
  require => File['/var/lib/puppet/concat/_etc_tomcat_server.xml/50_host_A'],
}
concat::fragment { 'host_A-footer':
  target => '/etc/tomcat/server.xml',
  content => "\n",
  order => '50_host_A/99',
  require => File['/var/lib/puppet/concat/_etc_tomcat_server.xml/50_host_A'],
}

Analog für weitere Hosts. Zusätzliche Fragmente für Host A lassen sich nun zwischen dem Header und Footer einordnen. Dies soll erstmal nur die Idee vergegenwärtigen. Ein komplexeres Anwendungsbeispiel findet sich unter https://github.com/lbetz/netways-tomcat.
Auch ist zu bedenken, das auf unterschiedlichen Hosts das temporärere concat-Verzeichnis nicht immer am selben Ort zu finden ist. Hier schaft ein selbstgeschriebener Fact Abhilfe.

# vardir.rb
Facter.add(:vardir) do
  setcode do
    Puppet[:vardir]
  end
end
Lennart Betz
Lennart Betz
Senior Consultant

Der diplomierte Mathematiker arbeitet bei NETWAYS im Bereich Consulting und bereichert seine Kunden mit seinem Wissen zu Icinga, Nagios und anderen Open Source Administrationstools. Im Büro erleuchtet Lennart seine Kollegen mit fundierten geschichtlichen Vorträgen die seinesgleichen suchen.