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:
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ä: