115: def find_or_load(namespace, name, type)
116: method = "find_#{type}"
117: namespace = namespace.downcase
118: name = name.downcase
119: fullname = (namespace + "::" + name).sub(/^::/, '')
120:
121: if name =~ /^::/
122: names_to_try = [name.sub(/^::/, '')]
123: else
124: names_to_try = [fullname]
125:
126:
127: names_to_try << fullname.split("::")[0] if fullname.include?("::")
128:
129:
130:
131:
132: names_to_try << name
133: names_to_try.compact!
134: end
135:
136: until (result = @loaded_code.send(method, namespace, name)) or names_to_try.empty? do
137: self.load(names_to_try.shift)
138: end
139: return result
140: end
141:
142:
143: def import(file)
144: if Puppet[:ignoreimport]
145: return AST::ASTArray.new(:children => [])
146: end
147:
148: if @lexer.file
149: dir = @lexer.file.sub(%r{[^/]+$},'').sub(/\/$/, '')
150: else
151: dir = "."
152: end
153: if dir == ""
154: dir = "."
155: end
156: result = ast AST::ASTArray
157:
158:
159:
160: raise "Got no file" unless file
161: pat = file
162: if pat.index("$")
163: Puppet.warning(
164: "The import of #{pat} contains a variable reference;" +
165: " variables are not interpolated for imports " +
166: "in file #{@lexer.file} at line #{@lexer.line}"
167: )
168: end
169: files = Puppet::Parser::Files.find_manifests(pat, :cwd => dir, :environment => @environment)
170: if files.size == 0
171: raise Puppet::ImportError.new("No file(s) found for import " +
172: "of '#{pat}'")
173: end
174:
175: files.collect { |file|
176: parser = Puppet::Parser::Parser.new(:loaded_code => @loaded_code, :environment => @environment)
177: parser.files = self.files
178: Puppet.debug("importing '%s'" % file)
179:
180: unless file =~ /^#{File::SEPARATOR}/
181: file = File.join(dir, file)
182: end
183: begin
184: parser.file = file
185: rescue Puppet::AlreadyImportedError
186:
187: next
188: end
189:
190:
191: parser.parse
192: }
193: end
194:
195: def initialize(options = {})
196: @loaded_code = options[:loaded_code] || Puppet::Parser::LoadedCode.new
197: @environment = options[:environment]
198: initvars()
199: end
200:
201:
202: def initvars
203: @lexer = Puppet::Parser::Lexer.new()
204: @files = {}
205: @loaded = []
206: @loading = {}
207: @loading.extend(MonitorMixin)
208: class << @loading
209: def done_with(item)
210: synchronize do
211: delete(item)[:busy].signal if self.has_key?(item) and self[item][:loader] == Thread.current
212: end
213: end
214: def owner_of(item)
215: synchronize do
216: if !self.has_key? item
217: self[item] = { :loader => Thread.current, :busy => self.new_cond}
218: :nobody
219: elsif self[item][:loader] == Thread.current
220: :this_thread
221: else
222: flag = self[item][:busy]
223: flag.wait
224: flag.signal
225: :another_thread
226: end
227: end
228: end
229: end
230: end
231:
232:
233: def able_to_import?(classname,item,msg)
234: unless @loaded.include?(item)
235: begin
236: case @loading.owner_of(item)
237: when :this_thread
238: return
239: when :another_thread
240: return able_to_import?(classname,item,msg)
241: when :nobody
242: import(item)
243: Puppet.info "Autoloaded #{msg}"
244: @loaded << item
245: end
246: rescue Puppet::ImportError => detail
247:
248: ensure
249: @loading.done_with(item)
250: end
251: end
252:
253:
254: return @loaded_code.hostclass(classname) || @loaded_code.definition(classname)
255: end
256:
257:
258: def load(classname)
259: return false if classname == ""
260: filename = classname.gsub("::", File::SEPARATOR)
261: mod = filename.scan(/^[\w-]+/).shift
262:
263:
264: [[mod, "module %s" % mod ],
265: [filename,"file %s from module %s" % [filename, mod]]
266: ].any? { |item,description| able_to_import?(classname,item,description) }
267: end
268:
269:
270: def namesplit(fullname)
271: ary = fullname.split("::")
272: n = ary.pop || ""
273: ns = ary.join("::")
274: return ns, n
275: end
276:
277:
278: def newclass(name, options = {})
279: name = name.downcase
280:
281: if @loaded_code.definition(name)
282: raise Puppet::ParseError, "Cannot redefine class %s as a definition" % name
283: end
284: code = options[:code]
285: parent = options[:parent]
286: doc = options[:doc]
287:
288:
289: if other = @loaded_code.hostclass(name) || @loaded_code.definition(name)
290:
291: if parent and other.parentclass and (parent != other.parentclass)
292: error("Class %s is already defined at %s:%s; cannot redefine" % [name, other.file, other.line])
293: end
294:
295:
296: if parent and ! other.parentclass
297: other.parentclass = parent
298: end
299:
300:
301: if code
302: tmp = name
303: if tmp == ""
304: tmp = "main"
305: end
306:
307: Puppet.debug addcontext("Adding code to %s" % tmp)
308:
309: if other.code and code
310:
311:
312: other.code = ast AST::ASTArray, :children => [other.code] unless other.code.is_a?(AST::ASTArray)
313: code = ast AST::ASTArray, :children => [code] unless code.is_a?(AST::ASTArray)
314: other.code.children += code.children
315: else
316: other.code ||= code
317: end
318: end
319:
320: if other.doc and doc
321: other.doc += doc
322: else
323: other.doc ||= doc
324: end
325: else
326:
327:
328:
329:
330: args = {:namespace => name, :classname => name, :parser => self}
331: args[:code] = code if code
332: args[:parentclass] = parent if parent
333: args[:doc] = doc
334: args[:line] = options[:line]
335:
336: @loaded_code.add_hostclass(name, ast(AST::HostClass, args))
337: end
338:
339: return @loaded_code.hostclass(name)
340: end
341:
342:
343: def newdefine(name, options = {})
344: name = name.downcase
345: if @loaded_code.hostclass(name)
346: raise Puppet::ParseError, "Cannot redefine class %s as a definition" %
347: name
348: end
349:
350: if other = @loaded_code.definition(name)
351: error("%s is already defined at %s:%s; cannot redefine" % [name, other.file, other.line])
352: end
353:
354: ns, whatever = namesplit(name)
355: args = {
356: :namespace => ns,
357: :arguments => options[:arguments],
358: :code => options[:code],
359: :parser => self,
360: :classname => name,
361: :doc => options[:doc],
362: :line => options[:line]
363: }
364:
365: [:code, :arguments].each do |param|
366: args[param] = options[param] if options[param]
367: end
368:
369: @loaded_code.add_definition(name, ast(AST::Definition, args))
370: end
371:
372:
373:
374: def newnode(names, options = {})
375: names = [names] unless names.instance_of?(Array)
376: doc = lexer.getcomment
377: names.collect do |name|
378: name = AST::HostName.new :value => name unless name.is_a?(AST::HostName)
379: if other = @loaded_code.node_exists?(name)
380: error("Node %s is already defined at %s:%s; cannot redefine" % [other.name, other.file, other.line])
381: end
382: name = name.to_s if name.is_a?(Symbol)
383: args = {
384: :name => name,
385: :parser => self,
386: :doc => doc,
387: :line => options[:line]
388: }
389: if options[:code]
390: args[:code] = options[:code]
391: end
392: if options[:parent]
393: args[:parentclass] = options[:parent]
394: end
395: node = ast(AST::Node, args)
396: node.classname = name.to_classname
397: @loaded_code.add_node(name, node)
398: node
399: end
400: end
401:
402: def on_error(token,value,stack)
403: if token == 0
404: value = 'end of file'
405: else
406: value = "'%s'" % value[:value]
407: end
408: error = "Syntax error at %s" % [value]
409:
410: if brace = @lexer.expected
411: error += "; expected '%s'" % brace
412: end
413:
414: except = Puppet::ParseError.new(error)
415: except.line = @lexer.line
416: if @lexer.file
417: except.file = @lexer.file
418: end
419:
420: raise except
421: end
422:
423:
424: def parse(string = nil)
425: if string
426: self.string = string
427: end
428: begin
429: @yydebug = false
430: main = yyparse(@lexer,:scan)
431: rescue Racc::ParseError => except
432: error = Puppet::ParseError.new(except)
433: error.line = @lexer.line
434: error.file = @lexer.file
435: error.set_backtrace except.backtrace
436: raise error
437: rescue Puppet::ParseError => except
438: except.line ||= @lexer.line
439: except.file ||= @lexer.file
440: raise except
441: rescue Puppet::Error => except
442:
443: except.line ||= @lexer.line
444: except.file ||= @lexer.file
445: raise except
446: rescue Puppet::DevError => except
447: except.line ||= @lexer.line
448: except.file ||= @lexer.file
449: raise except
450: rescue => except
451: error = Puppet::DevError.new(except.message)
452: error.line = @lexer.line
453: error.file = @lexer.file
454: error.set_backtrace except.backtrace
455: raise error
456: end
457: if main
458:
459: newclass("", :code => main)
460: end
461: return @loaded_code
462: ensure
463: @lexer.clear
464: end
465:
466:
467: def reparse?
468: if file = @files.detect { |name, file| file.changed? }
469: return file[1].stamp
470: else
471: return false
472: end
473: end
474:
475: def string=(string)
476: @lexer.string = string
477: end
478:
479: def version
480: return @version if defined?(@version)
481:
482: if Puppet[:config_version] == ""
483: @version = Time.now.to_i
484: return @version
485: end
486:
487: @version = Puppet::Util.execute([Puppet[:config_version]]).strip
488:
489: rescue Puppet::ExecutionFailure => e
490: raise Puppet::ParseError, "Unable to set config_version: #{e.message}"
491: end
492:
493:
494:
495:
496: def watch_file(filename)
497: check_and_add_to_watched_files(filename)
498: end
499:
500: private
501:
502: def check_and_add_to_watched_files(filename)
503: unless @files.include?(filename)
504: @files[filename] = Puppet::Util::LoadedFile.new(filename)
505: return true
506: else
507: return false
508: end
509: end
510: end