puppet 0.9.2

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of puppet might be problematic. Click here for more details.

Files changed (182) hide show
  1. data/CHANGELOG +0 -0
  2. data/COPYING +340 -0
  3. data/LICENSE +17 -0
  4. data/README +24 -0
  5. data/Rakefile +294 -0
  6. data/TODO +4 -0
  7. data/bin/cf2puppet +186 -0
  8. data/bin/puppet +176 -0
  9. data/bin/puppetca +213 -0
  10. data/bin/puppetd +246 -0
  11. data/bin/puppetdoc +184 -0
  12. data/bin/puppetmasterd +258 -0
  13. data/examples/code/allatonce +13 -0
  14. data/examples/code/assignments +11 -0
  15. data/examples/code/classing +35 -0
  16. data/examples/code/components +73 -0
  17. data/examples/code/execs +16 -0
  18. data/examples/code/failers/badclassnoparam +10 -0
  19. data/examples/code/failers/badclassparam +10 -0
  20. data/examples/code/failers/badcompnoparam +9 -0
  21. data/examples/code/failers/badcompparam +9 -0
  22. data/examples/code/failers/badtypeparam +3 -0
  23. data/examples/code/file.bl +11 -0
  24. data/examples/code/filedefaults +10 -0
  25. data/examples/code/fileparsing +116 -0
  26. data/examples/code/filerecursion +15 -0
  27. data/examples/code/functions +3 -0
  28. data/examples/code/groups +7 -0
  29. data/examples/code/head +30 -0
  30. data/examples/code/importing +8 -0
  31. data/examples/code/nodes +20 -0
  32. data/examples/code/one +8 -0
  33. data/examples/code/relationships +34 -0
  34. data/examples/code/selectors +28 -0
  35. data/examples/code/simpletests +11 -0
  36. data/examples/code/snippets/argumentdefaults +14 -0
  37. data/examples/code/snippets/casestatement +39 -0
  38. data/examples/code/snippets/classheirarchy.pp +15 -0
  39. data/examples/code/snippets/classincludes.pp +17 -0
  40. data/examples/code/snippets/classpathtest +11 -0
  41. data/examples/code/snippets/dirchmod +19 -0
  42. data/examples/code/snippets/failmissingexecpath.pp +13 -0
  43. data/examples/code/snippets/falsevalues.pp +3 -0
  44. data/examples/code/snippets/filecreate +11 -0
  45. data/examples/code/snippets/implicititeration +15 -0
  46. data/examples/code/snippets/multipleinstances +7 -0
  47. data/examples/code/snippets/namevartest +9 -0
  48. data/examples/code/snippets/scopetest +13 -0
  49. data/examples/code/snippets/selectorvalues.pp +22 -0
  50. data/examples/code/snippets/simpledefaults +5 -0
  51. data/examples/code/snippets/simpleselector +38 -0
  52. data/examples/code/svncommit +13 -0
  53. data/examples/root/bin/sleeper +69 -0
  54. data/examples/root/etc/configfile +0 -0
  55. data/examples/root/etc/debian-passwd +29 -0
  56. data/examples/root/etc/debian-syslog.conf +71 -0
  57. data/examples/root/etc/init.d/sleeper +65 -0
  58. data/examples/root/etc/otherfile +0 -0
  59. data/examples/root/etc/puppet/fileserver.conf +3 -0
  60. data/examples/root/etc/puppet/puppetmasterd.conf +10 -0
  61. data/ext/module:puppet +195 -0
  62. data/install.rb +270 -0
  63. data/lib/puppet.rb +249 -0
  64. data/lib/puppet/base64.rb +19 -0
  65. data/lib/puppet/client.rb +519 -0
  66. data/lib/puppet/config.rb +49 -0
  67. data/lib/puppet/daemon.rb +208 -0
  68. data/lib/puppet/element.rb +71 -0
  69. data/lib/puppet/event.rb +259 -0
  70. data/lib/puppet/log.rb +321 -0
  71. data/lib/puppet/metric.rb +250 -0
  72. data/lib/puppet/parsedfile.rb +38 -0
  73. data/lib/puppet/parser/ast.rb +1560 -0
  74. data/lib/puppet/parser/interpreter.rb +150 -0
  75. data/lib/puppet/parser/lexer.rb +226 -0
  76. data/lib/puppet/parser/parser.rb +1354 -0
  77. data/lib/puppet/parser/scope.rb +755 -0
  78. data/lib/puppet/server.rb +170 -0
  79. data/lib/puppet/server/authstore.rb +227 -0
  80. data/lib/puppet/server/ca.rb +140 -0
  81. data/lib/puppet/server/filebucket.rb +147 -0
  82. data/lib/puppet/server/fileserver.rb +477 -0
  83. data/lib/puppet/server/logger.rb +43 -0
  84. data/lib/puppet/server/master.rb +103 -0
  85. data/lib/puppet/server/servlet.rb +247 -0
  86. data/lib/puppet/sslcertificates.rb +737 -0
  87. data/lib/puppet/statechange.rb +150 -0
  88. data/lib/puppet/storage.rb +95 -0
  89. data/lib/puppet/transaction.rb +179 -0
  90. data/lib/puppet/transportable.rb +151 -0
  91. data/lib/puppet/type.rb +1354 -0
  92. data/lib/puppet/type/component.rb +141 -0
  93. data/lib/puppet/type/cron.rb +543 -0
  94. data/lib/puppet/type/exec.rb +316 -0
  95. data/lib/puppet/type/group.rb +152 -0
  96. data/lib/puppet/type/nameservice.rb +3 -0
  97. data/lib/puppet/type/nameservice/netinfo.rb +173 -0
  98. data/lib/puppet/type/nameservice/objectadd.rb +146 -0
  99. data/lib/puppet/type/nameservice/posix.rb +200 -0
  100. data/lib/puppet/type/package.rb +420 -0
  101. data/lib/puppet/type/package/apt.rb +70 -0
  102. data/lib/puppet/type/package/dpkg.rb +108 -0
  103. data/lib/puppet/type/package/rpm.rb +81 -0
  104. data/lib/puppet/type/package/sun.rb +117 -0
  105. data/lib/puppet/type/package/yum.rb +58 -0
  106. data/lib/puppet/type/pfile.rb +569 -0
  107. data/lib/puppet/type/pfile/checksum.rb +219 -0
  108. data/lib/puppet/type/pfile/create.rb +108 -0
  109. data/lib/puppet/type/pfile/group.rb +129 -0
  110. data/lib/puppet/type/pfile/mode.rb +131 -0
  111. data/lib/puppet/type/pfile/source.rb +264 -0
  112. data/lib/puppet/type/pfile/type.rb +31 -0
  113. data/lib/puppet/type/pfile/uid.rb +166 -0
  114. data/lib/puppet/type/pfilebucket.rb +80 -0
  115. data/lib/puppet/type/pprocess.rb +97 -0
  116. data/lib/puppet/type/service.rb +347 -0
  117. data/lib/puppet/type/service/base.rb +17 -0
  118. data/lib/puppet/type/service/debian.rb +50 -0
  119. data/lib/puppet/type/service/init.rb +145 -0
  120. data/lib/puppet/type/service/smf.rb +29 -0
  121. data/lib/puppet/type/state.rb +182 -0
  122. data/lib/puppet/type/symlink.rb +183 -0
  123. data/lib/puppet/type/tidy.rb +183 -0
  124. data/lib/puppet/type/typegen.rb +149 -0
  125. data/lib/puppet/type/typegen/filerecord.rb +243 -0
  126. data/lib/puppet/type/typegen/filetype.rb +316 -0
  127. data/lib/puppet/type/user.rb +290 -0
  128. data/lib/puppet/util.rb +138 -0
  129. data/test/certmgr/certmgr.rb +265 -0
  130. data/test/client/client.rb +203 -0
  131. data/test/executables/puppetbin.rb +53 -0
  132. data/test/executables/puppetca.rb +79 -0
  133. data/test/executables/puppetd.rb +71 -0
  134. data/test/executables/puppetmasterd.rb +153 -0
  135. data/test/executables/puppetmodule.rb +60 -0
  136. data/test/language/ast.rb +412 -0
  137. data/test/language/interpreter.rb +71 -0
  138. data/test/language/scope.rb +412 -0
  139. data/test/language/snippets.rb +445 -0
  140. data/test/other/events.rb +111 -0
  141. data/test/other/log.rb +195 -0
  142. data/test/other/metrics.rb +92 -0
  143. data/test/other/overrides.rb +115 -0
  144. data/test/other/parsedfile.rb +31 -0
  145. data/test/other/relationships.rb +113 -0
  146. data/test/other/state.rb +106 -0
  147. data/test/other/storage.rb +39 -0
  148. data/test/other/transactions.rb +235 -0
  149. data/test/parser/lexer.rb +120 -0
  150. data/test/parser/parser.rb +180 -0
  151. data/test/puppet/conffiles.rb +104 -0
  152. data/test/puppet/defaults.rb +100 -0
  153. data/test/puppet/error.rb +23 -0
  154. data/test/puppet/utiltest.rb +120 -0
  155. data/test/puppettest.rb +774 -0
  156. data/test/server/authstore.rb +209 -0
  157. data/test/server/bucket.rb +227 -0
  158. data/test/server/ca.rb +201 -0
  159. data/test/server/fileserver.rb +710 -0
  160. data/test/server/logger.rb +175 -0
  161. data/test/server/master.rb +150 -0
  162. data/test/server/server.rb +130 -0
  163. data/test/tagging/tagging.rb +80 -0
  164. data/test/test +51 -0
  165. data/test/types/basic.rb +119 -0
  166. data/test/types/component.rb +272 -0
  167. data/test/types/cron.rb +261 -0
  168. data/test/types/exec.rb +273 -0
  169. data/test/types/file.rb +616 -0
  170. data/test/types/filebucket.rb +167 -0
  171. data/test/types/fileignoresource.rb +287 -0
  172. data/test/types/filesources.rb +587 -0
  173. data/test/types/filetype.rb +162 -0
  174. data/test/types/group.rb +271 -0
  175. data/test/types/package.rb +205 -0
  176. data/test/types/query.rb +101 -0
  177. data/test/types/service.rb +100 -0
  178. data/test/types/symlink.rb +93 -0
  179. data/test/types/tidy.rb +124 -0
  180. data/test/types/type.rb +135 -0
  181. data/test/types/user.rb +371 -0
  182. metadata +243 -0
@@ -0,0 +1,38 @@
1
+ # A simple class that tells us when a file has changed and thus whether we
2
+ # should reload it
3
+
4
+ require 'puppet'
5
+
6
+ module Puppet
7
+ class ParsedFile
8
+ attr_reader :file
9
+
10
+ # Determine whether the file has changed and thus whether it should
11
+ # be reparsed
12
+ def changed?
13
+ tmp = self.stamp
14
+ retval = false
15
+ if tmp != @stamp
16
+ retval = true
17
+ @stamp = tmp
18
+ end
19
+ @statted = Time.now
20
+
21
+ return retval
22
+ end
23
+
24
+ # Create the file. Must be passed the file path.
25
+ def initialize(file)
26
+ @file = file
27
+ unless FileTest.exists?(@file)
28
+ raise Puppet::DevError, "Can not use a non-existent file for parsing"
29
+ end
30
+ @stamp = self.stamp
31
+ @statted = Time.now
32
+ end
33
+
34
+ def stamp
35
+ File.stat(@file).ctime
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,1560 @@
1
+ # the parent class for all of our syntactical objects
2
+
3
+ require 'puppet'
4
+
5
+ module Puppet
6
+ module Parser
7
+
8
+ # The base class for all of the objects that make up the parse trees.
9
+ # Handles things like file name, line #, and also does the initialization
10
+ # for all of the parameters of all of the child objects.
11
+ class AST
12
+ Puppet.setdefault(:typecheck, true)
13
+ Puppet.setdefault(:paramcheck, true)
14
+ attr_accessor :line, :file, :parent
15
+
16
+ # Just used for 'tree', which is only used in debugging.
17
+ @@pink = ""
18
+ @@green = ""
19
+ @@yellow = ""
20
+ @@slate = ""
21
+ @@reset = ""
22
+
23
+ # Just used for 'tree', which is only used in debugging.
24
+ @@indent = " " * 4
25
+ @@indline = @@pink + ("-" * 4) + @@reset
26
+ @@midline = @@slate + ("-" * 4) + @@reset
27
+
28
+ @@settypes = {}
29
+
30
+ # Just used for 'tree', which is only used in debugging.
31
+ def AST.indention
32
+ return @@indent * @@indention
33
+ end
34
+
35
+ # Just used for 'tree', which is only used in debugging.
36
+ def AST.midline
37
+ return @@midline
38
+ end
39
+
40
+ # Evaluate the current object. Basically just iterates across all
41
+ # of the contained children and evaluates them in turn, returning a
42
+ # list of all of the collected values, rejecting nil values
43
+ def evaluate(scope)
44
+ #Puppet.debug("Evaluating ast %s" % @name)
45
+ value = self.collect { |obj|
46
+ obj.safeevaluate(scope)
47
+ }.reject { |obj|
48
+ obj.nil?
49
+ }
50
+ end
51
+
52
+ # The version of the evaluate method that should be called, because it
53
+ # correctly handles errors. It is critical to use this method because
54
+ # it can enable you to catch the error where it happens, rather than
55
+ # much higher up the stack.
56
+ def safeevaluate(*args)
57
+ begin
58
+ self.evaluate(*args)
59
+ rescue Puppet::DevError
60
+ raise
61
+ rescue Puppet::ParseError
62
+ raise
63
+ rescue => detail
64
+ if Puppet[:debug]
65
+ puts caller
66
+ end
67
+ error = Puppet::DevError.new(
68
+ "Child of type %s failed with error %s: %s" %
69
+ [self.class, detail.class, detail.to_s]
70
+ )
71
+ error.stack = caller
72
+ raise error
73
+ end
74
+ end
75
+
76
+ # Again, just used for printing out the parse tree.
77
+ def typewrap(string)
78
+ #return self.class.to_s.sub(/.+::/,'') +
79
+ #"(" + @@green + string.to_s + @@reset + ")"
80
+ return @@green + string.to_s + @@reset +
81
+ "(" + self.class.to_s.sub(/.+::/,'') + ")"
82
+ end
83
+
84
+ # Initialize the object. Requires a hash as the argument, and takes
85
+ # each of the parameters of the hash and calls the settor method for
86
+ # them. This is probably pretty inefficient and should likely be changed
87
+ # at some point.
88
+ def initialize(args)
89
+ @file = nil
90
+ @line = nil
91
+ args.each { |param,value|
92
+ method = param.to_s + "="
93
+ unless self.respond_to?(method)
94
+ error = Puppet::DevError.new(
95
+ "Invalid parameter %s to object class %s" %
96
+ [param,self.class.to_s]
97
+ )
98
+ error.line = self.line
99
+ error.file = self.file
100
+ error.stack = caller
101
+ raise error
102
+ end
103
+
104
+ begin
105
+ #Puppet.debug("sending %s to %s" % [method, self.class])
106
+ self.send(method,value)
107
+ rescue => detail
108
+ error = Puppet::DevError.new(
109
+ "Could not set parameter %s on class %s: %s" %
110
+ [method,self.class.to_s,detail]
111
+ )
112
+ error.stack = caller
113
+ raise error
114
+ end
115
+ }
116
+ end
117
+
118
+ # The parent class of all AST objects that contain other AST objects.
119
+ # Everything but the really simple objects descend from this. It is
120
+ # important to note that Branch objects contain other AST objects only --
121
+ # if you want to contain values, use a descendent of the AST::Leaf class.
122
+ class Branch < AST
123
+ include Enumerable
124
+ attr_accessor :pin, :children
125
+
126
+ # Yield each contained AST node in turn. Used mostly by 'evaluate'.
127
+ # This definition means that I don't have to override 'evaluate'
128
+ # every time, but each child of Branch will likely need to override
129
+ # this method.
130
+ def each
131
+ @children.each { |child|
132
+ yield child
133
+ }
134
+ end
135
+
136
+ # Initialize our object. Largely relies on the method from the base
137
+ # class, but also does some verification.
138
+ def initialize(arghash)
139
+ super(arghash)
140
+
141
+ # Create the hash, if it was not set at initialization time.
142
+ unless defined? @children
143
+ @children = []
144
+ end
145
+
146
+ # Verify that we only got valid AST nodes.
147
+ @children.each { |child|
148
+ unless child.is_a?(AST)
149
+ raise Puppet::DevError,
150
+ "child %s is not an ast" % child
151
+ end
152
+ }
153
+ end
154
+
155
+ # Pretty-print the parse tree.
156
+ def tree(indent = 0)
157
+ return ((@@indline * indent) +
158
+ self.typewrap(self.pin)) + "\n" + self.collect { |child|
159
+ child.tree(indent + 1)
160
+ }.join("\n")
161
+ end
162
+ end
163
+
164
+ # The basic container class. This object behaves almost identically
165
+ # to a normal array except at initialization time. Note that its name
166
+ # is 'AST::ASTArray', rather than plain 'AST::Array'; I had too many
167
+ # bugs when it was just 'AST::Array', because things like
168
+ # 'object.is_a?(Array)' never behaved as I expected.
169
+ class ASTArray < AST::Branch
170
+ include Enumerable
171
+
172
+ # Return a child by index. Probably never used.
173
+ def [](index)
174
+ @children[index]
175
+ end
176
+
177
+ # Evaluate our children.
178
+ def evaluate(scope)
179
+ rets = nil
180
+ # We basically always operate declaratively, and when we
181
+ # do we need to evaluate the settor-like statements first. This
182
+ # is basically variable and type-default declarations.
183
+ if scope.declarative?
184
+ test = [
185
+ AST::VarDef, AST::TypeDefaults
186
+ ]
187
+
188
+ settors = []
189
+ others = []
190
+ @children.each { |child|
191
+ if test.include?(child.class)
192
+ settors.push child
193
+ else
194
+ others.push child
195
+ end
196
+ }
197
+ rets = [settors,others].flatten.collect { |child|
198
+ child.safeevaluate(scope)
199
+ }
200
+ else
201
+ # If we're not declarative, just do everything in order.
202
+ rets = @children.collect { |item|
203
+ item.safeevaluate(scope)
204
+ }
205
+ end
206
+ rets = rets.reject { |obj| obj.nil? }
207
+ end
208
+
209
+ def push(*ary)
210
+ ary.each { |child|
211
+ #Puppet.debug "adding %s(%s) of type %s to %s" %
212
+ # [child, child.object_id, child.class.to_s.sub(/.+::/,''),
213
+ # self.object_id]
214
+ @children.push(child)
215
+ }
216
+
217
+ return self
218
+ end
219
+
220
+ # Convert to a string. Only used for printing the parse tree.
221
+ def to_s
222
+ return "[" + @children.collect { |child|
223
+ child.to_s
224
+ }.join(", ") + "]"
225
+ end
226
+
227
+ # Print the parse tree.
228
+ def tree(indent = 0)
229
+ #puts((AST.indent * indent) + self.pin)
230
+ self.collect { |child|
231
+ child.tree(indent)
232
+ }.join("\n" + (AST.midline * (indent+1)) + "\n")
233
+ end
234
+ end
235
+
236
+ # A simple container class, containing the parameters for an object.
237
+ # Used for abstracting the grammar declarations. Basically unnecessary
238
+ # except that I kept finding bugs because I had too many arrays that
239
+ # meant completely different things.
240
+ class ObjectInst < ASTArray; end
241
+
242
+ # Another simple container class to make sure we can correctly arrayfy
243
+ # things.
244
+ class CompArgument < ASTArray; end
245
+
246
+ # The base class for all of the leaves of the parse trees. These
247
+ # basically just have types and values. Both of these parameters
248
+ # are simple values, not AST objects.
249
+ class Leaf < AST
250
+ attr_accessor :value, :type
251
+
252
+ # Return our value.
253
+ def evaluate(scope)
254
+ return @value
255
+ end
256
+
257
+ # Print the value in parse tree context.
258
+ def tree(indent = 0)
259
+ return ((@@indent * indent) + self.typewrap(self.value))
260
+ end
261
+
262
+ def to_s
263
+ return @value
264
+ end
265
+ end
266
+
267
+ # The boolean class. True or false. Converts the string it receives
268
+ # to a Ruby boolean.
269
+ class Boolean < AST::Leaf
270
+
271
+ # Use the parent method, but then convert to a real boolean.
272
+ def initialize(hash)
273
+ super
274
+
275
+ unless @value == 'true' or @value == 'false'
276
+ error = Puppet::DevError.new(
277
+ "'%s' is not a boolean" % @value
278
+ )
279
+ error.stack = caller
280
+ raise error
281
+ end
282
+ if @value == 'true'
283
+ @value = true
284
+ else
285
+ @value = false
286
+ end
287
+ end
288
+ end
289
+
290
+ # The base string class.
291
+ class String < AST::Leaf
292
+ # Interpolate the string looking for variables, and then return
293
+ # the result.
294
+ def evaluate(scope)
295
+ return scope.strinterp(@value)
296
+ end
297
+ end
298
+ #---------------------------------------------------------------
299
+
300
+ # The 'default' option on case statements and selectors.
301
+ class Default < AST::Leaf; end
302
+
303
+ # Capitalized words; used mostly for type-defaults, but also
304
+ # get returned by the lexer any other time an unquoted capitalized
305
+ # word is found.
306
+ class Type < AST::Leaf; end
307
+
308
+ # Lower-case words.
309
+ class Name < AST::Leaf; end
310
+
311
+ # A simple variable. This object is only used during interpolation;
312
+ # the VarDef class is used for assignment.
313
+ class Variable < Name
314
+ # Looks up the value of the object in the scope tree (does
315
+ # not include syntactical constructs, like '$' and '{}').
316
+ def evaluate(scope)
317
+ begin
318
+ return scope.lookupvar(@value)
319
+ rescue Puppet::ParseError => except
320
+ except.line = self.line
321
+ except.file = self.file
322
+ raise except
323
+ rescue => detail
324
+ error = Puppet::DevError.new(detail)
325
+ error.line = self.line
326
+ error.file = self.file
327
+ error.stack = caller
328
+ raise error
329
+ end
330
+ end
331
+ end
332
+
333
+ # Any normal puppet object declaration. Can result in a class or a
334
+ # component, in addition to builtin types.
335
+ class ObjectDef < AST::Branch
336
+ attr_accessor :name, :type
337
+ attr_reader :params
338
+
339
+ # probably not used at all
340
+ def []=(index,obj)
341
+ @params[index] = obj
342
+ end
343
+
344
+ # probably not used at all
345
+ def [](index)
346
+ return @params[index]
347
+ end
348
+
349
+ # Auto-generate a name
350
+ def autoname(type, object)
351
+ case object
352
+ when Puppet::Type:
353
+ raise Puppet::Error,
354
+ "Built-in types must be provided with a name"
355
+ when HostClass:
356
+ return type
357
+ else
358
+ Puppet.info "Autogenerating name for object of type %s" %
359
+ type
360
+ return [type, "-", self.object_id].join("")
361
+ end
362
+ end
363
+
364
+ # Iterate across all of our children.
365
+ def each
366
+ [@type,@name,@params].flatten.each { |param|
367
+ #Puppet.debug("yielding param %s" % param)
368
+ yield param
369
+ }
370
+ end
371
+
372
+ # Does not actually return an object; instead sets an object
373
+ # in the current scope.
374
+ def evaluate(scope)
375
+ hash = {}
376
+
377
+ # Get our type and name.
378
+ objtype = @type.safeevaluate(scope)
379
+
380
+ # If the type was a variable, we wouldn't have typechecked yet.
381
+ # Do it now, if so.
382
+ unless @checked
383
+ self.typecheck(objtype)
384
+ end
385
+
386
+ # See if our object was defined
387
+ begin
388
+ object = scope.lookuptype(objtype)
389
+ rescue Puppet::ParseError => except
390
+ except.line = self.line
391
+ except.file = self.file
392
+ raise except
393
+ rescue => detail
394
+ error = Puppet::ParseError.new(detail)
395
+ error.line = self.line
396
+ error.file = self.file
397
+ error.stack = caller
398
+ raise error
399
+ end
400
+
401
+ unless object
402
+ # If not, verify that it's a builtin type
403
+ begin
404
+ object = Puppet::Type.type(objtype)
405
+ rescue TypeError
406
+ # otherwise, the user specified an invalid type
407
+ error = Puppet::ParseError.new(
408
+ "Invalid type %s" % objtype
409
+ )
410
+ error.line = @line
411
+ error.file = @file
412
+ raise error
413
+ end
414
+ end
415
+
416
+ # Autogenerate the name if one was not passed.
417
+ if defined? @name
418
+ objnames = @name.safeevaluate(scope)
419
+ else
420
+ objnames = self.autoname(objtype, object)
421
+ end
422
+
423
+ # it's easier to always use an array, even for only one name
424
+ unless objnames.is_a?(Array)
425
+ objnames = [objnames]
426
+ end
427
+
428
+ # Retrieve the defaults for our type
429
+ hash = getdefaults(objtype, scope)
430
+
431
+ # then set all of the specified params
432
+ @params.each { |param|
433
+ ary = param.safeevaluate(scope)
434
+ hash[ary[0]] = ary[1]
435
+ }
436
+
437
+ # this is where our implicit iteration takes place;
438
+ # if someone passed an array as the name, then we act
439
+ # just like the called us many times
440
+ objnames.collect { |objname|
441
+ # If the object is a class, that means it's a builtin type
442
+ if object.is_a?(Class)
443
+ begin
444
+ Puppet.debug(
445
+ ("Setting object '%s' " +
446
+ "in scope %s " +
447
+ "with arguments %s") %
448
+ [objname, scope.object_id, hash.inspect]
449
+ )
450
+ obj = scope.setobject(
451
+ objtype,
452
+ objname,
453
+ hash,
454
+ @file,
455
+ @line
456
+ )
457
+ rescue Puppet::ParseError => except
458
+ except.line = self.line
459
+ except.file = self.file
460
+ raise except
461
+ rescue => detail
462
+ error = Puppet::ParseError.new(detail)
463
+ error.line = self.line
464
+ error.file = self.file
465
+ error.stack = caller
466
+ raise error
467
+ end
468
+ else
469
+ # but things like components create a new type; if we find
470
+ # one of those, evaluate that with our arguments
471
+ Puppet.debug("Calling object '%s' with arguments %s" %
472
+ [object.name, hash.inspect])
473
+ object.safeevaluate(scope,hash,objtype,objname)
474
+ end
475
+ }.reject { |obj| obj.nil? }
476
+ end
477
+
478
+ # Retrieve the defaults for our type
479
+ def getdefaults(objtype, scope)
480
+ # first, retrieve the defaults
481
+ begin
482
+ defaults = scope.lookupdefaults(objtype)
483
+ if defaults.length > 0
484
+ Puppet.debug "Got defaults for %s: %s" %
485
+ [objtype,defaults.inspect]
486
+ end
487
+ rescue => detail
488
+ raise Puppet::DevError,
489
+ "Could not lookup defaults for %s: %s" %
490
+ [objtype, detail.to_s]
491
+ end
492
+
493
+ hash = {}
494
+ # Add any found defaults to our argument list
495
+ defaults.each { |var,value|
496
+ Puppet.debug "Found default %s for %s" %
497
+ [var,objtype]
498
+
499
+ hash[var] = value
500
+ }
501
+
502
+ return hash
503
+ end
504
+
505
+ # Create our ObjectDef. Handles type checking for us.
506
+ def initialize(hash)
507
+ @checked = false
508
+ super
509
+
510
+ if @type.is_a?(Variable)
511
+ Puppet.debug "Delaying typecheck"
512
+ return
513
+ else
514
+ self.typecheck(@type.value)
515
+
516
+ objtype = @type.value
517
+ end
518
+
519
+ end
520
+
521
+ # Verify that all passed parameters are valid
522
+ def paramcheck(builtin, objtype)
523
+ # This defaults to true
524
+ unless Puppet[:paramcheck]
525
+ return
526
+ end
527
+
528
+ @params.each { |param|
529
+ if builtin
530
+ self.parambuiltincheck(builtin, param)
531
+ else
532
+ self.paramdefinedcheck(objtype, param)
533
+ end
534
+ }
535
+ end
536
+
537
+ def parambuiltincheck(type, param)
538
+ unless param.is_a?(AST::ObjectParam)
539
+ raise Puppet::DevError,
540
+ "Got something other than param"
541
+ end
542
+ begin
543
+ pname = param.param.value
544
+ rescue => detail
545
+ raise Puppet::DevError, detail.to_s
546
+ end
547
+ next if pname == "name" # always allow these
548
+ unless type.validarg?(pname)
549
+ error = Puppet::ParseError.new(
550
+ "Invalid parameter '%s' for type '%s'" %
551
+ [pname,type.name]
552
+ )
553
+ error.stack = caller
554
+ error.line = self.line
555
+ error.file = self.file
556
+ raise error
557
+ end
558
+ end
559
+
560
+ def paramdefinedcheck(objtype, param)
561
+ # FIXME we might need to do more here eventually...
562
+ if Puppet::Type.metaparam?(param.param.value.intern)
563
+ next
564
+ end
565
+
566
+ begin
567
+ pname = param.param.value
568
+ rescue => detail
569
+ raise Puppet::DevError, detail.to_s
570
+ end
571
+
572
+ unless @@settypes[objtype].validarg?(pname)
573
+ error = Puppet::ParseError.new(
574
+ "Invalid parameter '%s' for type '%s'" %
575
+ [pname,objtype]
576
+ )
577
+ error.stack = caller
578
+ error.line = self.line
579
+ error.file = self.file
580
+ raise error
581
+ end
582
+ end
583
+
584
+ # Set the parameters for our object.
585
+ def params=(params)
586
+ if params.is_a?(AST::ASTArray)
587
+ @params = params
588
+ else
589
+ @params = AST::ASTArray.new(
590
+ :line => params.line,
591
+ :file => params.file,
592
+ :children => [params]
593
+ )
594
+ end
595
+ end
596
+
597
+ # Print this object out.
598
+ def tree(indent = 0)
599
+ return [
600
+ @type.tree(indent + 1),
601
+ @name.tree(indent + 1),
602
+ ((@@indline * indent) + self.typewrap(self.pin)),
603
+ @params.collect { |param|
604
+ begin
605
+ param.tree(indent + 1)
606
+ rescue NoMethodError => detail
607
+ Puppet.err @params.inspect
608
+ error = Puppet::DevError.new(
609
+ "failed to tree a %s" % self.class
610
+ )
611
+ error.stack = caller
612
+ raise error
613
+ end
614
+ }.join("\n")
615
+ ].join("\n")
616
+ end
617
+
618
+ # Verify that the type is valid. This throws an error if there's
619
+ # a problem, so the return value doesn't matter
620
+ def typecheck(objtype)
621
+ # This will basically always be on, but I wanted to make it at
622
+ # least simple to turn off if it came to that
623
+ unless Puppet[:typecheck]
624
+ return
625
+ end
626
+
627
+ builtin = false
628
+ begin
629
+ builtin = Puppet::Type.type(objtype)
630
+ rescue TypeError
631
+ # nothing; we've already set builtin to false
632
+ end
633
+
634
+ unless builtin or @@settypes.include?(objtype)
635
+ error = Puppet::ParseError.new(
636
+ "Unknown type '%s'" % objtype
637
+ )
638
+ error.line = self.line
639
+ error.file = self.file
640
+ error.stack = caller
641
+ raise error
642
+ end
643
+
644
+ unless builtin
645
+ Puppet.debug "%s is a defined type" % objtype
646
+ end
647
+
648
+ self.paramcheck(builtin, objtype)
649
+
650
+ @checked = true
651
+ end
652
+
653
+ def to_s
654
+ return "%s => { %s }" % [@name,
655
+ @params.collect { |param|
656
+ param.to_s
657
+ }.join("\n")
658
+ ]
659
+ end
660
+ end
661
+
662
+ # A reference to an object. Only valid as an rvalue.
663
+ class ObjectRef < AST::Branch
664
+ attr_accessor :name, :type
665
+
666
+ def each
667
+ [@type,@name].flatten.each { |param|
668
+ #Puppet.debug("yielding param %s" % param)
669
+ yield param
670
+ }
671
+ end
672
+
673
+ # Evaluate our object, but just return a simple array of the type
674
+ # and name.
675
+ def evaluate(scope)
676
+ objtype = @type.safeevaluate(scope)
677
+ objnames = @name.safeevaluate(scope)
678
+
679
+ # it's easier to always use an array, even for only one name
680
+ unless objnames.is_a?(Array)
681
+ objnames = [objnames]
682
+ end
683
+
684
+ # Verify we can find the object.
685
+ begin
686
+ object = scope.lookuptype(objtype)
687
+ rescue Puppet::ParseError => except
688
+ except.line = self.line
689
+ except.file = self.file
690
+ raise except
691
+ rescue => detail
692
+ error = Puppet::ParseError.new(detail)
693
+ error.line = self.line
694
+ error.file = self.file
695
+ error.stack = caller
696
+ raise error
697
+ end
698
+ Puppet.debug "ObjectRef returned type %s" % object
699
+
700
+ # should we implicitly iterate here?
701
+ # yes, i believe that we essentially have to...
702
+ objnames.collect { |objname|
703
+ if object.is_a?(AST::Component)
704
+ objname = "%s[%s]" % [objtype,objname]
705
+ objtype = "component"
706
+ end
707
+ [objtype,objname]
708
+ }.reject { |obj| obj.nil? }
709
+ end
710
+
711
+ def tree(indent = 0)
712
+ return [
713
+ @type.tree(indent + 1),
714
+ @name.tree(indent + 1),
715
+ ((@@indline * indent) + self.typewrap(self.pin))
716
+ ].join("\n")
717
+ end
718
+
719
+ def to_s
720
+ return "%s[%s]" % [@name,@type]
721
+ end
722
+ end
723
+
724
+ # The AST object for the parameters inside ObjectDefs and Selectors.
725
+ class ObjectParam < AST::Branch
726
+ attr_accessor :value, :param
727
+
728
+ def each
729
+ [@param,@value].each { |child| yield child }
730
+ end
731
+
732
+ # Return the parameter and the value.
733
+ def evaluate(scope)
734
+ param = @param.safeevaluate(scope)
735
+ value = @value.safeevaluate(scope)
736
+ return [param, value]
737
+ end
738
+
739
+ def tree(indent = 0)
740
+ return [
741
+ @param.tree(indent + 1),
742
+ ((@@indline * indent) + self.typewrap(self.pin)),
743
+ @value.tree(indent + 1)
744
+ ].join("\n")
745
+ end
746
+
747
+ def to_s
748
+ return "%s => %s" % [@param,@value]
749
+ end
750
+ end
751
+
752
+ # The basic logical structure in Puppet. Supports a list of
753
+ # tests and statement arrays.
754
+ class CaseStatement < AST::Branch
755
+ attr_accessor :test, :options, :default
756
+
757
+ # Short-curcuit evaluation. Return the value of the statements for
758
+ # the first option that matches.
759
+ def evaluate(scope)
760
+ value = @test.safeevaluate(scope)
761
+
762
+ retvalue = nil
763
+ found = false
764
+
765
+ # Iterate across the options looking for a match.
766
+ @options.each { |option|
767
+ if option.eachvalue { |opval| break true if opval == value }
768
+ # we found a matching option
769
+ retvalue = option.safeevaluate(scope)
770
+ found = true
771
+ break
772
+ end
773
+ }
774
+
775
+ # Unless we found something, look for the default.
776
+ unless found
777
+ if defined? @default
778
+ retvalue = @default.safeevaluate(scope)
779
+ else
780
+ Puppet.debug "No true answers and no default"
781
+ end
782
+ end
783
+ return retvalue
784
+ end
785
+
786
+ # Do some input validation on our options.
787
+ def initialize(hash)
788
+ values = {}
789
+
790
+ super
791
+ # this won't work if we move away from only allowing constants
792
+ # here
793
+ # but for now, it's fine and useful
794
+ @options.each { |option|
795
+ if option.default?
796
+ @default = option
797
+ end
798
+ option.eachvalue { |val|
799
+ if values.include?(val)
800
+ raise Puppet::ParseError,
801
+ "Value %s appears twice in case statement" %
802
+ val
803
+ else
804
+ values[val] = true
805
+ end
806
+ }
807
+ }
808
+ end
809
+
810
+ def tree(indent = 0)
811
+ rettree = [
812
+ @test.tree(indent + 1),
813
+ ((@@indline * indent) + self.typewrap(self.pin)),
814
+ @options.tree(indent + 1)
815
+ ]
816
+
817
+ return rettree.flatten.join("\n")
818
+ end
819
+
820
+ def each
821
+ [@test,@options].each { |child| yield child }
822
+ end
823
+ end
824
+
825
+ # Each individual option in a case statement.
826
+ class CaseOpt < AST::Branch
827
+ attr_accessor :value, :statements
828
+
829
+ # CaseOpt is a bit special -- we just want the value first,
830
+ # so that CaseStatement can compare, and then it will selectively
831
+ # decide whether to fully evaluate this option
832
+
833
+ def each
834
+ [@value,@statements].each { |child| yield child }
835
+ end
836
+
837
+ # Are we the default option?
838
+ def default?
839
+ if defined? @default
840
+ return @default
841
+ end
842
+
843
+ if @value.is_a?(AST::ASTArray)
844
+ @value.each { |subval|
845
+ if subval.is_a?(AST::Default)
846
+ @default = true
847
+ break
848
+ end
849
+ }
850
+ else
851
+ if @value.is_a?(AST::Default)
852
+ @default = true
853
+ end
854
+ end
855
+
856
+ unless defined? @default
857
+ @default = false
858
+ end
859
+
860
+ return @default
861
+ end
862
+
863
+ # You can specify a list of values; return each in turn.
864
+ def eachvalue
865
+ if @value.is_a?(AST::ASTArray)
866
+ @value.each { |subval|
867
+ yield subval.value
868
+ }
869
+ else
870
+ yield @value.value
871
+ end
872
+ end
873
+
874
+ # Evaluate the actual statements; this only gets called if
875
+ # our option matched.
876
+ def evaluate(scope)
877
+ return @statements.safeevaluate(scope.newscope)
878
+ end
879
+
880
+ def tree(indent = 0)
881
+ rettree = [
882
+ @value.tree(indent + 1),
883
+ ((@@indline * indent) + self.typewrap(self.pin)),
884
+ @statements.tree(indent + 1)
885
+ ]
886
+ return rettree.flatten.join("\n")
887
+ end
888
+ end
889
+
890
+ # The inline conditional operator. Unlike CaseStatement, which executes
891
+ # code, we just return a value.
892
+ class Selector < AST::Branch
893
+ attr_accessor :param, :values
894
+
895
+ def each
896
+ [@param,@values].each { |child| yield child }
897
+ end
898
+
899
+ # Find the value that corresponds with the test.
900
+ def evaluate(scope)
901
+ retvalue = nil
902
+ found = nil
903
+
904
+ # Get our parameter.
905
+ paramvalue = @param.safeevaluate(scope)
906
+
907
+ default = nil
908
+
909
+ # Then look for a match in the options.
910
+ @values.each { |obj|
911
+ param = obj.param.safeevaluate(scope)
912
+ if param == paramvalue
913
+ # we found a matching option
914
+ retvalue = obj.value.safeevaluate(scope)
915
+ found = true
916
+ break
917
+ elsif obj.param.is_a?(Default)
918
+ default = obj
919
+ end
920
+ }
921
+
922
+ # Unless we found something, look for the default.
923
+ unless found
924
+ if default
925
+ retvalue = default.value.safeevaluate(scope)
926
+ else
927
+ error = Puppet::ParseError.new(
928
+ "No value for selector param '%s'" % paramvalue
929
+ )
930
+ error.line = self.line
931
+ error.file = self.file
932
+ raise error
933
+ end
934
+ end
935
+
936
+ return retvalue
937
+ end
938
+
939
+ def tree(indent = 0)
940
+ return [
941
+ @param.tree(indent + 1),
942
+ ((@@indline * indent) + self.typewrap(self.pin)),
943
+ @values.tree(indent + 1)
944
+ ].join("\n")
945
+ end
946
+ end
947
+
948
+ # Define a variable. Stores the value in the current scope.
949
+ class VarDef < AST::Branch
950
+ attr_accessor :name, :value
951
+
952
+ # Look up our name and value, and store them appropriately. The
953
+ # lexer strips off the syntax stuff like '$'.
954
+ def evaluate(scope)
955
+ name = @name.safeevaluate(scope)
956
+ value = @value.safeevaluate(scope)
957
+
958
+ begin
959
+ scope.setvar(name,value)
960
+ rescue Puppet::ParseError => except
961
+ except.line = self.line
962
+ except.file = self.file
963
+ raise except
964
+ rescue => detail
965
+ error = Puppet::ParseError.new(detail)
966
+ error.line = self.line
967
+ error.file = self.file
968
+ error.stack = caller
969
+ raise error
970
+ end
971
+ end
972
+
973
+ def each
974
+ [@name,@value].each { |child| yield child }
975
+ end
976
+
977
+ def tree(indent = 0)
978
+ return [
979
+ @name.tree(indent + 1),
980
+ ((@@indline * 4 * indent) + self.typewrap(self.pin)),
981
+ @value.tree(indent + 1)
982
+ ].join("\n")
983
+ end
984
+
985
+ def to_s
986
+ return "%s => %s" % [@name,@value]
987
+ end
988
+ end
989
+
990
+ # A statement syntactically similar to an ObjectDef, but uses a
991
+ # capitalized object type and cannot have a name.
992
+ class TypeDefaults < AST::Branch
993
+ attr_accessor :type, :params
994
+
995
+ def each
996
+ [@type,@params].each { |child| yield child }
997
+ end
998
+
999
+ # As opposed to ObjectDef, this stores each default for the given
1000
+ # object type.
1001
+ def evaluate(scope)
1002
+ type = @type.safeevaluate(scope)
1003
+ params = @params.safeevaluate(scope)
1004
+
1005
+ begin
1006
+ scope.setdefaults(type.downcase,params)
1007
+ rescue Puppet::ParseError => except
1008
+ except.line = self.line
1009
+ except.file = self.file
1010
+ raise except
1011
+ rescue => detail
1012
+ error = Puppet::ParseError.new(detail)
1013
+ error.line = self.line
1014
+ error.file = self.file
1015
+ error.stack = caller
1016
+ raise error
1017
+ end
1018
+ end
1019
+
1020
+ def tree(indent = 0)
1021
+ return [
1022
+ @type.tree(indent + 1),
1023
+ ((@@indline * 4 * indent) + self.typewrap(self.pin)),
1024
+ @params.tree(indent + 1)
1025
+ ].join("\n")
1026
+ end
1027
+
1028
+ def to_s
1029
+ return "%s { %s }" % [@type,@params]
1030
+ end
1031
+ end
1032
+
1033
+ # Define a new component. This basically just stores the
1034
+ # associated parse tree by name in our current scope. Note that
1035
+ # there is currently a mismatch in how we look up components -- it
1036
+ # usually uses scopes, but sometimes uses '@@settypes'.
1037
+ # FIXME This class should verify that each of its direct children
1038
+ # has an abstractable name -- i.e., if a file does not include a
1039
+ # variable in its name, then the user is essentially guaranteed to
1040
+ # encounter an error if the component is instantiated more than
1041
+ # once.
1042
+ class CompDef < AST::Branch
1043
+ attr_accessor :name, :args, :code
1044
+
1045
+ def each
1046
+ [@name,@args,@code].each { |child| yield child }
1047
+ end
1048
+
1049
+ # Store the parse tree.
1050
+ def evaluate(scope)
1051
+ name = @name.safeevaluate(scope)
1052
+ args = @args.safeevaluate(scope)
1053
+
1054
+ begin
1055
+ scope.settype(name,
1056
+ AST::Component.new(
1057
+ :name => name,
1058
+ :args => args,
1059
+ :code => @code
1060
+ )
1061
+ )
1062
+ rescue Puppet::ParseError => except
1063
+ except.line = self.line
1064
+ except.file = self.file
1065
+ raise except
1066
+ rescue => detail
1067
+ error = Puppet::ParseError.new(detail)
1068
+ error.line = self.line
1069
+ error.file = self.file
1070
+ error.stack = caller
1071
+ raise error
1072
+ end
1073
+ end
1074
+
1075
+ def initialize(hash)
1076
+ @parentclass = nil
1077
+ super
1078
+
1079
+ Puppet.debug "Defining type %s" % @name.value
1080
+
1081
+ # we need to both mark that a given argument is valid,
1082
+ # and we need to also store any provided default arguments
1083
+ # FIXME This creates a global list of types and their
1084
+ # acceptable arguments. This should really be scoped
1085
+ # instead.
1086
+ @@settypes[@name.value] = self
1087
+ end
1088
+
1089
+ def tree(indent = 0)
1090
+ return [
1091
+ @name.tree(indent + 1),
1092
+ ((@@indline * 4 * indent) + self.typewrap("define")),
1093
+ @args.tree(indent + 1),
1094
+ @code.tree(indent + 1),
1095
+ ].join("\n")
1096
+ end
1097
+
1098
+ def to_s
1099
+ return "define %s(%s) {\n%s }" % [@name, @args, @code]
1100
+ end
1101
+
1102
+ # Check whether a given argument is valid. Searches up through
1103
+ # any parent classes that might exist.
1104
+ def validarg?(param)
1105
+ found = false
1106
+ if @args.is_a?(AST::ASTArray)
1107
+ found = @args.detect { |arg|
1108
+ if arg.is_a?(AST::ASTArray)
1109
+ arg[0].value == param
1110
+ else
1111
+ arg.value == param
1112
+ end
1113
+ }
1114
+ else
1115
+ found = @args.value == param
1116
+ #Puppet.warning "got arg %s" % @args.inspect
1117
+ #hash[@args.value] += 1
1118
+ end
1119
+
1120
+ if found
1121
+ return true
1122
+ # a nil parentclass is an empty astarray
1123
+ # stupid but true
1124
+ elsif @parentclass
1125
+ parent = @@settypes[@parentclass.value]
1126
+ if parent and parent != []
1127
+ return parent.validarg?(param)
1128
+ else
1129
+ raise Puppet::Error, "Could not find parent class %s" %
1130
+ @parentclass.value
1131
+ end
1132
+ else
1133
+ return false
1134
+ end
1135
+
1136
+ end
1137
+ end
1138
+
1139
+ # Define a new class. Syntactically similar to component definitions,
1140
+ # but classes are always singletons -- only one can exist on a given
1141
+ # host.
1142
+ class ClassDef < AST::CompDef
1143
+ attr_accessor :parentclass
1144
+
1145
+ def each
1146
+ if @parentclass
1147
+ #[@name,@args,@parentclass,@code].each { |child| yield child }
1148
+ [@name,@parentclass,@code].each { |child| yield child }
1149
+ else
1150
+ #[@name,@args,@code].each { |child| yield child }
1151
+ [@name,@code].each { |child| yield child }
1152
+ end
1153
+ end
1154
+
1155
+ # Store our parse tree according to name.
1156
+ def evaluate(scope)
1157
+ name = @name.safeevaluate(scope)
1158
+ #args = @args.safeevaluate(scope)
1159
+
1160
+ #:args => args,
1161
+ arghash = {
1162
+ :name => name,
1163
+ :code => @code
1164
+ }
1165
+
1166
+ if @parentclass
1167
+ arghash[:parentclass] = @parentclass.safeevaluate(scope)
1168
+ end
1169
+
1170
+ #Puppet.debug("defining hostclass '%s' with arguments [%s]" %
1171
+ # [name,args])
1172
+
1173
+ begin
1174
+ scope.settype(name,
1175
+ HostClass.new(arghash)
1176
+ )
1177
+ rescue Puppet::ParseError => except
1178
+ except.line = self.line
1179
+ except.file = self.file
1180
+ raise except
1181
+ rescue => detail
1182
+ error = Puppet::ParseError.new(detail)
1183
+ error.line = self.line
1184
+ error.file = self.file
1185
+ error.stack = caller
1186
+ raise error
1187
+ end
1188
+ end
1189
+
1190
+ def initialize(hash)
1191
+ @parentclass = nil
1192
+ super
1193
+ end
1194
+
1195
+ def tree(indent = 0)
1196
+ #@args.tree(indent + 1),
1197
+ return [
1198
+ @name.tree(indent + 1),
1199
+ ((@@indline * 4 * indent) + self.typewrap("class")),
1200
+ @parentclass ? @parentclass.tree(indent + 1) : "",
1201
+ @code.tree(indent + 1),
1202
+ ].join("\n")
1203
+ end
1204
+
1205
+ def to_s
1206
+ return "class %s(%s) inherits %s {\n%s }" %
1207
+ [@name, @parentclass, @code]
1208
+ #[@name, @args, @parentclass, @code]
1209
+ end
1210
+ end
1211
+
1212
+ # Define a node. The node definition stores a parse tree for each
1213
+ # specified node, and this parse tree is only ever looked up when
1214
+ # a client connects.
1215
+ class NodeDef < AST::Branch
1216
+ attr_accessor :names, :code, :parentclass
1217
+
1218
+ def each
1219
+ [@names,@code].each { |child| yield child }
1220
+ end
1221
+
1222
+ # Do implicit iteration over each of the names passed.
1223
+ def evaluate(scope)
1224
+ names = @names.safeevaluate(scope)
1225
+
1226
+ unless names.is_a?(Array)
1227
+ names = [names]
1228
+ end
1229
+
1230
+ names.each { |name|
1231
+ Puppet.debug("defining host '%s' in scope %s" %
1232
+ [name, scope.object_id])
1233
+ arghash = {
1234
+ :name => name,
1235
+ :code => @code
1236
+ }
1237
+
1238
+ if @parentclass
1239
+ arghash[:parentclass] = @parentclass.safeevaluate(scope)
1240
+ end
1241
+
1242
+ begin
1243
+ scope.setnode(name,
1244
+ Node.new(arghash)
1245
+ )
1246
+ rescue Puppet::ParseError => except
1247
+ except.line = self.line
1248
+ except.file = self.file
1249
+ raise except
1250
+ rescue => detail
1251
+ error = Puppet::ParseError.new(detail)
1252
+ error.line = self.line
1253
+ error.file = self.file
1254
+ error.stack = caller
1255
+ raise error
1256
+ end
1257
+ }
1258
+ end
1259
+
1260
+ def initialize(hash)
1261
+ @parentclass = nil
1262
+ super
1263
+ end
1264
+
1265
+ def tree(indent = 0)
1266
+ return [
1267
+ @names.tree(indent + 1),
1268
+ ((@@indline * 4 * indent) + self.typewrap("node")),
1269
+ @code.tree(indent + 1),
1270
+ ].join("\n")
1271
+ end
1272
+
1273
+ def to_s
1274
+ return "node %s {\n%s }" % [@name, @code]
1275
+ end
1276
+ end
1277
+
1278
+ # Evaluate the stored parse tree for a given component. This will
1279
+ # receive the arguments passed to the component and also the type and
1280
+ # name of the component.
1281
+ class Component < AST::Branch
1282
+ class << self
1283
+ attr_accessor :name
1284
+ end
1285
+
1286
+ # The class name
1287
+ @name = :component
1288
+
1289
+ attr_accessor :name, :args, :code, :scope
1290
+
1291
+ def evaluate(scope,hash,objtype,objname)
1292
+
1293
+ scope = scope.newscope
1294
+
1295
+ # The type is the component or class name
1296
+ scope.type = objtype
1297
+
1298
+ # The name is the name the user has chosen or that has
1299
+ # been dynamically generated. This is almost never used
1300
+ scope.name = objname
1301
+
1302
+ #if self.is_a?(Node)
1303
+ # scope.isnodescope
1304
+ #end
1305
+
1306
+ # Additionally, add a tag for whatever kind of class
1307
+ # we are
1308
+ scope.tag(objtype)
1309
+
1310
+ unless objname =~ /-\d+/ # it was generated
1311
+ scope.tag(objname)
1312
+ end
1313
+ #scope.base = self.class.name
1314
+
1315
+
1316
+ # define all of the arguments in our local scope
1317
+ if self.args
1318
+
1319
+ # Verify that all required arguments are either present or
1320
+ # have been provided with defaults.
1321
+ # FIXME This should probably also require each parent
1322
+ # class's arguments...
1323
+ self.args.each { |arg, default|
1324
+ unless hash.include?(arg)
1325
+ if defined? default and ! default.nil?
1326
+ hash[arg] = default
1327
+ Puppet.debug "Got default %s for %s in %s" %
1328
+ [default.inspect, arg.inspect, objname.inspect]
1329
+ else
1330
+ error = Puppet::ParseError.new(
1331
+ "Must pass %s to %s of type %s" %
1332
+ [arg.inspect,name,objtype]
1333
+ )
1334
+ error.line = self.line
1335
+ error.file = self.file
1336
+ error.stack = caller
1337
+ raise error
1338
+ end
1339
+ end
1340
+ }
1341
+ end
1342
+
1343
+ # Set each of the provided arguments as variables in the
1344
+ # component's scope.
1345
+ hash["name"] = objname
1346
+ hash.each { |arg,value|
1347
+ begin
1348
+ scope.setvar(arg,hash[arg])
1349
+ rescue Puppet::ParseError => except
1350
+ except.line = self.line
1351
+ except.file = self.file
1352
+ raise except
1353
+ rescue Puppet::ParseError => except
1354
+ except.line = self.line
1355
+ except.file = self.file
1356
+ raise except
1357
+ rescue => except
1358
+ error = Puppet::ParseError.new(except.message)
1359
+ error.line = self.line
1360
+ error.file = self.file
1361
+ error.stack = caller
1362
+ raise error
1363
+ end
1364
+ }
1365
+
1366
+ # Now just evaluate the code with our new bindings.
1367
+ self.code.safeevaluate(scope)
1368
+
1369
+ # We return the scope, so that our children can make their scopes
1370
+ # under ours. This allows them to find our definitions.
1371
+ return scope
1372
+ end
1373
+ end
1374
+
1375
+ # The code associated with a class. This is different from components
1376
+ # in that each class is a singleton -- only one will exist for a given
1377
+ # node.
1378
+ class HostClass < AST::Component
1379
+ @name = :class
1380
+ attr_accessor :parentclass
1381
+
1382
+ def evaluate(scope,hash,objtype,objname)
1383
+ if scope.lookupclass(@name)
1384
+ Puppet.debug "%s class already evaluated" % @name
1385
+ return nil
1386
+ end
1387
+
1388
+ if tmp = self.evalparent(scope, hash, objname)
1389
+ # Override our scope binding with the parent scope
1390
+ # binding. This is quite hackish, but I can't think
1391
+ # of another way to make sure our scopes end up under
1392
+ # our parent scopes.
1393
+ scope = tmp
1394
+ end
1395
+
1396
+ # just use the Component evaluate method, but change the type
1397
+ # to our own type
1398
+ retval = super(scope,hash,@name,objname)
1399
+
1400
+ # Set the mark after we evaluate, so we don't record it but
1401
+ # then encounter an error
1402
+ scope.setclass(@name)
1403
+ return retval
1404
+ end
1405
+
1406
+ # Evaluate our parent class. Parent classes are evaluated in the
1407
+ # exact same scope as the children. This is maybe not a good idea
1408
+ # but, eh.
1409
+ def evalparent(scope, args, name)
1410
+ if @parentclass
1411
+ parentobj = nil
1412
+
1413
+ begin
1414
+ parentobj = scope.lookuptype(@parentclass)
1415
+ rescue Puppet::ParseError => except
1416
+ except.line = self.line
1417
+ except.file = self.file
1418
+ raise except
1419
+ rescue => detail
1420
+ error = Puppet::ParseError.new(detail)
1421
+ error.line = self.line
1422
+ error.file = self.file
1423
+ raise error
1424
+ end
1425
+ unless parentobj
1426
+ error = Puppet::ParseError.new(
1427
+ "Could not find parent '%s' of '%s'" %
1428
+ [@parentclass,@name])
1429
+ error.line = self.line
1430
+ error.file = self.file
1431
+ raise error
1432
+ end
1433
+
1434
+ # Verify that the parent and child are of the same type
1435
+ unless parentobj.class == self.class
1436
+ error = Puppet::ParseError.new(
1437
+ "Class %s has incompatible parent type, %s vs %s" %
1438
+ [@name, parentobj.class, self.class]
1439
+ )
1440
+ error.file = self.file
1441
+ error.line = self.line
1442
+ raise error
1443
+ end
1444
+ return parentobj.safeevaluate(scope,args,@parentclass,name)
1445
+ end
1446
+ end
1447
+
1448
+ def initialize(hash)
1449
+ @parentclass = nil
1450
+ super
1451
+ end
1452
+
1453
+ end
1454
+
1455
+ # The specific code associated with a host. Nodes are annoyingly unlike
1456
+ # other objects. That's just the way it is, at least for now.
1457
+ class Node < AST::HostClass
1458
+ @name = :node
1459
+ attr_accessor :name, :args, :code, :parentclass
1460
+
1461
+ def evaluate(scope, facts = {})
1462
+ scope = scope.newscope
1463
+
1464
+ # nodes are never instantiated like a normal object,
1465
+ # but we need the type to be the name users would use for
1466
+ # instantiation, otherwise tags don't work out
1467
+
1468
+ # The name has already been evaluated, so it's a normal
1469
+ # string.
1470
+ scope.type = @name
1471
+ scope.name = @name
1472
+
1473
+ # Mark this scope as a nodescope, so that classes will be
1474
+ # singletons within it
1475
+ scope.isnodescope
1476
+
1477
+ # Now set all of the facts inside this scope
1478
+ facts.each { |var, value|
1479
+ scope.setvar(var, value)
1480
+ }
1481
+
1482
+ if tmp = self.evalparent(scope)
1483
+ # Again, override our scope with the parent scope, if
1484
+ # there is one.
1485
+ scope = tmp
1486
+ end
1487
+
1488
+ #scope.tag(@name)
1489
+
1490
+ # We never pass the facts to the parent class, because they've
1491
+ # already been defined at this top-level scope.
1492
+ #super(scope, facts, @name, @name)
1493
+
1494
+ # And then evaluate our code.
1495
+ @code.safeevaluate(scope)
1496
+
1497
+ return scope
1498
+ end
1499
+
1500
+ # Evaluate our parent class.
1501
+ def evalparent(scope)
1502
+ if @parentclass
1503
+ # This is pretty messed up. I don't know if this will
1504
+ # work in the long term, but we need to evaluate the node
1505
+ # in our own scope, even though our parent node has
1506
+ # a scope associated with it, because otherwise we 1) won't
1507
+ # get our facts defined, and 2) we won't actually get the
1508
+ # objects returned, based on how nodes work.
1509
+
1510
+ # We also can't just evaluate the node itself, because
1511
+ # it would create a node scope within this scope,
1512
+ # and that would cause mass havoc.
1513
+ node = nil
1514
+
1515
+ # The 'node' method just returns a hash of the node
1516
+ # code and name. It's used here, and in 'evalnode'.
1517
+ unless hash = scope.node(@parentclass)
1518
+ raise Puppet::ParseError,
1519
+ "Could not find parent node %s" %
1520
+ @parentclass
1521
+ end
1522
+
1523
+ node = hash[:node]
1524
+ # Tag the scope with the parent's name/type.
1525
+ name = node.name
1526
+ #Puppet.info "Tagging with parent node %s" % name
1527
+ scope.tag(name)
1528
+
1529
+ begin
1530
+ code = node.code
1531
+ code.safeevaluate(scope)
1532
+ rescue Puppet::ParseError => except
1533
+ except.line = self.line
1534
+ except.file = self.file
1535
+ raise except
1536
+ rescue => detail
1537
+ error = Puppet::ParseError.new(detail)
1538
+ error.line = self.line
1539
+ error.file = self.file
1540
+ raise error
1541
+ end
1542
+
1543
+ if node.parentclass
1544
+ node.evalparent(scope)
1545
+ end
1546
+ end
1547
+ end
1548
+
1549
+ def initialize(hash)
1550
+ @parentclass = nil
1551
+ super
1552
+
1553
+ end
1554
+ end
1555
+ #---------------------------------------------------------------
1556
+ end
1557
+ end
1558
+ end
1559
+
1560
+ # $Id: ast.rb 736 2005-10-30 01:12:03Z luke $