Puppet Augeas

Version 9 (Andrei Pozolotin, 07/28/2010 03:33 pm)

1 1
# Using Puppet with Augeas
2 1
3 1
[Augeas](http://augeas.net/index.html) is a lovely tool that treats
4 1
config files (well, anything really, but it's mostly about config
5 1
files) as trees of values. You then modify the tree as you like,
6 1
and write the file back.
7 1
8 1
This is basically the solution to the problem of dealing with
9 1
upstream configuration changes combined with local modifications:
10 1
you can allow the upstream changes through and then apply changes
11 1
with Augeas to the new version.
12 1
13 1
Assuming you want to work with Augeas, this is a description of how
14 1
to perform Augeas changes using Puppet. You'll need Puppet >=
15 4 Sami Haahtinen
0.24.7 for this. The basic usage is [Type Reference](http://docs.reductivelabs.com/references/latest/type.html#augeas).
16 1
17 1
The somewhat more important, and unfortunately complicated, part is
18 1
figuring out what the tree for a file looks like so you can
19 1
manipulate it properly. The definition that Augeas uses to turn a
20 1
file into a tree is called a lens, and understanding the trees is
21 1
more difficult than it should be, because many lenses are not
22 1
documented sufficiently, or at all. The documentation for those
23 1
that are
24 1
[has its own surprisingly hard to find page](http://augeas.net/docs/references/lenses/index.html)
25 1
on the Augeas site. You can see what lenses are available by
26 1
looking in /usr/share/augeas/lenses/ (or
27 1
/usr/local/share/augeas/lenses/ , or possibly somewhere else,
28 1
depending on your setup).
29 1
30 1
You can also see which files Augeas has successfully parsed by
31 1
entering "ls /files/" in augtool and drilling down from there. If a
32 1
file hasn't been properly parsed by Augeas, it simply won't show
33 1
up. This could mean that the file has a syntax error, or it could
34 1
imply a failure in the lense itself.
35 1
36 1
Here's an example of how to determine the tree structure of a file,
37 1
in this case /etc/exports.
38 1
39 1
The easiest thing to do is to set up the file with some examples (I
40 1
pulled examples from the bottom of "man 5 exports") and see what
41 1
they look like:
42 1
43 1
    $ augtool
44 1
    augtool> ls /files/etc/exports/
45 1
    comment[1] = /etc/exports: the access control list for filesystems which may be exported
46 1
    comment[2] = to NFS clients.  See exports(5).
47 1
    comment[3] = sample /etc/exports file
48 1
    dir[1]/ = /
49 1
    dir[2]/ = /projects
50 1
    dir[3]/ = /usr
51 1
    dir[4]/ = /home/joe
52 1
53 1
From here you can investigate the structure, like so:
54 1
55 1
    augtool> ls /files/etc/exports/dir[1]
56 1
    client[1]/ = master
57 1
    client[2]/ = trusty
58 1
59 1
The corresponding line in the file is:
60 1
61 1
    /               master(rw) trusty(rw,no_root_squash)
62 1
63 1
Digging further:
64 1
65 1
    augtool> ls /files/etc/exports/dir[1]/client[1]
66 1
    option = rw
67 1
68 1
So, if you want to add a new entry, you'd do something like this:
69 1
70 1
    augtool> set /files/etc/exports/dir[last()+1] /foo
71 1
    augtool> set /files/etc/exports/dir[last()]/client weeble
72 1
    augtool> set /files/etc/exports/dir[last()]/client/option[1] ro
73 1
    augtool> set /files/etc/exports/dir[last()]/client/option[2] all_squash
74 1
    augtool> save
75 1
    Saved 1 file(s)
76 1
77 1
Which creates the line:
78 1
79 1
    /foo weeble(ro,all_squash)
80 1
81 1
Now that you've played around in augtool, you can make changes
82 1
using Puppet:
83 1
84 1
    augeas{ "export foo" :
85 1
        context => "/files/etc/exports",
86 1
        changes => [
87 1
            "set dir[last()+1] /foo",
88 1
            "set dir[last()]/client weeble",
89 1
            "set dir[last()]/client/option[1] ro",
90 1
            "set dir[last()]/client/option[2] all_squash",
91 1
        ],
92 1
    }
93 1
94 1
This adds the line described above.
95 5 Patrick Mohr
96 5 Patrick Mohr
97 5 Patrick Mohr
**For information on quotes and spaces, see** http://groups.google.com/group/puppet-users/browse_thread/thread/b4730f74589433e5
98 6 Andrei Pozolotin
99 6 Andrei Pozolotin
100 6 Andrei Pozolotin
# Working Examples
101 6 Andrei Pozolotin
102 9 Andrei Pozolotin
test it like this:
103 9 Andrei Pozolotin
104 9 Andrei Pozolotin
    sudo puppet apply --verbose --debug --trace --summarize test.pp
105 9 Andrei Pozolotin
106 7 Andrei Pozolotin
## /etc/sysctl.conf
107 6 Andrei Pozolotin
108 7 Andrei Pozolotin
works on puppet 2.6.1; fedora 12; ubuntu 10.04
109 6 Andrei Pozolotin
    
110 6 Andrei Pozolotin
    class sysctl {
111 6 Andrei Pozolotin
    
112 6 Andrei Pozolotin
      # nested class/define
113 6 Andrei Pozolotin
      define conf ( $value ) {
114 6 Andrei Pozolotin
        
115 6 Andrei Pozolotin
        # $name is provided by define invocation
116 6 Andrei Pozolotin
        
117 6 Andrei Pozolotin
        # guid of this entry
118 6 Andrei Pozolotin
        $key = $name
119 6 Andrei Pozolotin
        
120 6 Andrei Pozolotin
        $context = "/files/etc/sysctl.conf"
121 6 Andrei Pozolotin
        
122 6 Andrei Pozolotin
         augeas { "sysctl_conf/$key":
123 6 Andrei Pozolotin
           context => "$context",
124 6 Andrei Pozolotin
           onlyif  => "get $key != $value",
125 6 Andrei Pozolotin
           changes => "set $key $value",
126 6 Andrei Pozolotin
           notify  => Exec["sysctl"],
127 6 Andrei Pozolotin
         }
128 6 Andrei Pozolotin
      
129 6 Andrei Pozolotin
      } 
130 6 Andrei Pozolotin
    
131 6 Andrei Pozolotin
       file { "sysctl_conf":
132 6 Andrei Pozolotin
          name => $operatingsystem ? {
133 6 Andrei Pozolotin
            default => "/etc/sysctl.conf",
134 6 Andrei Pozolotin
          },
135 6 Andrei Pozolotin
       }
136 6 Andrei Pozolotin
    
137 6 Andrei Pozolotin
       exec { "sysctl -p":
138 6 Andrei Pozolotin
          alias => "sysctl",
139 6 Andrei Pozolotin
          refreshonly => true,
140 6 Andrei Pozolotin
          subscribe => File["sysctl_conf"],
141 6 Andrei Pozolotin
       }
142 6 Andrei Pozolotin
    
143 6 Andrei Pozolotin
    }
144 6 Andrei Pozolotin
145 8 Andrei Pozolotin
use case:
146 7 Andrei Pozolotin
147 8 Andrei Pozolotin
    include sysctl
148 8 Andrei Pozolotin
149 8 Andrei Pozolotin
    sysctl::conf { 
150 8 Andrei Pozolotin
      
151 8 Andrei Pozolotin
      # prevent java heap swap
152 8 Andrei Pozolotin
      "vm.swappiness": value =>  0;
153 8 Andrei Pozolotin
      
154 8 Andrei Pozolotin
      # increase max read/write buffer size that can be applied via setsockopt()
155 8 Andrei Pozolotin
      "net.core.rmem_max": value =>  16777216;
156 8 Andrei Pozolotin
      "net.core.wmem_max": value =>  16777216;
157 8 Andrei Pozolotin
      
158 8 Andrei Pozolotin
    }
159 8 Andrei Pozolotin
160 8 Andrei Pozolotin
161 7 Andrei Pozolotin
## /etc/security/limits.conf
162 7 Andrei Pozolotin
163 7 Andrei Pozolotin
works on puppet 2.6.1; fedora 12; ubuntu 10.04
164 7 Andrei Pozolotin
    
165 7 Andrei Pozolotin
    class limits {
166 7 Andrei Pozolotin
    
167 7 Andrei Pozolotin
      # nested class/define
168 7 Andrei Pozolotin
      define conf (
169 7 Andrei Pozolotin
        $domain = "root",
170 7 Andrei Pozolotin
        $type = "soft",
171 7 Andrei Pozolotin
        $item = "nofile",
172 7 Andrei Pozolotin
        $value = "10000"
173 7 Andrei Pozolotin
        ) {
174 7 Andrei Pozolotin
        
175 7 Andrei Pozolotin
          # guid of this entry
176 7 Andrei Pozolotin
          $key = "$domain/$type/$item"
177 7 Andrei Pozolotin
          
178 7 Andrei Pozolotin
          # augtool> match /files/etc/security/limits.conf/domain[.="root"][./type="hard" and ./item="nofile" and ./value="10000"]
179 7 Andrei Pozolotin
    
180 7 Andrei Pozolotin
          $context = "/files/etc/security/limits.conf"
181 7 Andrei Pozolotin
     
182 7 Andrei Pozolotin
          $path_list  = "domain[.=\"$domain\"][./type=\"$type\" and ./item=\"$item\"]"
183 7 Andrei Pozolotin
          $path_exact = "domain[.=\"$domain\"][./type=\"$type\" and ./item=\"$item\" and ./value=\"$value\"]"
184 7 Andrei Pozolotin
        
185 7 Andrei Pozolotin
          # TODO add duplicate entry cleanup
186 7 Andrei Pozolotin
          
187 7 Andrei Pozolotin
          augeas { "limits_conf/$key":
188 7 Andrei Pozolotin
             context => "$context",
189 7 Andrei Pozolotin
             onlyif  => "match $path_exact size==0",
190 7 Andrei Pozolotin
             changes => [
191 7 Andrei Pozolotin
               # remove all matching to the $domain, $type, $item, for any $value
192 7 Andrei Pozolotin
               "rm $path_list", 
193 7 Andrei Pozolotin
               # insert new node at the end of tree
194 7 Andrei Pozolotin
               "set domain[last()+1] $domain",
195 7 Andrei Pozolotin
               # assign values to the new node
196 7 Andrei Pozolotin
               "set domain[last()]/type $type",
197 7 Andrei Pozolotin
               "set domain[last()]/item $item",
198 7 Andrei Pozolotin
               "set domain[last()]/value $value",
199 7 Andrei Pozolotin
             ],
200 7 Andrei Pozolotin
           }
201 7 Andrei Pozolotin
      
202 7 Andrei Pozolotin
      } 
203 7 Andrei Pozolotin
    
204 7 Andrei Pozolotin
    }
205 8 Andrei Pozolotin
206 8 Andrei Pozolotin
use case:
207 8 Andrei Pozolotin
208 8 Andrei Pozolotin
    include limits
209 8 Andrei Pozolotin
210 8 Andrei Pozolotin
    limits::conf { 
211 8 Andrei Pozolotin
      
212 8 Andrei Pozolotin
      # maximum number of open files/sockets for root
213 8 Andrei Pozolotin
      "root-soft": domain => root, type => soft, item => nofile, value =>  9999;
214 8 Andrei Pozolotin
      "root-hard": domain => root, type => hard, item => nofile, value =>  9999;
215 8 Andrei Pozolotin
      
216 8 Andrei Pozolotin
    }
217 8 Andrei Pozolotin
218 8 Andrei Pozolotin
219 6 Andrei Pozolotin