Staattisten faktojen kirjoittaminen on varsin helppoa melko vähäisinkin Ruby-taidoin. Alla esimerkki faktasta, joka palauttaa true tai false riippuen siitä, onko noodilla /boot-osiota:

Facter.add(:has_bootfs) do
 setcode do
 if Facter.value(:mountpoints)['/boot'].nil?
 false
 else
 true
 end
 end
 end

Kuten yltä näkyy, itse tieto löytyi jo valmiiksi Facterin "mountpoints"-faktan sisältä: yllä sen sisältöä vain muunnettiin erilliseksi, helposti käsiteltäväksi faktaksi visualisointia ja skriptausta helpottamaan.

Mountpoints-fakta sisältää myös paljon muuta kiinnostaa tietoa, kuten sen, miten suuri osio on ja paljonko siellä on tilaa vapaana. Jos näistäkin tiedoista halutaan luoda samaan tapaan erilliset faktat, voidaan ne helposti luoda dynaamisesti:

if Facter.value(:has_bootfs)
 facts = [ 'size','available']
 
 facts.each do |fact|
 Facter.add("bootfs_#{fact}") do
 setcode do
 Facter.value(:mountpoints)['/boot'][fact]
 end
 end
 end
 end

Yllä oleva koodi siis luo kaksi uutta faktaa mutta vain jos /boot-osio on olemassa:

Näitä faktoja voidaan käyttää normaaliin tapaan:

$ facter -p has_bootfs
 true
 $ facter -p bootfs_size
 235.32 MiB
 $ facter -p bootfs_available
 111.28 MiB

Vaikka yllä olevassa esimerkissä ei varsinaisesti luoda mitään uutta dataa, voi samalla strategialla muodostaa räätälöityjä faktoja dynaamisesta sisällöstä. Alla luodaan erillinen fakta ("user_<username>_is_present") jokaisesta *NIX-järjestelmässä olevasta käyttäjästä pois lukien järjestelmäkäyttäjät:

require 'etc'
 
 Etc.passwd do |entry|
 # Normal users have IDs in this range in /etc/login.defs
 if entry.uid >= 1000 and entry.uid <= 60000
 Facter.add("user_#{entry.name}_is_present") do
 setcode do
 true
 end
 end
 end
 end

Näiden dynaamisesti luotujen faktojen käyttö on helppoa:

$ facter -p user_john_is_present
 true
 $ facter -p user_jack_is_present
 
 $ facter -p user_jane_is_present
 true

Ainoa rajoitus on se, että datasta, jota ei ole olemassa, ei voida tietenkään luoda faktaa. Esimerkiksi yllä fakta käyttäjästä "jack", jota ei ole olemassa, saa tyhjän arvon (undef/nil) sen sijaan, että se palauttaisi totuusarvon false. Tällä ei Puppet-koodin tai faktojen visualisoinnin kannalta ole kuitenkaan suurta merkitystä.

NOTE: this article is now also available in English (here).

Aiemmassa blogikirjoituksessa käsittelin Augeaksen käyttöä Puppetin kanssa esimerkkinä PostgreSQL:n pg_hba.conf -tiedoston muokkaus. Tässä kirjoituksessa käsitellään XML-tiedostojen muokkausta Augeaksella, mikä on luultavasti siistein ratkaisu silloin, kun ei haluta hallita koko tiedostoa esimerkiksi ERB-templatena. Alla muokataan Pwm:n web.xml-tiedostoa, johon on määritettävä asetushakemiston polku.

Ennen itse Augeas-resurssin kirjoitusta kannattaa tarkistaa, mikä Augeaksen käsitys muokattavan tiedoston rakenteesta on. Ensin augtoolille pitää kertoa, mistä hallittava tiedosto löytyy:

$ augtool
 augtool> set /augeas/load/Xml/incl[1] /var/lib/tomcat8/webapps/pwm/WEB-INF/web.xml
 augtool> load

Nyt Augeas näkee var-hakemiston polussa /files:

augtool> ls /files
 etc/ = (none)
 lib/ = (none)
 home/ = (none)
 boot/ = (none)
 augtool> load
 augtool> ls /files
 etc/ = (none)
 lib/ = (none)
 home/ = (none)
 boot/ = (none)
 var/ = (none)

Varmistetaan vielä, että muokattava tiedosto näkyy Augeakselle:

augtool> ls var/lib/tomcat8/webapps/pwm/WEB-INF/
 web.xml/ = (none)

Nyt katsotaan, miltä tiedosto Augeaksen mielestä näyttää. Tämä komento tuottaa valtavan määrän tulostetta, josta alla näytetään vain oleellinen:

augtool> print var/lib/tomcat8/webapps/pwm/WEB-INF/web.xml/
 --- snip ---
 /files/var/lib/tomcat8/webapps/pwm/WEB-INF/web.xml/web-app/context-param/param-name
 /files/var/lib/tomcat8/webapps/pwm/WEB-INF/web.xml/web-app/context-param/param-name/#text = "applicationPath"
 /files/var/lib/tomcat8/webapps/pwm/WEB-INF/web.xml/web-app/context-param/#text[3] = " "
 /files/var/lib/tomcat8/webapps/pwm/WEB-INF/web.xml/web-app/context-param/param-value
 /files/var/lib/tomcat8/webapps/pwm/WEB-INF/web.xml/web-app/context-param/param-value/#text = "unspecified"
 /files/var/lib/tomcat8/webapps/pwm/WEB-INF/web.xml/web-app/context-param/#text[4] = " "
 --- snip --

Tämän pohjalta voidaan jo luoda varsinainen Augeas-resurssi:

 augeas { 'pwm applicationPath':
 incl => "/var/lib/tomcat8/webapps/pwm/WEB-INF/web.xml",
 lens => 'Xml.lns',
 changes => "set web-app/context-param/param-value/#text /etc/pwm",
 }

Resurssi kannattaa tallentaa esim. augtest.pp -tiedostoon ja alustavasti testata puppet apply-komennolla:

$ puppet apply augtest.pp

Tämän jälkeen koodin voikin jo integroida haluttuun Puppet-moduuliin (tässä tapauksessa puppet-pwm).

Puppetin palvelinpuoleen kuuluu on nykyisin aikamoinen tukku palvelinsovelluksia, työkaluja ja suositeltuja työnkulkuja:

  1. Palvelinkomponentit
    • Puppetserver: Varsinainen Puppet-palvelin, johon Puppet agentit ottavat yhteyttä. Toimii myös itsenäisesti ilman PuppetDB:tä ja Puppetboardia
    • PuppetDB: Datasäilö, johon Puppet agentit tallentavat faktojaan ja raporttejaan. Dataan päästään haluttaessa käsin ohjelmallisesti.
    • PostgreSQL: PuppetDB:n taustalla oleva tietokantapalvelin
    • Puppetboard: PuppetDB:n datan visualisoitiin tarkoitettu webbisovellus.
    • Apache 2: Puppetboardin taustalla oleva webbipalvelin
  2. Työkalut
    • Git: versionhallintajärjestelmä, josta hallintarepo, r10k ja monien Puppet moduulien käyttö on riippuvaista.
    • Hiera: hierarkinen datasäilö Puppet-moduulien, esim. profiilien, parametreille. Voidaan käyttää asiakaskoneiden asetusten räätälöintiin eri luokitteluperustein. Luokittelu voi perustua faktoihin (esim. käyttöjärjestelmäversio, datakeskus) tai asiakaskoneelle erikseen määritettyihin arvoihin (esim. asiakas, koneen kriittisyys liiketoiminnan kannalta).
    • Eyaml: Hieran datatiedostossa (yaml) olevan datan kryptaukseen tarkoitettu työkalu. Tärkeä erityisesti silloin, kun Hieran dataa säilötään organisaation ulkopuolella kuten esimerkiksi GitLabissa.
    • R10k: työkalu dynaamisten Puppet environmentien hallintaan. Käytännössä r10k luo kustakin hallintarepon haarasta Puppet environmentin. Kukin haara sisältää environmentin vaatimat Puppet-moduulit sekä niiden tarvitsemat parametrit.
  3. Suositellut työnkulut

Windowsin järjestelmätiedostoja ei useinkaan pysty muuttamaan käsin - tai Puppetilla - ilman niiden suojaustason alentamista. Ongelma on siinä, että järjestelmätiedostojen omistaja on usein (pseudo)käyttäjä "TrustedInstaller", eikä edes administrator-tason käyttäjillä ole niihin kirjoitusoikeutta. Onneksi rajoitukset voi kiertää melko suoraviivaisesti käyttämällä takeown.exe -komentoa sekä puppetlabs/acl -moduulia. Alla on muokattu erästä Group Policyn XML-tiedostoa, jonka merkistökoodaus oli siinä määrin poikkeuksellinen, että sen lukeminen Rubyn reXML -kirjastolla ei onnistunut:

include ::acl
 
 # Vaihda tiedoston omistaja
 exec { 'takeown-Search.admx':
 command => 'C:WindowsSystem32takeown.exe /f C:WindowsPolicyDefinitionsSearch.admx',
 }
 
 # Määritä uudet oikeudet
 acl { 'Search.admx':
 target => 'C:/Windows/PolicyDefinitions/Search.admx',
 permissions => [
 { identity => 'Järjestelmänvalvojat', rights => ['full'] },
 { identity => 'SYSTEM', rights => ['full'] }
 ],
 require => Exec['takeown-Search.admx'],
 }
 
 # Korvaa tiedosto
 file { 'C:/Windows/PolicyDefinitions/Search.admx':
 source => 'puppet:///modules/winpolicydefinitions/Search.admx',
 require => Acl['Search.admx'], 
 }

Tästä prosessista kannattaisi muokata oma määritelty resurssinsa, tai jopa peräti oma tyyppinsä, mikäli sille löytyisi runsaasti käyttöä. Takeown-komentoon kannattaisi lisätä myös "unless"-parametri, jotta sitä ei ajeta turhaan joka kerta. Jos Execissä olisi "provider => 'powershell'", niin tarkistuksen voisi tehdä muutamin eri tavoin.

NOTE: this article is now also available in English.

Puppetin Augeas-resurssilla voidaan muokata konfiguraatiotiedostojen osia sen sijaan, että hallittaisiin koko tiedostoa esim. templatella tai staattisella tiedostolla. Koko tiedoston hallinta tuottaa yleensä varmemmin ja vähemmällä vaivalla halutun lopputuloksen, mutta erityisesti luettelomaisten konfiguraatiotiedostojen tapauksessa Augeas-resurssi on erittäin käyttökelpoinen. Augeaksen käyttö ei-triviaaleissa tapauksissa on kuitenkin varsin haastavaa, ja usein onkin hyödyllistä rakentaa oikeat komennot käsin "augtool"-kehotteessa ja vasta sitten muuntaa ne Puppetin Augeas-resurssiksi.

Aluksi kannattaa tutkia, mikä Augeaksen näkemys jonkin konfiguraatiotiedoston rakenteesta on (ylimääräisiä rivejä on poistettu):

augtool> ls /files/etc/postgresql/9.4/main/pg_hba.conf
 --- snip ---
 #comment[65] = Database administrative login by Unix domain socket
 1/ = (none)
 2/ = (none)
 3/ = (none)
 4/ = (none)

Yllä muokattavat rivit (objektit) on numeroitu (1-4). Kunkin sisällön saa selville seuraavasti:

augtool> print /files/etc/postgresql/9.4/main/pg_hba.conf/1
 /files/etc/postgresql/9.4/main/pg_hba.conf/1
 /files/etc/postgresql/9.4/main/pg_hba.conf/1/type = "local"
 /files/etc/postgresql/9.4/main/pg_hba.conf/1/database = "all"
 /files/etc/postgresql/9.4/main/pg_hba.conf/1/user = "postgres"
 /files/etc/postgresql/9.4/main/pg_hba.conf/1/method = "peer"

Augeaksella voidaan muokata näitä objekteja (1-4). Esimerkiksi jos objektin 1 kentän "method" arvoksi haluttaisiin asettaa "password", tehtäisiin näin:

augtool> set /files/etc/postgresql/9.4/main/pg_hba.conf/1/method password
 augtool> print /files/etc/postgresql/9.4/main/pg_hba.conf/1
 /files/etc/postgresql/9.4/main/pg_hba.conf/1
 /files/etc/postgresql/9.4/main/pg_hba.conf/1/type = "local"
 /files/etc/postgresql/9.4/main/pg_hba.conf/1/database = "all"
 /files/etc/postgresql/9.4/main/pg_hba.conf/1/user = "postgres"
 /files/etc/postgresql/9.4/main/pg_hba.conf/1/method = "password"

Jos tiedostoon halutaan lisätä uusi rivi, pitää käyttää "ins"-komentoa, mikä onkin asteen verran hankalampaa. Ensin lisätään uusi rivi ensimmäisen tunnistautumisrivin jälkeen:

augtool> ins 0444 after /files/etc/postgresql/9.4/main/pg_hba.conf/1

Sen jälkeen määritetään tarvittavat kentät:

augtool> set /files/etc/postgresql/9.4/main/pg_hba.conf/0444/type local
 augtool> set /files/etc/postgresql/9.4/main/pg_hba.conf/0444/database tietokanta
 augtool> set /files/etc/postgresql/9.4/main/pg_hba.conf/0444/user matti
 augtool> set /files/etc/postgresql/9.4/main/pg_hba.conf/0444/method password

Tunnistautumisrivin numero (0444) on tarkoituksellisesti hyvin suuri, jotta tiedostoon muilla keinoin lisätyt rivit eivät saa vahingossa samaa tunnistetta. Alussa oleva nolla varmistaa lisäksi sen, että vaikka tunnistautumisrivejä luotaisiin muualta peräti 444 kappaletta, eivät rivien 0444 ja 444 indeksit mene silti päällekkäin. Tämä johtuu siitä, että Augeas ei ikinä lisää nollaa objektin tunnisteen alkuun - tai näin ainakin viisaammat ovat kertoneet. PostgreSQL:n tapauksessa rivimäärät ovat pieniä, joten päällekkäisyyksien riski on muutoinkin minimaalinen.

Tehdyt muutokset täytyy lopuksi tallentaa:

augtool> save

Kun konfiguraatiotiedoston muokkaus augtoolilla luonnistuu, voidaan rivit muuntaa Puppetin Augeas-resurssiksi:

augeas { 'sovellus-pg_hba.conf':
 context => "/files${::postgresql::params::pg_hba_conf}",
 changes => [
 "ins 0444 after 1",
 "set 0444/type local",
 "set 0444/database tietokanta",
 "set 0444/user matti",
 "set 0444/method password"
 ],
 lens => 'Pg_hba.lns',
 incl => "${::postgresql::params::pg_hba_conf}",
 # Varmista, ettei samaa riviä luoda uudelleen
 onlyif => "match *[user = 'matti'] size == 0",
 notify => Class['postgresql::service'],
 }

Parametri "context" lisätään Augeas-polun alkuun, eli yllä ennen 0444:sta. Näin säästytään toistamasta koko polkua "changes"-parametrissa, kuten augtoolia käytettäessä on tarpeen.

Linkkejä:

menucross-circle