| Class | Puppet::Util::Settings |
| In: |
lib/puppet/util/settings.rb
|
| Parent: | Object |
The class for handling configuration files.
| file | [RW] | |
| timer | [R] |
Create a new collection of config settings.
# File lib/puppet/util/settings.rb, line 197
197: def initialize
198: @config = {}
199: @shortnames = {}
200:
201: @created = []
202: @searchpath = nil
203:
204: # Mutex-like thing to protect @values
205: @sync = Sync.new
206:
207: # Keep track of set values.
208: @values = Hash.new { |hash, key| hash[key] = {} }
209:
210: # And keep a per-environment cache
211: @cache = Hash.new { |hash, key| hash[key] = {} }
212:
213: # A central concept of a name.
214: @name = nil
215:
216: # The list of sections we've used.
217: @used = []
218: end
Set a config value. This doesn‘t set the defaults, it sets the value itself.
# File lib/puppet/util/settings.rb, line 21
21: def []=(param, value)
22: param = symbolize(param)
23: unless element = @config[param]
24: raise ArgumentError,
25: "Attempt to assign a value to unknown configuration parameter %s" % param.inspect
26: end
27: if element.respond_to?(:munge)
28: value = element.munge(value)
29: end
30: if element.respond_to?(:handle)
31: element.handle(value)
32: end
33: # Reset the name, so it's looked up again.
34: if param == :name
35: @name = nil
36: end
37: @sync.synchronize do # yay, thread-safe
38: @values[:memory][param] = value
39: @cache.clear
40: end
41:
42: return value
43: end
Generate the list of valid arguments, in a format that GetoptLong can understand, and add them to the passed option list.
# File lib/puppet/util/settings.rb, line 47
47: def addargs(options)
48: # Hackish, but acceptable. Copy the current ARGV for restarting.
49: Puppet.args = ARGV.dup
50:
51: # Add all of the config parameters as valid options.
52: self.each { |name, element|
53: element.getopt_args.each { |args| options << args }
54: }
55:
56: return options
57: end
# File lib/puppet/util/settings.rb, line 59
59: def apply
60: trans = self.to_transportable
61: begin
62: config = trans.to_catalog
63: config.store_state = false
64: config.apply
65: config.clear
66: rescue => detail
67: if Puppet[:trace]
68: puts detail.backtrace
69: end
70: Puppet.err "Could not configure myself: %s" % detail
71: end
72: end
Is our parameter a boolean parameter?
# File lib/puppet/util/settings.rb, line 75
75: def boolean?(param)
76: param = symbolize(param)
77: if @config.include?(param) and @config[param].kind_of? CBoolean
78: return true
79: else
80: return false
81: end
82: end
Remove all set values, potentially skipping cli values.
# File lib/puppet/util/settings.rb, line 85
85: def clear(exceptcli = false)
86: @sync.synchronize do
87: @values.each do |name, values|
88: @values.delete(name) unless exceptcli and name == :cli
89: end
90:
91: # Don't clear the 'used' in this case, since it's a config file reparse,
92: # and we want to retain this info.
93: unless exceptcli
94: @used = []
95: end
96:
97: @cache.clear
98:
99: @name = nil
100: end
101: end
This is mostly just used for testing.
# File lib/puppet/util/settings.rb, line 104
104: def clearused
105: @cache.clear
106: @used = []
107: end
Do variable interpolation on the value.
# File lib/puppet/util/settings.rb, line 110
110: def convert(value, environment = nil)
111: return value unless value
112: return value unless value.is_a? String
113: newval = value.gsub(/\$(\w+)|\$\{(\w+)\}/) do |value|
114: varname = $2 || $1
115: if varname == "environment" and environment
116: environment
117: elsif pval = self.value(varname)
118: pval
119: else
120: raise Puppet::DevError, "Could not find value for %s" % value
121: end
122: end
123:
124: return newval
125: end
Return a value‘s description.
# File lib/puppet/util/settings.rb, line 128
128: def description(name)
129: if obj = @config[symbolize(name)]
130: obj.desc
131: else
132: nil
133: end
134: end
# File lib/puppet/util/settings.rb, line 136
136: def each
137: @config.each { |name, object|
138: yield name, object
139: }
140: end
# File lib/puppet/util/settings.rb, line 258
258: def generate_config
259: puts to_config
260: true
261: end
# File lib/puppet/util/settings.rb, line 263
263: def generate_manifest
264: puts to_manifest
265: true
266: end
Handle a command-line argument.
# File lib/puppet/util/settings.rb, line 161
161: def handlearg(opt, value = nil)
162: @cache.clear
163: value = munge_value(value) if value
164: str = opt.sub(/^--/,'')
165: bool = true
166: newstr = str.sub(/^no-/, '')
167: if newstr != str
168: str = newstr
169: bool = false
170: end
171: str = str.intern
172: if self.valid?(str)
173: @sync.synchronize do
174: if self.boolean?(str)
175: @values[:cli][str] = bool
176: else
177: @values[:cli][str] = value
178: end
179: end
180: else
181: raise ArgumentError, "Invalid argument %s" % opt
182: end
183: end
# File lib/puppet/util/settings.rb, line 185
185: def include?(name)
186: name = name.intern if name.is_a? String
187: @config.include?(name)
188: end
Return a given object‘s file metadata.
# File lib/puppet/util/settings.rb, line 279
279: def metadata(param)
280: if obj = @config[symbolize(param)] and obj.is_a?(CFile)
281: return [:owner, :group, :mode].inject({}) do |meta, p|
282: if v = obj.send(p)
283: meta[p] = v
284: end
285: meta
286: end
287: else
288: nil
289: end
290: end
Make a directory with the appropriate user, group, and mode
# File lib/puppet/util/settings.rb, line 293
293: def mkdir(default)
294: obj = get_config_file_default(default)
295:
296: Puppet::Util::SUIDManager.asuser(obj.owner, obj.group) do
297: mode = obj.mode || 0750
298: Dir.mkdir(obj.value, mode)
299: end
300: end
Figure out our name.
# File lib/puppet/util/settings.rb, line 303
303: def name
304: unless @name
305: unless @config[:name]
306: return nil
307: end
308: searchpath.each do |source|
309: next if source == :name
310: @sync.synchronize do
311: @name = @values[source][:name]
312: end
313: break if @name
314: end
315: unless @name
316: @name = convert(@config[:name].default).intern
317: end
318: end
319: @name
320: end
Parse the configuration file. As of May 2007, this is a backward-compatibility method and will be deprecated soon.
# File lib/puppet/util/settings.rb, line 390
390: def old_parse(file)
391: text = nil
392:
393: if file.is_a? Puppet::Util::LoadedFile
394: @file = file
395: else
396: @file = Puppet::Util::LoadedFile.new(file)
397: end
398:
399: # Don't create a timer for the old style parsing.
400: # settimer()
401:
402: begin
403: text = File.read(@file.file)
404: rescue Errno::ENOENT
405: raise Puppet::Error, "No such file %s" % file
406: rescue Errno::EACCES
407: raise Puppet::Error, "Permission denied to file %s" % file
408: end
409:
410: @sync.synchronize do
411: @values = Hash.new { |names, name|
412: names[name] = {}
413: }
414: end
415:
416: # Get rid of the values set by the file, keeping cli values.
417: self.clear(true)
418:
419: section = "puppet"
420: metas = %w{owner group mode}
421: values = Hash.new { |hash, key| hash[key] = {} }
422: @sync.synchronize do
423: text.split(/\n/).each { |line|
424: case line
425: when /^\[(\w+)\]$/: section = $1 # Section names
426: when /^\s*#/: next # Skip comments
427: when /^\s*$/: next # Skip blanks
428: when /^\s*(\w+)\s*=\s*(.+)$/: # settings
429: var = $1.intern
430: if var == :mode
431: value = $2
432: else
433: value = munge_value($2)
434: end
435:
436: # Only warn if we don't know what this config var is. This
437: # prevents exceptions later on.
438: unless @config.include?(var) or metas.include?(var.to_s)
439: Puppet.warning "Discarded unknown configuration parameter %s" % var.inspect
440: next # Skip this line.
441: end
442:
443: # Mmm, "special" attributes
444: if metas.include?(var.to_s)
445: unless values.include?(section)
446: values[section] = {}
447: end
448: values[section][var.to_s] = value
449:
450: # If the parameter is valid, then set it.
451: if section == Puppet[:name] and @config.include?(var)
452: #@config[var].value = value
453: @values[:main][var] = value
454: end
455: next
456: end
457:
458: # Don't override set parameters, since the file is parsed
459: # after cli arguments are handled.
460: unless @config.include?(var) and @config[var].setbycli
461: Puppet.debug "%s: Setting %s to '%s'" % [section, var, value]
462: @values[:main][var] = value
463: end
464: @config[var].section = symbolize(section)
465:
466: metas.each { |meta|
467: if values[section][meta]
468: if @config[var].respond_to?(meta + "=")
469: @config[var].send(meta + "=", values[section][meta])
470: end
471: end
472: }
473: else
474: raise Puppet::Error, "Could not match line %s" % line
475: end
476: }
477: end
478: end
Return all of the parameters associated with a given section.
# File lib/puppet/util/settings.rb, line 323
323: def params(section = nil)
324: if section
325: section = section.intern if section.is_a? String
326: @config.find_all { |name, obj|
327: obj.section == section
328: }.collect { |name, obj|
329: name
330: }
331: else
332: @config.keys
333: end
334: end
Parse the configuration file. Just provides thread safety.
# File lib/puppet/util/settings.rb, line 338
338: def parse(file)
339: # We have to clear outside of the sync, because it's
340: # also using synchronize().
341: clear(true)
342:
343: @sync.synchronize do
344: unsafe_parse(file)
345: end
346: end
Iterate across all of the objects in a given section.
# File lib/puppet/util/settings.rb, line 506
506: def persection(section)
507: section = symbolize(section)
508: self.each { |name, obj|
509: if obj.section == section
510: yield obj
511: end
512: }
513: end
Prints the contents of a config file with the available config elements, or it prints a single value of a config element.
# File lib/puppet/util/settings.rb, line 227
227: def print_config_options
228: env = value(:environment)
229: val = value(:configprint)
230: if val == "all"
231: hash = {}
232: each do |name, obj|
233: val = value(name,env)
234: val = val.inspect if val == ""
235: hash[name] = val
236: end
237: hash.sort { |a,b| a[0].to_s <=> b[0].to_s }.each do |name, val|
238: puts "%s = %s" % [name, val]
239: end
240: else
241: val.split(/\s*,\s*/).sort.each do |v|
242: if include?(v)
243: #if there is only one value, just print it for back compatibility
244: if v == val
245: puts value(val,env)
246: break
247: end
248: puts "%s = %s" % [v, value(v,env)]
249: else
250: puts "invalid parameter: %s" % v
251: return false
252: end
253: end
254: end
255: true
256: end
# File lib/puppet/util/settings.rb, line 268
268: def print_configs
269: return print_config_options if value(:configprint) != ""
270: return generate_config if value(:genconfig)
271: return generate_manifest if value(:genmanifest)
272: end
# File lib/puppet/util/settings.rb, line 274
274: def print_configs?
275: return (value(:configprint) != "" || value(:genconfig) || value(:genmanifest)) && true
276: end
# File lib/puppet/util/settings.rb, line 843
843: def readwritelock(default, *args, &bloc)
844: file = value(get_config_file_default(default).name)
845: tmpfile = file + ".tmp"
846: sync = Sync.new
847: unless FileTest.directory?(File.dirname(tmpfile))
848: raise Puppet::DevError, "Cannot create %s; directory %s does not exist" %
849: [file, File.dirname(file)]
850: end
851:
852: sync.synchronize(Sync::EX) do
853: File.open(file, "r+", 0600) do |rf|
854: rf.lock_exclusive do
855: if File.exist?(tmpfile)
856: raise Puppet::Error, ".tmp file already exists for %s; Aborting locked write. Check the .tmp file and delete if appropriate" %
857: [file]
858: end
859:
860: writesub(default, tmpfile, *args, &bloc)
861:
862: begin
863: File.rename(tmpfile, file)
864: rescue => detail
865: Puppet.err "Could not rename %s to %s: %s" %
866: [file, tmpfile, detail]
867: end
868: end
869: end
870: end
871: end
Reparse our config file, if necessary.
# File lib/puppet/util/settings.rb, line 516
516: def reparse
517: if defined? @file and @file.changed?
518: Puppet.notice "Reparsing %s" % @file.file
519: @sync.synchronize do
520: parse(@file)
521: end
522: reuse()
523: end
524: end
# File lib/puppet/util/settings.rb, line 526
526: def reuse
527: return unless defined? @used
528: @sync.synchronize do # yay, thread-safe
529: @used.each do |section|
530: @used.delete(section)
531: self.use(section)
532: end
533: end
534: end
The order in which to search for values.
# File lib/puppet/util/settings.rb, line 537
537: def searchpath(environment = nil)
538: if environment
539: [:cli, :memory, environment, :name, :main]
540: else
541: [:cli, :memory, :name, :main]
542: end
543: end
Convert a single section into transportable objects.
# File lib/puppet/util/settings.rb, line 561
561: def section_to_transportable(section, done = nil)
562: done ||= Hash.new { |hash, key| hash[key] = {} }
563: objects = []
564: persection(section) do |obj|
565: if @config[:mkusers] and value(:mkusers)
566: objects += add_user_resources(section, obj, done)
567: end
568:
569: value = obj.value
570:
571: # Only files are convertable to transportable resources.
572: next unless obj.respond_to? :to_transportable and transobjects = obj.to_transportable
573:
574: transobjects = [transobjects] unless transobjects.is_a? Array
575: transobjects.each do |trans|
576: # transportable could return nil
577: next unless trans
578: unless done[:file].include? trans.name
579: @created << trans.name
580: objects << trans
581: done[:file][trans.name] = trans
582: end
583: end
584: end
585:
586: bucket = Puppet::TransBucket.new
587: bucket.type = "Settings"
588: bucket.name = section
589: bucket.push(*objects)
590: bucket.keyword = "class"
591:
592: return bucket
593: end
Get a list of objects per section
# File lib/puppet/util/settings.rb, line 546
546: def sectionlist
547: sectionlist = []
548: self.each { |name, obj|
549: section = obj.section || "puppet"
550: sections[section] ||= []
551: unless sectionlist.include?(section)
552: sectionlist << section
553: end
554: sections[section] << obj
555: }
556:
557: return sectionlist, sections
558: end
Set a bunch of defaults in a given section. The sections are actually pretty pointless, but they help break things up a bit, anyway.
# File lib/puppet/util/settings.rb, line 597
597: def setdefaults(section, defs)
598: section = symbolize(section)
599: call = []
600: defs.each { |name, hash|
601: if hash.is_a? Array
602: unless hash.length == 2
603: raise ArgumentError, "Defaults specified as an array must contain only the default value and the decription"
604: end
605: tmp = hash
606: hash = {}
607: [:default, :desc].zip(tmp).each { |p,v| hash[p] = v }
608: end
609: name = symbolize(name)
610: hash[:name] = name
611: hash[:section] = section
612: if @config.include?(name)
613: raise ArgumentError, "Parameter %s is already defined" % name
614: end
615: tryconfig = newelement(hash)
616: if short = tryconfig.short
617: if other = @shortnames[short]
618: raise ArgumentError, "Parameter %s is already using short name '%s'" % [other.name, short]
619: end
620: @shortnames[short] = tryconfig
621: end
622: @config[name] = tryconfig
623:
624: # Collect the settings that need to have their hooks called immediately.
625: # We have to collect them so that we can be sure we're fully initialized before
626: # the hook is called.
627: call << tryconfig if tryconfig.call_on_define
628: }
629:
630: call.each { |setting| setting.handle(self.value(setting.name)) }
631: end
Create a timer to check whether the file should be reparsed.
# File lib/puppet/util/settings.rb, line 634
634: def settimer
635: if Puppet[:filetimeout] > 0
636: @timer = Puppet.newtimer(
637: :interval => Puppet[:filetimeout],
638: :tolerance => 1,
639: :start? => true
640: ) do
641: self.reparse()
642: end
643: end
644: end
Convert our list of config elements into a configuration file.
# File lib/puppet/util/settings.rb, line 653
653: def to_config
654: str = %{The configuration file for #{Puppet[:name]}. Note that this file
655: is likely to have unused configuration parameters in it; any parameter that's
656: valid anywhere in Puppet can be in any config file, even if it's not used.
657:
658: Every section can specify three special parameters: owner, group, and mode.
659: These parameters affect the required permissions of any files specified after
660: their specification. Puppet will sometimes use these parameters to check its
661: own configured state, so they can be used to make Puppet a bit more self-managing.
662:
663: Generated on #{Time.now}.
664:
665: }.gsub(/^/, "# ")
666:
667: # Add a section heading that matches our name.
668: if @config.include?(:name)
669: str += "[%s]\n" % self[:name]
670: end
671: eachsection do |section|
672: persection(section) do |obj|
673: str += obj.to_config + "\n"
674: end
675: end
676:
677: return str
678: end
Convert our list of objects into a component that can be applied.
# File lib/puppet/util/settings.rb, line 647
647: def to_configuration
648: transport = self.to_transportable
649: return transport.to_catalog
650: end
Convert to a parseable manifest
# File lib/puppet/util/settings.rb, line 710
710: def to_manifest
711: transport = self.to_transportable
712:
713: manifest = transport.to_manifest + "\n"
714: eachsection { |section|
715: manifest += "include #{section}\n"
716: }
717:
718: return manifest
719: end
Convert our configuration into a list of transportable objects.
# File lib/puppet/util/settings.rb, line 681
681: def to_transportable(*sections)
682: done = Hash.new { |hash, key|
683: hash[key] = {}
684: }
685:
686: topbucket = Puppet::TransBucket.new
687: if defined? @file.file and @file.file
688: topbucket.name = @file.file
689: else
690: topbucket.name = "top"
691: end
692: topbucket.type = "Settings"
693: topbucket.top = true
694:
695: # Now iterate over each section
696: if sections.empty?
697: eachsection do |section|
698: sections << section
699: end
700: end
701: sections.each do |section|
702: obj = section_to_transportable(section, done)
703: topbucket.push obj
704: end
705:
706: topbucket
707: end
Create the necessary objects to use a section. This is idempotent; you can ‘use’ a section as many times as you want.
# File lib/puppet/util/settings.rb, line 723
723: def use(*sections)
724: @sync.synchronize do # yay, thread-safe
725: sections = sections.reject { |s| @used.include?(s.to_sym) }
726:
727: return if sections.empty?
728:
729: bucket = to_transportable(*sections)
730:
731: begin
732: catalog = bucket.to_catalog
733: rescue => detail
734: puts detail.backtrace if Puppet[:trace]
735: Puppet.err "Could not create resources for managing Puppet's files and directories: %s" % detail
736:
737: # We need some way to get rid of any resources created during the catalog creation
738: # but not cleaned up.
739: return
740: end
741:
742: begin
743: catalog.host_config = false
744: catalog.apply do |transaction|
745: if transaction.any_failed?
746: report = transaction.report
747: failures = report.logs.find_all { |log| log.level == :err }
748: raise "Got %s failure(s) while initializing: %s" % [failures.length, failures.collect { |l| l.to_s }.join("; ")]
749: end
750: end
751: ensure
752: catalog.clear
753: end
754:
755: sections.each { |s| @used << s }
756: @used.uniq!
757: end
758: end
# File lib/puppet/util/settings.rb, line 760
760: def valid?(param)
761: param = symbolize(param)
762: @config.has_key?(param)
763: end
Find the correct value using our search path. Optionally accept an environment in which to search before the other configuration sections.
# File lib/puppet/util/settings.rb, line 767
767: def value(param, environment = nil)
768: param = symbolize(param)
769: environment = symbolize(environment) if environment
770:
771: # Short circuit to nil for undefined parameters.
772: return nil unless @config.include?(param)
773:
774: # Yay, recursion.
775: self.reparse() unless param == :filetimeout
776:
777: # Check the cache first. It needs to be a per-environment
778: # cache so that we don't spread values from one env
779: # to another.
780: if cached = @cache[environment||"none"][param]
781: return cached
782: end
783:
784: # See if we can find it within our searchable list of values
785: val = catch :foundval do
786: each_source(environment) do |source|
787: # Look for the value. We have to test the hash for whether
788: # it exists, because the value might be false.
789: @sync.synchronize do
790: if @values[source].include?(param)
791: throw :foundval, @values[source][param]
792: end
793: end
794: end
795: throw :foundval, nil
796: end
797:
798: # If we didn't get a value, use the default
799: val = @config[param].default if val.nil?
800:
801: # Convert it if necessary
802: val = convert(val, environment)
803:
804: # And cache it
805: @cache[environment||"none"][param] = val
806: return val
807: end