diff options
| author | luke <luke@980ebf18-57e1-0310-9a29-db15c13687c0> | 2007-02-07 23:56:59 +0000 |
|---|---|---|
| committer | luke <luke@980ebf18-57e1-0310-9a29-db15c13687c0> | 2007-02-07 23:56:59 +0000 |
| commit | 6d8068eddd0d29ec53f62557eb53f6ebb8e40591 (patch) | |
| tree | 8c93181b9325fee95d7ecdc6e79341ff6d3604b0 /lib | |
| parent | 162602323406117444ce4375ead91d8f92f2b31a (diff) | |
| download | puppet-6d8068eddd0d29ec53f62557eb53f6ebb8e40591.tar.gz puppet-6d8068eddd0d29ec53f62557eb53f6ebb8e40591.tar.xz puppet-6d8068eddd0d29ec53f62557eb53f6ebb8e40591.zip | |
Moving some of the stand-alone classes into the util/ subdirectory, to clean up the top-level namespace a bit. This is a lot of file modifications, but most of them just change class names and file paths.
git-svn-id: https://reductivelabs.com/svn/puppet/trunk@2178 980ebf18-57e1-0310-9a29-db15c13687c0
Diffstat (limited to 'lib')
54 files changed, 1524 insertions, 1538 deletions
diff --git a/lib/puppet.rb b/lib/puppet.rb index 739113fc9..f12645357 100644 --- a/lib/puppet.rb +++ b/lib/puppet.rb @@ -4,11 +4,11 @@ require 'facter' require 'puppet/error' require 'puppet/external/event-loop' require 'puppet/util' -require 'puppet/log' -require 'puppet/autoload' -require 'puppet/config' -require 'puppet/feature' -require 'puppet/suidmanager' +require 'puppet/util/log' +require 'puppet/util/autoload' +require 'puppet/util/config' +require 'puppet/util/feature' +require 'puppet/util/suidmanager' #------------------------------------------------------------ # the top-level module @@ -47,18 +47,18 @@ module Puppet end # the hash that determines how our system behaves - @@config = Puppet::Config.new + @@config = Puppet::Util::Config.new # The services running in this process. @services ||= [] # define helper messages for each of the message levels - Puppet::Log.eachlevel { |level| + Puppet::Util::Log.eachlevel { |level| define_method(level,proc { |args| if args.is_a?(Array) args = args.join(" ") end - Puppet::Log.create( + Puppet::Util::Log.create( :level => level, :message => args ) @@ -71,7 +71,7 @@ module Puppet alias :error :err # The feature collection - @features = Puppet::Feature.new('puppet/feature') + @features = Puppet::Util::Feature.new('puppet/feature') # Store a new default value. def self.setdefaults(section, hash) @@ -85,7 +85,7 @@ module Puppet def self.[](param) case param when :debug: - if Puppet::Log.level == :debug + if Puppet::Util::Log.level == :debug return true else return false @@ -106,9 +106,9 @@ module Puppet def self.debug=(value) if value - Puppet::Log.level=(:debug) + Puppet::Util::Log.level=(:debug) else - Puppet::Log.level=(:notice) + Puppet::Util::Log.level=(:notice) end end @@ -194,7 +194,7 @@ module Puppet command = $0 + " " + self.args.join(" ") Puppet.notice "Restarting with '%s'" % command Puppet.shutdown(false) - Puppet::Log.reopen + Puppet::Util::Log.reopen exec(command) end @@ -245,7 +245,7 @@ module Puppet end trap(:USR2) do - Puppet::Log.reopen + Puppet::Util::Log.reopen end end @@ -389,7 +389,7 @@ end require 'puppet/server' require 'puppet/type' -require 'puppet/storage' +require 'puppet/util/storage' if Puppet[:storeconfigs] require 'puppet/rails' end diff --git a/lib/puppet/client.rb b/lib/puppet/client.rb index de3b3eaec..e668aa27a 100644 --- a/lib/puppet/client.rb +++ b/lib/puppet/client.rb @@ -153,7 +153,7 @@ module Puppet else self.stopping = true if self.respond_to? :running? and self.running? - Puppet::Storage.store + Puppet::Util::Storage.store end rmpidfile() end diff --git a/lib/puppet/client/master.rb b/lib/puppet/client/master.rb index b59d710c9..046e0c5aa 100644 --- a/lib/puppet/client/master.rb +++ b/lib/puppet/client/master.rb @@ -137,7 +137,7 @@ class Puppet::Client::MasterClient < Puppet::Client puts detail.backtrace end ensure - Puppet::Storage.store + Puppet::Util::Storage.store end if Puppet[:report] @@ -177,8 +177,8 @@ class Puppet::Client::MasterClient < Puppet::Client # Initialize and load storage def dostorage begin - Puppet::Storage.load - @compile_time ||= Puppet::Storage.cache(:configuration)[:compile_time] + Puppet::Util::Storage.load + @compile_time ||= Puppet::Util::Storage.cache(:configuration)[:compile_time] rescue => detail if Puppet[:trace] puts detail.backtrace @@ -626,7 +626,7 @@ class Puppet::Client::MasterClient < Puppet::Client fromcache = true else @compile_time = Time.now - Puppet::Storage.cache(:configuration)[:compile_time] = @compile_time + Puppet::Util::Storage.cache(:configuration)[:compile_time] = @compile_time end begin diff --git a/lib/puppet/configuration.rb b/lib/puppet/configuration.rb index 952a54397..5b2d90af8 100644 --- a/lib/puppet/configuration.rb +++ b/lib/puppet/configuration.rb @@ -4,7 +4,7 @@ module Puppet # use basedirs that are in the user's home directory. conf = nil var = nil - if self.name != "puppetmasterd" and Puppet::SUIDManager.uid != 0 + if self.name != "puppetmasterd" and Puppet::Util::SUIDManager.uid != 0 conf = File.expand_path("~/.puppet") var = File.expand_path("~/.puppet/var") else diff --git a/lib/puppet/daemon.rb b/lib/puppet/daemon.rb index bd02a9dc3..9fdb33bcf 100755 --- a/lib/puppet/daemon.rb +++ b/lib/puppet/daemon.rb @@ -29,7 +29,7 @@ module Puppet end # Get rid of console logging - Puppet::Log.close(:console) + Puppet::Util::Log.close(:console) Process.setsid Dir.chdir("/") @@ -37,7 +37,7 @@ module Puppet $stdin.reopen "/dev/null" $stdout.reopen "/dev/null", "a" $stderr.reopen $stdout - Puppet::Log.reopen + Puppet::Util::Log.reopen rescue => detail File.open("/tmp/daemonout", "w") { |f| f.puts "Could not start %s: %s" % [Puppet.name, detail] @@ -264,8 +264,8 @@ module Puppet rmpidfile() # And close all logs except the console. - Puppet::Log.destinations.reject { |d| d == :console }.each do |dest| - Puppet::Log.close(dest) + Puppet::Util::Log.destinations.reject { |d| d == :console }.each do |dest| + Puppet::Util::Log.close(dest) end super diff --git a/lib/puppet/dsl.rb b/lib/puppet/dsl.rb index 071a3f6dd..44c03e8a9 100644 --- a/lib/puppet/dsl.rb +++ b/lib/puppet/dsl.rb @@ -95,8 +95,8 @@ module Puppet end Puppet[:user] = Process.uid Puppet[:group] = Process.gid - Puppet::Log.newdestination(:console) - Puppet::Log.level = :info + Puppet::Util::Log.newdestination(:console) + Puppet::Util::Log.level = :info end private diff --git a/lib/puppet/filetype.rb b/lib/puppet/filetype.rb deleted file mode 100755 index d05c1469d..000000000 --- a/lib/puppet/filetype.rb +++ /dev/null @@ -1,302 +0,0 @@ -module Puppet - # Basic classes for reading, writing, and emptying files. Not much - # to see here. - class FileType - attr_accessor :loaded, :path, :synced - - class << self - attr_accessor :name - include Puppet::Util::ClassGen - end - - # Create a new filetype. - def self.newfiletype(name, &block) - @filetypes ||= {} - - klass = genclass(name, - :block => block, - :prefix => "FileType", - :hash => @filetypes - ) - - # Rename the read and write methods, so that we're sure they - # maintain the stats. - klass.class_eval do - # Rename the read method - define_method(:real_read, instance_method(:read)) - define_method(:read) do - begin - val = real_read() - @loaded = Time.now - if val - return val.gsub(/# HEADER.*\n/,'') - else - return "" - end - rescue Puppet::Error => detail - raise - rescue => detail - if Puppet[:trace] - puts detail.backtrace - end - raise Puppet::Error, "%s could not read %s: %s" % - [self.class, @path, detail] - end - end - - # And then the write method - define_method(:real_write, instance_method(:write)) - define_method(:write) do |text| - begin - val = real_write(text) - @synced = Time.now - return val - rescue Puppet::Error => detail - raise - rescue => detail - if Puppet[:debug] - puts detail.backtrace - end - raise Puppet::Error, "%s could not write %s: %s" % - [self.class, @path, detail] - end - end - end - end - - def self.filetype(type) - @filetypes[type] - end - - def initialize(path) - @path = path - end - - # Operate on plain files. - newfiletype(:flat) do - # Read the file. - def read - if File.exists?(@path) - File.read(@path) - else - return nil - end - end - - # Remove the file. - def remove - if File.exists?(@path) - File.unlink(@path) - end - end - - # Overwrite the file. - def write(text) - File.open(@path, "w") { |f| f.print text; f.flush } - end - end - - # Operate on plain files. - newfiletype(:ram) do - @@tabs = {} - - def self.clear - @@tabs.clear - end - - def initialize(path) - super - @@tabs[@path] ||= "" - end - - # Read the file. - def read - Puppet.info "Reading %s from RAM" % @path - @@tabs[@path] - end - - # Remove the file. - def remove - Puppet.info "Removing %s from RAM" % @path - @@tabs[@path] = "" - end - - # Overwrite the file. - def write(text) - Puppet.info "Writing %s to RAM" % @path - @@tabs[@path] = text - end - end - - # Handle Linux-style cron tabs. - newfiletype(:crontab) do - def initialize(user) - self.path = user - end - - def path=(user) - begin - @uid = Puppet::Util.uid(user) - rescue Puppet::Error => detail - raise Puppet::Error, "Could not retrieve user %s" % user - end - - # XXX We have to have the user name, not the uid, because some - # systems *cough*linux*cough* require it that way - @path = user - end - - # Read a specific @path's cron tab. - def read - %x{#{cmdbase()} -l 2>/dev/null} - end - - # Remove a specific @path's cron tab. - def remove - if Facter.value("operatingsystem") == "FreeBSD" - %x{/bin/echo yes | #{cmdbase()} -r 2>/dev/null} - else - %x{#{cmdbase()} -r 2>/dev/null} - end - end - - # Overwrite a specific @path's cron tab; must be passed the @path name - # and the text with which to create the cron tab. - def write(text) - IO.popen("#{cmdbase()} -", "w") { |p| - p.print text - } - end - - private - - # Only add the -u flag when the @path is different. Fedora apparently - # does not think I should be allowed to set the @path to my own user name - def cmdbase - cmd = nil - if @uid == Puppet::SUIDManager.uid - return "crontab" - else - return "crontab -u #{@path}" - end - end - end - - # SunOS has completely different cron commands; this class implements - # its versions. - newfiletype(:suntab) do - # Read a specific @path's cron tab. - def read - Puppet::SUIDManager.asuser(@path) { - %x{crontab -l 2>/dev/null} - } - end - - # Remove a specific @path's cron tab. - def remove - Puppet::SUIDManager.asuser(@path) { - %x{crontab -r 2>/dev/null} - } - end - - # Overwrite a specific @path's cron tab; must be passed the @path name - # and the text with which to create the cron tab. - def write(text) - Puppet::SUIDManager.asuser(@path) { - IO.popen("crontab", "w") { |p| - p.print text - } - } - end - end - - # Treat netinfo tables as a single file, just for simplicity of certain - # types - newfiletype(:netinfo) do - class << self - attr_accessor :format - end - def read - %x{nidump -r /#{@path} /} - end - - # This really only makes sense for cron tabs. - def remove - %x{nireport / /#{@path} name}.split("\n").each do |name| - newname = name.gsub(/\//, '\/').sub(/\s+$/, '') - output = %x{niutil -destroy / '/#{@path}/#{newname}'} - - unless $? == 0 - raise Puppet::Error, "Could not remove %s from %s" % - [name, @path] - end - end - end - - # Convert our table to an array of hashes. This only works for - # handling one table at a time. - def to_array(text = nil) - unless text - text = read - end - - hash = nil - - # Initialize it with the first record - records = [] - text.split("\n").each do |line| - next if line =~ /^[{}]$/ # Skip the wrapping lines - next if line =~ /"name" = \( "#{@path}" \)/ # Skip the table name - next if line =~ /CHILDREN = \(/ # Skip this header - next if line =~ /^ \)/ # and its closer - - # Now we should have nothing but records, wrapped in braces - - case line - when /^\s+\{/: hash = {} - when /^\s+\}/: records << hash - when /\s+"(\w+)" = \( (.+) \)/ - field = $1 - values = $2 - - # Always use an array - hash[field] = [] - - values.split(/, /).each do |value| - if value =~ /^"(.*)"$/ - hash[field] << $1 - else - raise ArgumentError, "Could not match value %s" % value - end - end - else - raise ArgumentError, "Could not match line %s" % line - end - end - - records - end - - def write(text) - text.gsub!(/^#.*\n/,'') - text.gsub!(/^$/,'') - if text == "" or text == "\n" - self.remove - return - end - unless format = self.class.format - raise Puppe::DevError, "You must define the NetInfo format to inport" - end - IO.popen("niload -d #{format} . 1>/dev/null 2>/dev/null", "w") { |p| - p.print text - } - - unless $? == 0 - raise ArgumentError, "Failed to write %s" % @path - end - end - end - end -end - -# $Id$ diff --git a/lib/puppet/inifile.rb b/lib/puppet/inifile.rb deleted file mode 100644 index 3ffd6d137..000000000 --- a/lib/puppet/inifile.rb +++ /dev/null @@ -1,212 +0,0 @@ -# Module Puppet::IniConfig -# A generic way to parse .ini style files and manipulate them in memory -# One 'file' can be made up of several physical files. Changes to sections -# on the file are tracked so that only the physical files in which -# something has changed are written back to disk -# Great care is taken to preserve comments and blank lines from the original -# files -# -# The parsing tries to stay close to python's ConfigParser - -require 'puppet/filetype' - -module Puppet - module IniConfig - - # A section in a .ini file - class Section - attr_reader :name, :file - - def initialize(name, file) - @name = name - @file = file - @dirty = false - @entries = [] - end - - # Has this section been modified since it's been read in - # or written back to disk - def dirty? - @dirty - end - - # Should only be used internally - def mark_clean - @dirty = false - end - - # Add a line of text (e.g., a comment) Such lines - # will be written back out in exactly the same - # place they were read in - def add_line(line) - @entries << line - end - - # Set the entry 'key=value'. If no entry with the - # given key exists, one is appended to teh end of the section - def []=(key, value) - entry = find_entry(key) - @dirty = true - if entry.nil? - @entries << [key, value] - else - entry[1] = value - end - end - - # Return the value associated with KEY. If no such entry - # exists, return nil - def [](key) - entry = find_entry(key) - if entry.nil? - return nil - end - return entry[1] - end - - # Format the section as text in the way it should be - # written to file - def format - text = "[#{name}]\n" - @entries.each do |entry| - if entry.is_a?(Array) - key, value = entry - unless value.nil? - text << "#{key}=#{value}\n" - end - else - text << entry - end - end - return text - end - - private - def find_entry(key) - @entries.each do |entry| - if entry.is_a?(Array) && entry[0] == key - return entry - end - end - return nil - end - - end - - # A logical .ini-file that can be spread across several physical - # files. For each physical file, call #read with the filename - class File - def initialize - @files = {} - end - - # Add the contents of the file with name FILE to the - # already existing sections - def read(file) - text = Puppet::FileType.filetype(:flat).new(file).read - if text.nil? - raise "Could not find #{file}" - end - - section = nil # The name of the current section - optname = nil # The name of the last option in section - line = 0 - @files[file] = [] - text.each_line do |l| - line += 1 - if l.strip.empty? || "#;".include?(l[0,1]) || - (l.split(nil, 2)[0].downcase == "rem" && - l[0,1].downcase == "r") - # Whitespace or comment - if section.nil? - @files[file] << l - else - section.add_line(l) - end - elsif " \t\r\n\f".include?(l[0,1]) && section && optname - # continuation line - section[optname] += "\n" + l.chomp - elsif l =~ /^\[([^\]]+)\]/ - # section heading - section.mark_clean unless section.nil? - section = add_section($1, file) - optname = nil - elsif l =~ /^\s*([^\s=]+)\s*\=(.+)$/ - # We allow space around the keys, but not the values - # For the values, we don't know if space is significant - if section.nil? - raise "#{file}:#{line}:Key/value pair outside of a section for key #{$1}" - else - section[$1] = $2 - optname = $1 - end - else - raise "#{file}:#{line}: Can't parse '#{l.chomp}'" - end - end - section.mark_clean unless section.nil? - end - - # Store all modifications made to sections in this file back - # to the physical files. If no modifications were made to - # a physical file, nothing is written - def store - @files.each do |file, lines| - text = "" - dirty = false - lines.each do |l| - if l.is_a?(Section) - dirty ||= l.dirty? - text << l.format - l.mark_clean - else - text << l - end - end - if dirty - Puppet::FileType.filetype(:flat).new(file).write(text) - end - end - end - - # Execute BLOCK, passing each section in this file - # as an argument - def each_section(&block) - @files.each do |file, list| - list.each do |entry| - if entry.is_a?(Section) - yield(entry) - end - end - end - end - - # Return the Section with the given name or nil - def [](name) - name = name.to_s - each_section do |section| - return section if section.name == name - end - return nil - end - - # Return true if the file contains a section with name NAME - def include?(name) - return ! self[name].nil? - end - - # Add a section to be stored in FILE when store is called - def add_section(name, file) - if include?(name) - raise "A section with name #{name} already exists" - end - result = Section.new(name, file) - @files[file] ||= [] - @files[file] << result - return result - end - end - - - end -end diff --git a/lib/puppet/log.rb b/lib/puppet/log.rb deleted file mode 100644 index 44b4352ce..000000000 --- a/lib/puppet/log.rb +++ /dev/null @@ -1,550 +0,0 @@ -require 'syslog' - -module Puppet - # Pass feedback to the user. Log levels are modeled after syslog's, and it is - # expected that that will be the most common log destination. Supports - # multiple destinations, one of which is a remote server. - class Log - include Puppet::Util - - @levels = [:debug,:info,:notice,:warning,:err,:alert,:emerg,:crit] - @loglevel = 2 - - @desttypes = {} - - # A type of log destination. - class Destination - class << self - attr_accessor :name - end - - def self.initvars - @matches = [] - end - - # Mark the things we're supposed to match. - def self.match(obj) - @matches ||= [] - @matches << obj - end - - # See whether we match a given thing. - def self.match?(obj) - # Convert single-word strings into symbols like :console and :syslog - if obj.is_a? String and obj =~ /^\w+$/ - obj = obj.downcase.intern - end - - @matches.each do |thing| - # Search for direct matches or class matches - return true if thing === obj or thing == obj.class.to_s - end - return false - end - - def name - if defined? @name - return @name - else - return self.class.name - end - end - - # Set how to handle a message. - def self.sethandler(&block) - define_method(:handle, &block) - end - - # Mark how to initialize our object. - def self.setinit(&block) - define_method(:initialize, &block) - end - end - - # Create a new destination type. - def self.newdesttype(name, options = {}, &block) - dest = genclass(name, :parent => Destination, :prefix => "Dest", - :block => block, - :hash => @desttypes, - :attributes => options - ) - dest.match(dest.name) - - return dest - end - - @destinations = {} - - class << self - include Puppet::Util - include Puppet::Util::ClassGen - end - - # Reset all logs to basics. Basically just closes all files and undefs - # all of the other objects. - def Log.close(dest = nil) - if dest - if @destinations.include?(dest) - if @destinations.respond_to?(:close) - @destinations[dest].close - end - @destinations.delete(dest) - end - else - @destinations.each { |name, dest| - if dest.respond_to?(:flush) - dest.flush - end - if dest.respond_to?(:close) - dest.close - end - } - @destinations = {} - end - end - - # Flush any log destinations that support such operations. - def Log.flush - @destinations.each { |type, dest| - if dest.respond_to?(:flush) - dest.flush - end - } - end - - # Create a new log message. The primary role of this method is to - # avoid creating log messages below the loglevel. - def Log.create(hash) - unless hash.include?(:level) - raise Puppet::DevError, "Logs require a level" - end - unless @levels.index(hash[:level]) - raise Puppet::DevError, "Invalid log level %s" % hash[:level] - end - if @levels.index(hash[:level]) >= @loglevel - return Puppet::Log.new(hash) - else - return nil - end - end - - def Log.destinations - return @destinations.keys - end - - # Yield each valid level in turn - def Log.eachlevel - @levels.each { |level| yield level } - end - - # Return the current log level. - def Log.level - return @levels[@loglevel] - end - - # Set the current log level. - def Log.level=(level) - unless level.is_a?(Symbol) - level = level.intern - end - - unless @levels.include?(level) - raise Puppet::DevError, "Invalid loglevel %s" % level - end - - @loglevel = @levels.index(level) - end - - def Log.levels - @levels.dup - end - - newdesttype :syslog do - def close - Syslog.close - end - - def initialize - if Syslog.opened? - Syslog.close - end - name = Puppet.name - name = "puppet-#{name}" unless name =~ /puppet/ - - options = Syslog::LOG_PID | Syslog::LOG_NDELAY - - # XXX This should really be configurable. - str = Puppet[:syslogfacility] - begin - facility = Syslog.const_get("LOG_#{str.upcase}") - rescue NameError - raise Puppet::Error, "Invalid syslog facility %s" % str - end - - @syslog = Syslog.open(name, options, facility) - end - - def handle(msg) - # XXX Syslog currently has a bug that makes it so you - # cannot log a message with a '%' in it. So, we get rid - # of them. - if msg.source == "Puppet" - @syslog.send(msg.level, msg.to_s.gsub("%", '%%')) - else - @syslog.send(msg.level, "(%s) %s" % - [msg.source.to_s.gsub("%", ""), - msg.to_s.gsub("%", '%%') - ] - ) - end - end - end - - newdesttype :file do - match(/^\//) - - def close - if defined? @file - @file.close - @file = nil - end - end - - def flush - if defined? @file - @file.flush - end - end - - def initialize(path) - @name = path - # first make sure the directory exists - # We can't just use 'Config.use' here, because they've - # specified a "special" destination. - unless FileTest.exist?(File.dirname(path)) - Puppet.recmkdir(File.dirname(path)) - Puppet.info "Creating log directory %s" % File.dirname(path) - end - - # create the log file, if it doesn't already exist - file = File.open(path, File::WRONLY|File::CREAT|File::APPEND) - - @file = file - - @autoflush = Puppet[:autoflush] - end - - def handle(msg) - @file.puts("%s %s (%s): %s" % - [msg.time, msg.source, msg.level, msg.to_s]) - - @file.flush if @autoflush - end - end - - newdesttype :console do - - - PINK = {:console => "[0;31m", :html => "FFA0A0"} - GREEN = {:console => "[0;32m", :html => "00CD00"} - YELLOW = {:console => "[0;33m", :html => "FFFF60"} - SLATE = {:console => "[0;34m", :html => "80A0FF"} - ORANGE = {:console => "[0;35m", :html => "FFA500"} - BLUE = {:console => "[0;36m", :html => "40FFFF"} - RESET = {:console => "[0m", :html => ""} - - @@colormap = { - :debug => SLATE, - :info => GREEN, - :notice => PINK, - :warning => ORANGE, - :err => YELLOW, - :alert => BLUE, - :emerg => RESET, - :crit => RESET - } - - def colorize(level, str) - case Puppet[:color] - when false: str - when true, :ansi, "ansi": console_color(level, str) - when :html, "html": html_color(level, str) - end - end - - def console_color(level, str) - @@colormap[level][:console] + str + RESET[:console] - end - - def html_color(level, str) - %{<span style="color: %s">%s</span>} % [@@colormap[level][:html], str] - end - - def initialize - # Flush output immediately. - $stdout.sync = true - end - - def handle(msg) - if msg.source == "Puppet" - puts colorize(msg.level, "%s: %s" % [msg.level, msg.to_s]) - else - puts colorize(msg.level, "%s: %s: %s" % [msg.level, msg.source, msg.to_s]) - end - end - end - - newdesttype :host do - def initialize(host) - Puppet.info "Treating %s as a hostname" % host - args = {} - if host =~ /:(\d+)/ - args[:Port] = $1 - args[:Server] = host.sub(/:\d+/, '') - else - args[:Server] = host - end - - @name = host - - @driver = Puppet::Client::LogClient.new(args) - end - - def handle(msg) - unless msg.is_a?(String) or msg.remote - unless defined? @hostname - @hostname = Facter["hostname"].value - end - unless defined? @domain - @domain = Facter["domain"].value - if @domain - @hostname += "." + @domain - end - end - if msg.source =~ /^\// - msg.source = @hostname + ":" + msg.source - elsif msg.source == "Puppet" - msg.source = @hostname + " " + msg.source - else - msg.source = @hostname + " " + msg.source - end - begin - #puts "would have sent %s" % msg - #puts "would have sent %s" % - # CGI.escape(YAML.dump(msg)) - begin - tmp = CGI.escape(YAML.dump(msg)) - rescue => detail - puts "Could not dump: %s" % detail.to_s - return - end - # Add the hostname to the source - @driver.addlog(tmp) - rescue => detail - if Puppet[:trace] - puts detail.backtrace - end - Puppet.err detail - Puppet::Log.close(self) - end - end - end - end - - # Log to a transaction report. - newdesttype :report do - match "Puppet::Transaction::Report" - - def initialize(report) - @report = report - end - - def handle(msg) - # Only add messages from objects, since anything else is - # probably unrelated to this run. - if msg.objectsource? - @report.newlog(msg) - end - end - end - - # Log to an array, just for testing. - newdesttype :array do - match "Array" - - def initialize(array) - @array = array - end - - def handle(msg) - @array << msg - end - end - - # Create a new log destination. - def Log.newdestination(dest) - # Each destination can only occur once. - if @destinations.find { |name, obj| obj.name == dest } - return - end - - name, type = @desttypes.find do |name, klass| - klass.match?(dest) - end - - unless type - raise Puppet::DevError, "Unknown destination type %s" % dest - end - - begin - if type.instance_method(:initialize).arity == 1 - @destinations[dest] = type.new(dest) - else - @destinations[dest] = type.new() - end - rescue => detail - if Puppet[:debug] - puts detail.backtrace - end - - # If this was our only destination, then add the console back in. - if @destinations.empty? and (dest != :console and dest != "console") - newdestination(:console) - end - end - end - - # Route the actual message. FIXME There are lots of things this method - # should do, like caching, storing messages when there are not yet - # destinations, a bit more. It's worth noting that there's a potential - # for a loop here, if the machine somehow gets the destination set as - # itself. - def Log.newmessage(msg) - if @levels.index(msg.level) < @loglevel - return - end - - @destinations.each do |name, dest| - threadlock(dest) do - dest.handle(msg) - end - end - end - - def Log.sendlevel?(level) - @levels.index(level) >= @loglevel - end - - # Reopen all of our logs. - def Log.reopen - Puppet.notice "Reopening log files" - types = @destinations.keys - @destinations.each { |type, dest| - if dest.respond_to?(:close) - dest.close - end - } - @destinations.clear - # We need to make sure we always end up with some kind of destination - begin - types.each { |type| - Log.newdestination(type) - } - rescue => detail - if @destinations.empty? - Log.newdestination(:syslog) - Puppet.err detail.to_s - end - end - end - - # Is the passed level a valid log level? - def self.validlevel?(level) - @levels.include?(level) - end - - attr_accessor :level, :message, :time, :tags, :remote - attr_reader :source - - def initialize(args) - unless args.include?(:level) && args.include?(:message) - raise Puppet::DevError, "Puppet::Log called incorrectly" - end - - if args[:level].class == String - @level = args[:level].intern - elsif args[:level].class == Symbol - @level = args[:level] - else - raise Puppet::DevError, - "Level is not a string or symbol: #{args[:level].class}" - end - - # Just return unless we're actually at a level we should send - #return unless self.class.sendlevel?(@level) - - @message = args[:message].to_s - @time = Time.now - # this should include the host name, and probly lots of other - # stuff, at some point - unless self.class.validlevel?(level) - raise Puppet::DevError, "Invalid message level #{level}" - end - - if args.include?(:tags) - @tags = args[:tags] - end - - if args.include?(:source) - self.source = args[:source] - else - @source = "Puppet" - end - - Log.newmessage(self) - end - - # Was the source of this log an object? - def objectsource? - if defined? @objectsource and @objectsource - @objectsource - else - false - end - end - - # If they pass a source in to us, we make sure it is a string, and - # we retrieve any tags we can. - def source=(source) - # We can't store the actual source, we just store the path. - # We can't just check for whether it responds to :path, because - # plenty of providers respond to that in their normal function. - if source.is_a?(Puppet::Element) and source.respond_to?(:path) - @objectsource = true - @source = source.path - else - @objectsource = false - @source = source.to_s - end - unless defined? @tags and @tags - if source.respond_to?(:tags) - @tags = source.tags - end - end - end - - def tagged?(tag) - @tags.detect { |t| t.to_s == tag.to_s } - end - - def to_report - "%s %s (%s): %s" % [self.time, self.source, self.level, self.to_s] - end - - def to_s - return @message - end - end -end - -# $Id$ diff --git a/lib/puppet/metatype/manager.rb b/lib/puppet/metatype/manager.rb index 1e2d6db97..1c0194c16 100644 --- a/lib/puppet/metatype/manager.rb +++ b/lib/puppet/metatype/manager.rb @@ -109,7 +109,7 @@ module Manager end # Now set up autoload any providers that might exist for this type. - klass.providerloader = Puppet::Autoload.new(klass, + klass.providerloader = Puppet::Util::Autoload.new(klass, "puppet/provider/#{klass.name.to_s}" ) @@ -156,7 +156,7 @@ module Manager # Create a loader for Puppet types. def typeloader unless defined? @typeloader - @typeloader = Puppet::Autoload.new(self, + @typeloader = Puppet::Util::Autoload.new(self, "puppet/type", :wrap => false ) end diff --git a/lib/puppet/metatype/metaparams.rb b/lib/puppet/metatype/metaparams.rb index ee667bfb3..4c85699ba 100644 --- a/lib/puppet/metatype/metaparams.rb +++ b/lib/puppet/metatype/metaparams.rb @@ -136,7 +136,7 @@ class Puppet::Type syslog (which is currently the default)." defaultto :notice - newvalues(*Puppet::Log.levels) + newvalues(*Puppet::Util::Log.levels) newvalues(:verbose) munge do |loglevel| diff --git a/lib/puppet/metric.rb b/lib/puppet/metric.rb deleted file mode 100644 index 3749d257f..000000000 --- a/lib/puppet/metric.rb +++ /dev/null @@ -1,160 +0,0 @@ -# included so we can test object types -require 'puppet' - -module Puppet - # A class for handling metrics. This is currently ridiculously hackish. - class Metric - Puppet.config.setdefaults("metrics", - :rrddir => {:default => "$vardir/rrd", - :owner => "$user", - :group => "$group", - :desc => "The directory where RRD database files are stored. - Directories for each reporting host will be created under - this directory." - }, - :rrdgraph => [false, "Whether RRD information should be graphed."], - :rrdinterval => ["$runinterval", "How often RRD should expect data. - This should match how often the hosts report back to the server."] - ) - - # Load the library as a feature, so we can test its presence. - Puppet.features.add :rrd, :libs => 'RRD' - - attr_accessor :type, :name, :value, :label - - attr_writer :basedir - - def basedir - if defined? @basedir - @basedir - else - Puppet[:rrddir] - end - end - - def create(start = nil) - Puppet.config.use(:metrics) - - start ||= Time.now.to_i - 5 - - path = self.path - args = [ - path, - "--start", start, - "--step", Puppet[:rrdinterval] - ] - - values.each { |value| - # the 7200 is the heartbeat -- this means that any data that isn't - # more frequently than every two hours gets thrown away - args.push "DS:%s:GAUGE:7200:U:U" % [value[0]] - } - args.push "RRA:AVERAGE:0.5:1:300" - - begin - RRD.create(*args) - rescue => detail - raise "Could not create RRD file %s: %s" % [path,detail] - end - end - - def dump - puts RRD.info(self.path) - end - - def graph(range = nil) - unless Puppet.features.rrd? - Puppet.warning "RRD library is missing; cannot graph metrics" - return - end - - unit = 60 * 60 * 24 - colorstack = %w{#ff0000 #00ff00 #0000ff #099000 #000990 #f00990 #0f0f0f} - - {:daily => unit, :weekly => unit * 7, :monthly => unit * 30, :yearly => unit * 365}.each do |name, time| - file = self.path.sub(/\.rrd$/, "-%s.png" % name) - args = [file] - - args.push("--title",self.label) - args.push("--imgformat","PNG") - args.push("--interlace") - i = 0 - defs = [] - lines = [] - #p @values.collect { |s,l| s } - values.zip(colorstack).each { |value,color| - next if value.nil? - # this actually uses the data label - defs.push("DEF:%s=%s:%s:AVERAGE" % [value[0],self.path,value[0]]) - lines.push("LINE2:%s%s:%s" % [value[0],color,value[1]]) - } - args << defs - args << lines - args.flatten! - if range - args.push("--start",range[0],"--end",range[1]) - else - args.push("--start", Time.now.to_i - time, "--end", Time.now.to_i) - end - - begin - RRD.graph(*args) - rescue => detail - Puppet.err "Failed to graph %s: %s" % [self.name,detail] - end - end - end - - def initialize(name,label = nil) - @name = name.to_s - - if label - @label = label - else - @label = name.to_s.capitalize.gsub("_", " ") - end - - @values = [] - end - - def path - return File.join(self.basedir, @name + ".rrd") - end - - def newvalue(name,value,label = nil) - unless label - label = name.to_s.capitalize.gsub("_", " ") - end - @values.push [name,label,value] - end - - def store(time) - unless Puppet.features.rrd? - Puppet.warning "RRD library is missing; cannot store metrics" - return - end - unless FileTest.exists?(self.path) - self.create(time - 5) - end - - # XXX this is not terribly error-resistant - args = [time] - values.each { |value| - args.push value[2] - } - arg = args.join(":") - begin - RRD.update(self.path,arg) - #system("rrdtool updatev %s '%s'" % [self.path, arg]) - rescue => detail - raise Puppet::Error, "Failed to update %s: %s" % [self.name,detail] - end - end - - def values - @values.sort { |a, b| a[1] <=> b[1] } - end - end -end - -# $Id$ diff --git a/lib/puppet/networkclient.rb b/lib/puppet/networkclient.rb index 8951c3ccb..f082665c0 100644 --- a/lib/puppet/networkclient.rb +++ b/lib/puppet/networkclient.rb @@ -5,7 +5,6 @@ require 'facter' require 'openssl' require 'puppet/transaction' require 'puppet/transportable' -require 'puppet/metric' require 'puppet/daemon' require 'puppet/server' require 'puppet/external/base64' diff --git a/lib/puppet/parameter.rb b/lib/puppet/parameter.rb index 35db58815..d34bc7a9e 100644 --- a/lib/puppet/parameter.rb +++ b/lib/puppet/parameter.rb @@ -284,7 +284,7 @@ class Puppet::Parameter < Puppet::Element self.devfail "Parent %s has no loglevel" % @parent.name end - Puppet::Log.create( + Puppet::Util::Log.create( :level => @parent[:loglevel], :message => msg, :source => self diff --git a/lib/puppet/parser/ast.rb b/lib/puppet/parser/ast.rb index e8cfcc4dd..d8d421c47 100644 --- a/lib/puppet/parser/ast.rb +++ b/lib/puppet/parser/ast.rb @@ -1,7 +1,7 @@ # the parent class for all of our syntactical objects require 'puppet' -require 'puppet/autoload' +require 'puppet/util/autoload' # The base class for all of the objects that make up the parse trees. # Handles things like file name, line #, and also does the initialization @@ -114,7 +114,7 @@ class Puppet::Parser::AST end #--------------------------------------------------------------- # Now autoload everything. - @autoloader = Puppet::Autoload.new(self, + @autoloader = Puppet::Util::Autoload.new(self, "puppet/parser/ast" ) @autoloader.loadall diff --git a/lib/puppet/parser/functions.rb b/lib/puppet/parser/functions.rb index 9fbec43df..47c5ff110 100644 --- a/lib/puppet/parser/functions.rb +++ b/lib/puppet/parser/functions.rb @@ -1,5 +1,5 @@ # Grr -require 'puppet/autoload' +require 'puppet/util/autoload' require 'puppet/parser/scope' module Puppet::Parser @@ -13,7 +13,7 @@ module Functions def self.autoloader unless defined? @autoloader - @autoloader = Puppet::Autoload.new(self, + @autoloader = Puppet::Util::Autoload.new(self, "puppet/parser/functions", :wrap => false ) @@ -181,7 +181,7 @@ module Functions end # Runs a newfunction to create a function for each of the log levels - Puppet::Log.levels.each do |level| + Puppet::Util::Log.levels.each do |level| newfunction(level, :doc => "Log a message on the server at level #{level.to_s}.") do |vals| send(level, vals.join(" ")) diff --git a/lib/puppet/parser/grammar.ra b/lib/puppet/parser/grammar.ra index fe4a08c57..5877439ec 100644 --- a/lib/puppet/parser/grammar.ra +++ b/lib/puppet/parser/grammar.ra @@ -615,7 +615,7 @@ endcomma: # nothing end ---- header ---- require 'puppet' -require 'puppet/loadedfile' +require 'puppet/util/loadedfile' require 'puppet/parser/lexer' require 'puppet/parser/ast' #require 'puppet/parser/interpreter' diff --git a/lib/puppet/parser/interpreter.rb b/lib/puppet/parser/interpreter.rb index 63b107985..3b268c16c 100644 --- a/lib/puppet/parser/interpreter.rb +++ b/lib/puppet/parser/interpreter.rb @@ -611,8 +611,8 @@ class Puppet::Parser::Interpreter # reparsed. def newfile(*files) files.each do |file| - unless file.is_a? Puppet::LoadedFile - file = Puppet::LoadedFile.new(file) + unless file.is_a? Puppet::Util::LoadedFile + file = Puppet::Util::LoadedFile.new(file) end @files << file end diff --git a/lib/puppet/parser/parser.rb b/lib/puppet/parser/parser.rb index 414405302..94d4f7fad 100644 --- a/lib/puppet/parser/parser.rb +++ b/lib/puppet/parser/parser.rb @@ -8,7 +8,7 @@ require 'racc/parser' require 'puppet' -require 'puppet/loadedfile' +require 'puppet/util/loadedfile' require 'puppet/parser/lexer' require 'puppet/parser/ast' #require 'puppet/parser/interpreter' @@ -29,7 +29,7 @@ module Puppet class Parser < Racc::Parser -module_eval <<'..end grammar.ra modeval..id0df015d0a0', 'grammar.ra', 633 +module_eval <<'..end grammar.ra modeval..id05c09a2dac', 'grammar.ra', 633 require 'puppet/parser/functions' attr_reader :file, :interp @@ -101,7 +101,7 @@ def file=(file) if @files.detect { |f| f.file == file } raise Puppet::ImportError.new("Import loop detected") else - @files << Puppet::LoadedFile.new(file) + @files << Puppet::Util::LoadedFile.new(file) @lexer.file = file end end @@ -199,7 +199,7 @@ end # $Id$ -..end grammar.ra modeval..id0df015d0a0 +..end grammar.ra modeval..id05c09a2dac ##### racc 1.4.5 generates ### diff --git a/lib/puppet/provider/parsedfile.rb b/lib/puppet/provider/parsedfile.rb index a685c7250..f1b77e56b 100755 --- a/lib/puppet/provider/parsedfile.rb +++ b/lib/puppet/provider/parsedfile.rb @@ -1,5 +1,5 @@ require 'puppet' -require 'puppet/filetype' +require 'puppet/util/filetype' require 'puppet/util/fileparsing' # This provider can be used as the parent class for a provider that @@ -37,7 +37,7 @@ class Puppet::Provider::ParsedFile < Puppet::Provider def self.filetype unless defined? @filetype - @filetype = Puppet::FileType.filetype(:flat) + @filetype = Puppet::Util::FileType.filetype(:flat) end return @filetype end @@ -45,7 +45,7 @@ class Puppet::Provider::ParsedFile < Puppet::Provider def self.filetype=(type) if type.is_a?(Class) @filetype = type - elsif klass = Puppet::FileType.filetype(type) + elsif klass = Puppet::Util::FileType.filetype(type) @filetype = klass else raise ArgumentError, "Invalid filetype %s" % type @@ -101,7 +101,7 @@ class Puppet::Provider::ParsedFile < Puppet::Provider @target = nil # Default to flat files - @filetype = Puppet::FileType.filetype(:flat) + @filetype = Puppet::Util::FileType.filetype(:flat) super end diff --git a/lib/puppet/reports/log.rb b/lib/puppet/reports/log.rb index 4df832980..614a07c7a 100644 --- a/lib/puppet/reports/log.rb +++ b/lib/puppet/reports/log.rb @@ -5,7 +5,7 @@ Puppet::Server::Report.newreport(:log) do def process self.logs.each do |log| - Puppet::Log.newmessage(log) + Puppet::Util::Log.newmessage(log) end end end diff --git a/lib/puppet/reports/rrdgraph.rb b/lib/puppet/reports/rrdgraph.rb index 86f54bf5d..ef353a1b9 100644 --- a/lib/puppet/reports/rrdgraph.rb +++ b/lib/puppet/reports/rrdgraph.rb @@ -96,7 +96,7 @@ Puppet::Server::Report.newreport(:rrdgraph) do unless File.directory?(hostdir) # Some hackishness to create the dir - config = Puppet::Config.new + config = Puppet::Util::Config.new config.setdefaults(:reports, :hostdir => [hostdir, "eh"]) # This creates the dir. diff --git a/lib/puppet/reports/store.rb b/lib/puppet/reports/store.rb index cb0aa0a3c..23bbc037e 100644 --- a/lib/puppet/reports/store.rb +++ b/lib/puppet/reports/store.rb @@ -11,7 +11,7 @@ Puppet::Server::Report.newreport(:store, :useyaml => true) do default report)." def mkclientdir(client, dir) - config = Puppet::Config.new + config = Puppet::Util::Config.new config.setdefaults("reportclient-#{client}", "clientdir-#{client}" => { :default => dir, :mode => 0750, diff --git a/lib/puppet/server/authconfig.rb b/lib/puppet/server/authconfig.rb index 073ff9516..d43371a77 100644 --- a/lib/puppet/server/authconfig.rb +++ b/lib/puppet/server/authconfig.rb @@ -1,4 +1,4 @@ -require 'puppet/loadedfile' +require 'puppet/util/loadedfile' require 'puppet/server/rights' module Puppet @@ -6,7 +6,7 @@ class Server class ConfigurationError < Puppet::Error; end -class AuthConfig < Puppet::LoadedFile +class AuthConfig < Puppet::Util::LoadedFile Puppet.config.setdefaults(:puppet, :authconfig => [ "$confdir/namespaceauth.conf", "The configuration file that defines the rights to the different diff --git a/lib/puppet/server/fileserver.rb b/lib/puppet/server/fileserver.rb index b541f2474..3ea44d785 100755 --- a/lib/puppet/server/fileserver.rb +++ b/lib/puppet/server/fileserver.rb @@ -75,7 +75,7 @@ class Server if hash[:Config] == false @noreadconfig = true else - @config = Puppet::LoadedFile.new( + @config = Puppet::Util::LoadedFile.new( hash[:Config] || Puppet[:fileserverconfig] ) @noreadconfig = false diff --git a/lib/puppet/server/logger.rb b/lib/puppet/server/logger.rb index 2170a5772..aa3521573 100755 --- a/lib/puppet/server/logger.rb +++ b/lib/puppet/server/logger.rb @@ -42,7 +42,7 @@ class Server end end - Puppet::Log.newmessage(message) + Puppet::Util::Log.newmessage(message) # This is necessary or XMLRPC gets all pukey return "" diff --git a/lib/puppet/server/report.rb b/lib/puppet/server/report.rb index 3c325041d..4298f8ee6 100755 --- a/lib/puppet/server/report.rb +++ b/lib/puppet/server/report.rb @@ -39,7 +39,7 @@ class Server ) @reports = {} - @reportloader = Puppet::Autoload.new(self, "puppet/reports") + @reportloader = Puppet::Util::Autoload.new(self, "puppet/reports") class << self attr_reader :hooks diff --git a/lib/puppet/sslcertificates/ca.rb b/lib/puppet/sslcertificates/ca.rb index 13b845472..0d5678b7a 100644 --- a/lib/puppet/sslcertificates/ca.rb +++ b/lib/puppet/sslcertificates/ca.rb @@ -237,7 +237,7 @@ class Puppet::SSLCertificates::CA ) # This creates the cakey file - Puppet::SUIDManager.asuser(Puppet[:user], Puppet[:group]) do + Puppet::Util::SUIDManager.asuser(Puppet[:user], Puppet[:group]) do @cert = cert.mkselfsigned end Puppet.config.write(:cacert) do |f| diff --git a/lib/puppet/storage.rb b/lib/puppet/storage.rb deleted file mode 100644 index ebebb8ee5..000000000 --- a/lib/puppet/storage.rb +++ /dev/null @@ -1,106 +0,0 @@ -require 'yaml' -require 'sync' -#require 'puppet/lockfile' - -module Puppet - # a class for storing state - class Storage - include Singleton - include Puppet::Util - - def initialize - self.class.load - end - - # Return a hash that will be stored to disk. It's worth noting - # here that we use the object's full path, not just the name/type - # combination. At the least, this is useful for those non-isomorphic - # types like exec, but it also means that if an object changes locations - # in the configuration it will lose its cache. - def self.cache(object) - if object.is_a? Puppet::Type - # We used to store things by path, now we store them by ref. - # In oscar(0.20.0) this changed to using the ref. - if @@state.include?(object.path) - @@state[object.ref] = @@state[object.path] - @@state.delete(object.path) - end - name = object.ref - elsif object.is_a?(Symbol) - name = object - else - raise ArgumentError, "You can only cache information for Types and symbols" - end - - return @@state[name] ||= {} - end - - def self.clear - @@state.clear - Storage.init - end - - def self.init - @@state = {} - @@splitchar = "\t" - end - - self.init - - def self.load - Puppet.config.use(:puppet) - - unless File.exists?(Puppet[:statefile]) - unless defined? @@state and ! @@state.nil? - self.init - end - return - end - Puppet::Util.benchmark(:debug, "Loaded state") do - Puppet::Util.readlock(Puppet[:statefile]) do |file| - begin - @@state = YAML.load(file) - rescue => detail - Puppet.err "Checksumfile %s is corrupt (%s); replacing" % - [Puppet[:statefile], detail] - begin - File.rename(Puppet[:statefile], - Puppet[:statefile] + ".bad") - rescue - raise Puppet::Error, - "Could not rename corrupt %s; remove manually" % - Puppet[:statefile] - end - end - end - end - - unless @@state.is_a?(Hash) - Puppet.err "State got corrupted" - self.init - end - - #Puppet.debug "Loaded state is %s" % @@state.inspect - end - - def self.stateinspect - @@state.inspect - end - - def self.store - Puppet.debug "Storing state" - - unless FileTest.exist?(Puppet[:statefile]) - Puppet.info "Creating state file %s" % Puppet[:statefile] - end - - Puppet::Util.benchmark(:debug, "Stored state") do - Puppet::Util.writelock(Puppet[:statefile], 0660) do |file| - file.print YAML.dump(@@state) - end - end - end - end -end - -# $Id$ diff --git a/lib/puppet/suidmanager.rb b/lib/puppet/suidmanager.rb deleted file mode 100644 index 659ffaab8..000000000 --- a/lib/puppet/suidmanager.rb +++ /dev/null @@ -1,88 +0,0 @@ -require 'facter' -require 'puppet/util/warnings' - -module Puppet - module SUIDManager - include Puppet::Util::Warnings - - platform = Facter["kernel"].value - [:uid=, :gid=, :uid, :gid].each do |method| - define_method(method) do |*args| - # NOTE: 'method' is closed here. - newmethod = method - - if platform == "Darwin" and (method == :uid= or method == :gid=) - Puppet::Util::Warnings.warnonce "Cannot change real UID on Darwin" - newmethod = ("e" + method.to_s).intern - end - - return Process.send(newmethod, *args) - end - module_function method - end - - [:euid=, :euid, :egid=, :egid].each do |method| - define_method(method) do |*args| - Process.send(method, *args) - end - module_function method - end - - def asuser(new_euid=nil, new_egid=nil) - # Unless we're root, don't do a damn thing. - unless Process.uid == 0 - return yield - end - old_egid = old_euid = nil - if new_egid - old_egid = self.egid - self.egid = convert_xid(:gid, new_egid) - end - if new_euid - old_euid = self.euid - self.euid = convert_xid(:uid, new_euid) - end - - return yield - ensure - self.euid = old_euid if old_euid - self.egid = old_egid if old_egid - end - - # Make sure the passed argument is a number. - def convert_xid(type, id) - map = {:gid => :group, :uid => :user} - raise ArgumentError, "Invalid id type %s" % type unless map.include?(type) - ret = Puppet::Util.send(type, id) - if ret == nil - raise Puppet::Error, "Invalid %s: %s" % [map[type], id] - end - return ret - end - - module_function :asuser, :convert_xid - - def run_and_capture(command, new_uid=nil, new_gid=nil) - output = nil - - output = Puppet::Util.execute(command, false, new_uid, new_gid) - - [output, $?.dup] - end - - module_function :run_and_capture - - def system(command, new_uid=nil, new_gid=nil) - status = nil - asuser(new_uid, new_gid) do - Kernel.system(command) - status = $?.dup - end - status - end - - module_function :system - end -end - -# $Id$ diff --git a/lib/puppet/transaction.rb b/lib/puppet/transaction.rb index 3c8a3a2e8..43ee135a6 100644 --- a/lib/puppet/transaction.rb +++ b/lib/puppet/transaction.rb @@ -292,7 +292,7 @@ class Transaction graph(@resources, :resources) # Start logging. - Puppet::Log.newdestination(@report) + Puppet::Util::Log.newdestination(@report) prepare() @@ -314,7 +314,7 @@ class Transaction }.flatten.reject { |e| e.nil? } ensure # And then close the transaction log. - Puppet::Log.close(@report) + Puppet::Util::Log.close(@report) end Puppet.debug "Finishing transaction %s with %s changes" % diff --git a/lib/puppet/transaction/report.rb b/lib/puppet/transaction/report.rb index 769866ef0..e14135140 100644 --- a/lib/puppet/transaction/report.rb +++ b/lib/puppet/transaction/report.rb @@ -25,7 +25,7 @@ class Puppet::Transaction::Report # Create a new metric. def newmetric(name, hash) - metric = Puppet::Metric.new(name) + metric = Puppet::Util::Metric.new(name) hash.each do |name, value| metric.newvalue(name, value) diff --git a/lib/puppet/type.rb b/lib/puppet/type.rb index fd4704ae5..023f85ab3 100644 --- a/lib/puppet/type.rb +++ b/lib/puppet/type.rb @@ -1,12 +1,12 @@ require 'puppet' -require 'puppet/log' +require 'puppet/util/log' require 'puppet/element' require 'puppet/event' -require 'puppet/metric' +require 'puppet/util/metric' require 'puppet/type/property' require 'puppet/parameter' require 'puppet/util' -require 'puppet/autoload' +require 'puppet/util/autoload' require 'puppet/metatype/manager' # see the bottom of the file for the rest of the inclusions @@ -141,7 +141,7 @@ class Type < Puppet::Element # create a log at specified level def log(msg) - Puppet::Log.create( + Puppet::Util::Log.create( :level => @parameters[:loglevel].value, :message => msg, :source => self @@ -299,13 +299,13 @@ class Type < Puppet::Element # Return a cached value def cached(name) - Puppet::Storage.cache(self)[name] + Puppet::Util::Storage.cache(self)[name] #@cache[name] ||= nil end # Cache a value def cache(name, value) - Puppet::Storage.cache(self)[name] = value + Puppet::Util::Storage.cache(self)[name] = value #@cache[name] = value end diff --git a/lib/puppet/type/cron.rb b/lib/puppet/type/cron.rb index 9c991886e..a91af13ed 100755 --- a/lib/puppet/type/cron.rb +++ b/lib/puppet/type/cron.rb @@ -1,7 +1,7 @@ require 'etc' require 'facter' require 'puppet/type/property' -require 'puppet/filetype' +require 'puppet/util/filetype' require 'puppet/type/parsedtype' module Puppet @@ -433,9 +433,9 @@ module Puppet def self.defaulttype case Facter["operatingsystem"].value when "Solaris": - return Puppet::FileType.filetype(:suntab) + return Puppet::Util::FileType.filetype(:suntab) else - return Puppet::FileType.filetype(:crontab) + return Puppet::Util::FileType.filetype(:crontab) end end diff --git a/lib/puppet/type/exec.rb b/lib/puppet/type/exec.rb index 9dfa2b05e..33796b129 100755 --- a/lib/puppet/type/exec.rb +++ b/lib/puppet/type/exec.rb @@ -195,7 +195,7 @@ module Puppet # Most validation is handled by the SUIDManager class. validate do |user| - unless Puppet::SUIDManager.uid == 0 + unless Puppet::Util::SUIDManager.uid == 0 self.fail "Only root can execute commands as other users" end end @@ -235,7 +235,7 @@ module Puppet values = [:true, :false] # And all of the log levels - Puppet::Log.eachlevel { |level| values << level } + Puppet::Util::Log.eachlevel { |level| values << level } newvalues(*values) end @@ -473,9 +473,12 @@ module Puppet self.collect { |i| i } end - # Verify that we pass all of the checks. - def check + # Verify that we pass all of the checks. The argument determines whether + # we skip the :refreshonly check, which is necessary because we now check + # within refresh() + def check(refreshing = false) self.class.checks.each { |check| + next if refreshing and check == :refreshonly if @parameters.include?(check) val = @parameters[check].value val = [val] unless val.is_a? Array @@ -498,9 +501,9 @@ module Puppet end end - # this might be a very, very bad idea... + # Run the command, or optionally run a separately-specified command. def refresh - if self.check + if self.check(true) if cmd = self[:refresh] self.run(cmd) else @@ -563,7 +566,7 @@ module Puppet withenv env do Timeout::timeout(self[:timeout]) do - output, status = Puppet::SUIDManager.run_and_capture( + output, status = Puppet::Util::SUIDManager.run_and_capture( [command], self[:user], self[:group] ) end diff --git a/lib/puppet/type/parsedtype.rb b/lib/puppet/type/parsedtype.rb index 1232c979e..40a90d5ae 100755 --- a/lib/puppet/type/parsedtype.rb +++ b/lib/puppet/type/parsedtype.rb @@ -1,6 +1,6 @@ require 'etc' require 'facter' -require 'puppet/filetype' +require 'puppet/util/filetype' require 'puppet/type/property' module Puppet diff --git a/lib/puppet/type/pfile.rb b/lib/puppet/type/pfile.rb index 478978623..89e94d140 100644 --- a/lib/puppet/type/pfile.rb +++ b/lib/puppet/type/pfile.rb @@ -270,7 +270,7 @@ module Puppet # Determine the user to write files as. def asuser if self.should(:owner) and ! self.should(:owner).is_a?(Symbol) - writeable = Puppet::SUIDManager.asuser(self.should(:owner)) { + writeable = Puppet::Util::SUIDManager.asuser(self.should(:owner)) { FileTest.writable?(File.dirname(self[:path])) } @@ -1058,7 +1058,7 @@ module Puppet yield # We're getting different behaviors from different versions of ruby, so... # asroot = true - # Puppet::SUIDManager.asuser(asuser(), self.should(:group)) do + # Puppet::Util::SUIDManager.asuser(asuser(), self.should(:group)) do # if FileTest.writable?(dir) # asroot = false # yield diff --git a/lib/puppet/type/pfile/owner.rb b/lib/puppet/type/pfile/owner.rb index 908f25d0f..07a2b880b 100755 --- a/lib/puppet/type/pfile/owner.rb +++ b/lib/puppet/type/pfile/owner.rb @@ -121,7 +121,7 @@ module Puppet end def sync - unless Puppet::SUIDManager.uid == 0 + unless Puppet::Util::SUIDManager.uid == 0 unless defined? @@notifieduid self.notice "Cannot manage ownership unless running as root" #@parent.delete(self.name) diff --git a/lib/puppet/type/pfile/source.rb b/lib/puppet/type/pfile/source.rb index 12bb7003e..d06366a79 100755 --- a/lib/puppet/type/pfile/source.rb +++ b/lib/puppet/type/pfile/source.rb @@ -105,7 +105,7 @@ module Puppet } # we can't manage ownership as root, so don't even try - unless Puppet::SUIDManager.uid == 0 + unless Puppet::Util::SUIDManager.uid == 0 args.delete(:owner) end diff --git a/lib/puppet/type/pfile/target.rb b/lib/puppet/type/pfile/target.rb index f6ae0f9c8..3de74bb74 100644 --- a/lib/puppet/type/pfile/target.rb +++ b/lib/puppet/type/pfile/target.rb @@ -28,7 +28,7 @@ module Puppet @parent.remove_existing(target) Dir.chdir(File.dirname(@parent[:path])) do - Puppet::SUIDManager.asuser(@parent.asuser()) do + Puppet::Util::SUIDManager.asuser(@parent.asuser()) do mode = @parent.should(:mode) if mode Puppet::Util.withumask(000) do diff --git a/lib/puppet/type/property.rb b/lib/puppet/type/property.rb index 7279ffee7..b4dcdfae0 100644 --- a/lib/puppet/type/property.rb +++ b/lib/puppet/type/property.rb @@ -281,7 +281,7 @@ class Property < Puppet::Parameter self.devfail "Parent %s has no loglevel" % @parent.name end - Puppet::Log.create( + Puppet::Util::Log.create( :level => @parent[:loglevel], :message => msg, :source => self diff --git a/lib/puppet/type/yumrepo.rb b/lib/puppet/type/yumrepo.rb index b7a1ff5e7..94ba3ef6c 100644 --- a/lib/puppet/type/yumrepo.rb +++ b/lib/puppet/type/yumrepo.rb @@ -1,7 +1,7 @@ # Description of yum repositories require 'puppet/propertychange' -require 'puppet/inifile' +require 'puppet/util/inifile' require 'puppet/type/parsedtype' module Puppet @@ -72,7 +72,7 @@ module Puppet attr_accessor :yumconf end - self.filetype = Puppet::FileType.filetype(:flat) + self.filetype = Puppet::Util::FileType.filetype(:flat) @inifile = nil @@ -102,7 +102,7 @@ module Puppet l end - # Return the Puppet::IniConfig::File for the whole yum config + # Return the Puppet::Util::IniConfig::File for the whole yum config def self.inifile if @inifile.nil? @inifile = read() @@ -134,7 +134,7 @@ module Puppet # Non-test code should use self.inifile to get at the # underlying file def self.read - result = Puppet::IniConfig::File.new() + result = Puppet::Util::IniConfig::File.new() result.read(yumconf) main = result['main'] if main.nil? @@ -161,7 +161,7 @@ module Puppet return result end - # Return the Puppet::IniConfig::Section with name NAME + # Return the Puppet::Util::IniConfig::Section with name NAME # from the yum config def self.section(name) result = inifile[name] @@ -189,7 +189,7 @@ module Puppet super end - # Return the Puppet::IniConfig::Section for this yumrepo element + # Return the Puppet::Util::IniConfig::Section for this yumrepo element def section self.class.section(self[:name]) end diff --git a/lib/puppet/util.rb b/lib/puppet/util.rb index 2bd3035c6..3bd7d277c 100644 --- a/lib/puppet/util.rb +++ b/lib/puppet/util.rb @@ -33,10 +33,10 @@ module Util unless group raise Puppet::Error, "No such group %s" % Puppet[:group] end - unless Puppet::SUIDManager.gid == group + unless Puppet::Util::SUIDManager.gid == group begin - Puppet::SUIDManager.egid = group - Puppet::SUIDManager.gid = group + Puppet::Util::SUIDManager.egid = group + Puppet::Util::SUIDManager.gid = group rescue => detail Puppet.warning "could not change to group %s: %s" % [group.inspect, detail] @@ -54,10 +54,10 @@ module Util unless user raise Puppet::Error, "No such user %s" % Puppet[:user] end - unless Puppet::SUIDManager.uid == user + unless Puppet::Util::SUIDManager.uid == user begin - Puppet::SUIDManager.uid = user - Puppet::SUIDManager.euid = user + Puppet::Util::SUIDManager.uid = user + Puppet::Util::SUIDManager.euid = user rescue $stderr.puts "could not change to user %s" % user exit(74) @@ -104,19 +104,19 @@ module Util # the messages to be a little richer. Most classes will be calling this # method. def self.logmethods(klass, useself = true) - Puppet::Log.eachlevel { |level| + Puppet::Util::Log.eachlevel { |level| klass.send(:define_method, level, proc { |args| if args.is_a?(Array) args = args.join(" ") end if useself - Puppet::Log.create( + Puppet::Util::Log.create( :level => level, :source => self, :message => args ) else - Puppet::Log.create( + Puppet::Util::Log.create( :level => level, :message => args ) @@ -206,7 +206,7 @@ module Util end # Only benchmark if our log level is high enough - if level != :none and Puppet::Log.sendlevel?(level) + if level != :none and Puppet::Util::Log.sendlevel?(level) result = nil seconds = Benchmark.realtime { yield @@ -285,10 +285,10 @@ module Util end if uid - uid = Puppet::SUIDManager.convert_xid(:uid, uid) + uid = Puppet::Util::SUIDManager.convert_xid(:uid, uid) end if gid - gid = Puppet::SUIDManager.convert_xid(:gid, gid) + gid = Puppet::Util::SUIDManager.convert_xid(:gid, gid) end @@os ||= Facter.value(:operatingsystem) diff --git a/lib/puppet/autoload.rb b/lib/puppet/util/autoload.rb index 862aabbff..f171254af 100644 --- a/lib/puppet/autoload.rb +++ b/lib/puppet/util/autoload.rb @@ -1,5 +1,5 @@ # Autoload paths, either based on names or all at once. -class Puppet::Autoload +class Puppet::Util::Autoload include Puppet::Util @autoloaders = {} diff --git a/lib/puppet/config.rb b/lib/puppet/util/config.rb index 77838fec0..097d59b9f 100644 --- a/lib/puppet/config.rb +++ b/lib/puppet/util/config.rb @@ -2,9 +2,8 @@ require 'puppet' require 'sync' require 'puppet/transportable' -module Puppet # The class for handling configuration files. -class Config +class Puppet::Util::Config include Enumerable include Puppet::Util @@ -216,7 +215,7 @@ class Config raise ArgumentError, "Default %s is not a file" % default end - Puppet::SUIDManager.asuser(obj.owner, obj.group) do + Puppet::Util::SUIDManager.asuser(obj.owner, obj.group) do mode = obj.mode || 0750 Dir.mkdir(obj.value, mode) end @@ -248,10 +247,10 @@ class Config def parse(file) text = nil - if file.is_a? Puppet::LoadedFile + if file.is_a? Puppet::Util::LoadedFile @file = file else - @file = Puppet::LoadedFile.new(file) + @file = Puppet::Util::LoadedFile.new(file) end # Create a timer so that this. @@ -430,7 +429,7 @@ class Config newobj[:comment] ||= "%s user" % name end else - newobj = TransObject.new(name, type.to_s) + newobj = Puppet::TransObject.new(name, type.to_s) newobj.tags = ["puppet", "configuration", section] newobj[:ensure] = "present" if type == :user @@ -643,12 +642,12 @@ Generated on #{Time.now}. end chown = nil - if Puppet::SUIDManager.uid == 0 + if Puppet::Util::SUIDManager.uid == 0 chown = [obj.owner, obj.group] else chown = [nil, nil] end - Puppet::SUIDManager.asuser(*chown) do + Puppet::Util::SUIDManager.asuser(*chown) do mode = obj.mode || 0640 if args.empty? @@ -676,13 +675,13 @@ Generated on #{Time.now}. end chown = nil - if Puppet::SUIDManager.uid == 0 + if Puppet::Util::SUIDManager.uid == 0 chown = [obj.owner, obj.group] else chown = [nil, nil] end - Puppet::SUIDManager.asuser(*chown) do + Puppet::Util::SUIDManager.asuser(*chown) do mode = obj.mode || 0640 if args.empty? args << "w" @@ -906,7 +905,7 @@ Generated on #{Time.now}. } # Only chown or chgrp when root - if Puppet::SUIDManager.uid == 0 + if Puppet::Util::SUIDManager.uid == 0 [:group, :owner].each { |var| if value = self.send(var) obj[var] = value @@ -956,6 +955,5 @@ Generated on #{Time.now}. end end end -end # $Id$ diff --git a/lib/puppet/feature.rb b/lib/puppet/util/feature.rb index ece8ec86a..30c38e286 100644 --- a/lib/puppet/feature.rb +++ b/lib/puppet/util/feature.rb @@ -1,7 +1,7 @@ # Created by Luke Kanies on 2006-11-07. # Copyright (c) 2006. All rights reserved. -class Puppet::Feature +class Puppet::Util::Feature attr_reader :path # Create a new feature test. You have to pass the feature name, @@ -52,7 +52,7 @@ class Puppet::Feature # Create a new feature collection. def initialize(path) @path = path - @loader = Puppet::Autoload.new(self, @path) + @loader = Puppet::Util::Autoload.new(self, @path) end def load diff --git a/lib/puppet/util/filetype.rb b/lib/puppet/util/filetype.rb new file mode 100755 index 000000000..8abe0cc00 --- /dev/null +++ b/lib/puppet/util/filetype.rb @@ -0,0 +1,300 @@ +# Basic classes for reading, writing, and emptying files. Not much +# to see here. +class Puppet::Util::FileType + attr_accessor :loaded, :path, :synced + + class << self + attr_accessor :name + include Puppet::Util::ClassGen + end + + # Create a new filetype. + def self.newfiletype(name, &block) + @filetypes ||= {} + + klass = genclass(name, + :block => block, + :prefix => "FileType", + :hash => @filetypes + ) + + # Rename the read and write methods, so that we're sure they + # maintain the stats. + klass.class_eval do + # Rename the read method + define_method(:real_read, instance_method(:read)) + define_method(:read) do + begin + val = real_read() + @loaded = Time.now + if val + return val.gsub(/# HEADER.*\n/,'') + else + return "" + end + rescue Puppet::Error => detail + raise + rescue => detail + if Puppet[:trace] + puts detail.backtrace + end + raise Puppet::Error, "%s could not read %s: %s" % + [self.class, @path, detail] + end + end + + # And then the write method + define_method(:real_write, instance_method(:write)) + define_method(:write) do |text| + begin + val = real_write(text) + @synced = Time.now + return val + rescue Puppet::Error => detail + raise + rescue => detail + if Puppet[:debug] + puts detail.backtrace + end + raise Puppet::Error, "%s could not write %s: %s" % + [self.class, @path, detail] + end + end + end + end + + def self.filetype(type) + @filetypes[type] + end + + def initialize(path) + @path = path + end + + # Operate on plain files. + newfiletype(:flat) do + # Read the file. + def read + if File.exists?(@path) + File.read(@path) + else + return nil + end + end + + # Remove the file. + def remove + if File.exists?(@path) + File.unlink(@path) + end + end + + # Overwrite the file. + def write(text) + File.open(@path, "w") { |f| f.print text; f.flush } + end + end + + # Operate on plain files. + newfiletype(:ram) do + @@tabs = {} + + def self.clear + @@tabs.clear + end + + def initialize(path) + super + @@tabs[@path] ||= "" + end + + # Read the file. + def read + Puppet.info "Reading %s from RAM" % @path + @@tabs[@path] + end + + # Remove the file. + def remove + Puppet.info "Removing %s from RAM" % @path + @@tabs[@path] = "" + end + + # Overwrite the file. + def write(text) + Puppet.info "Writing %s to RAM" % @path + @@tabs[@path] = text + end + end + + # Handle Linux-style cron tabs. + newfiletype(:crontab) do + def initialize(user) + self.path = user + end + + def path=(user) + begin + @uid = Puppet::Util.uid(user) + rescue Puppet::Error => detail + raise Puppet::Error, "Could not retrieve user %s" % user + end + + # XXX We have to have the user name, not the uid, because some + # systems *cough*linux*cough* require it that way + @path = user + end + + # Read a specific @path's cron tab. + def read + %x{#{cmdbase()} -l 2>/dev/null} + end + + # Remove a specific @path's cron tab. + def remove + if Facter.value("operatingsystem") == "FreeBSD" + %x{/bin/echo yes | #{cmdbase()} -r 2>/dev/null} + else + %x{#{cmdbase()} -r 2>/dev/null} + end + end + + # Overwrite a specific @path's cron tab; must be passed the @path name + # and the text with which to create the cron tab. + def write(text) + IO.popen("#{cmdbase()} -", "w") { |p| + p.print text + } + end + + private + + # Only add the -u flag when the @path is different. Fedora apparently + # does not think I should be allowed to set the @path to my own user name + def cmdbase + cmd = nil + if @uid == Puppet::Util::SUIDManager.uid + return "crontab" + else + return "crontab -u #{@path}" + end + end + end + + # SunOS has completely different cron commands; this class implements + # its versions. + newfiletype(:suntab) do + # Read a specific @path's cron tab. + def read + Puppet::Util::SUIDManager.asuser(@path) { + %x{crontab -l 2>/dev/null} + } + end + + # Remove a specific @path's cron tab. + def remove + Puppet::Util::SUIDManager.asuser(@path) { + %x{crontab -r 2>/dev/null} + } + end + + # Overwrite a specific @path's cron tab; must be passed the @path name + # and the text with which to create the cron tab. + def write(text) + Puppet::Util::SUIDManager.asuser(@path) { + IO.popen("crontab", "w") { |p| + p.print text + } + } + end + end + + # Treat netinfo tables as a single file, just for simplicity of certain + # types + newfiletype(:netinfo) do + class << self + attr_accessor :format + end + def read + %x{nidump -r /#{@path} /} + end + + # This really only makes sense for cron tabs. + def remove + %x{nireport / /#{@path} name}.split("\n").each do |name| + newname = name.gsub(/\//, '\/').sub(/\s+$/, '') + output = %x{niutil -destroy / '/#{@path}/#{newname}'} + + unless $? == 0 + raise Puppet::Error, "Could not remove %s from %s" % + [name, @path] + end + end + end + + # Convert our table to an array of hashes. This only works for + # handling one table at a time. + def to_array(text = nil) + unless text + text = read + end + + hash = nil + + # Initialize it with the first record + records = [] + text.split("\n").each do |line| + next if line =~ /^[{}]$/ # Skip the wrapping lines + next if line =~ /"name" = \( "#{@path}" \)/ # Skip the table name + next if line =~ /CHILDREN = \(/ # Skip this header + next if line =~ /^ \)/ # and its closer + + # Now we should have nothing but records, wrapped in braces + + case line + when /^\s+\{/: hash = {} + when /^\s+\}/: records << hash + when /\s+"(\w+)" = \( (.+) \)/ + field = $1 + values = $2 + + # Always use an array + hash[field] = [] + + values.split(/, /).each do |value| + if value =~ /^"(.*)"$/ + hash[field] << $1 + else + raise ArgumentError, "Could not match value %s" % value + end + end + else + raise ArgumentError, "Could not match line %s" % line + end + end + + records + end + + def write(text) + text.gsub!(/^#.*\n/,'') + text.gsub!(/^$/,'') + if text == "" or text == "\n" + self.remove + return + end + unless format = self.class.format + raise Puppe::DevError, "You must define the NetInfo format to inport" + end + IO.popen("niload -d #{format} . 1>/dev/null 2>/dev/null", "w") { |p| + p.print text + } + + unless $? == 0 + raise ArgumentError, "Failed to write %s" % @path + end + end + end +end + +# $Id$ diff --git a/lib/puppet/util/inifile.rb b/lib/puppet/util/inifile.rb new file mode 100644 index 000000000..d050e6dd1 --- /dev/null +++ b/lib/puppet/util/inifile.rb @@ -0,0 +1,209 @@ +# Module Puppet::IniConfig +# A generic way to parse .ini style files and manipulate them in memory +# One 'file' can be made up of several physical files. Changes to sections +# on the file are tracked so that only the physical files in which +# something has changed are written back to disk +# Great care is taken to preserve comments and blank lines from the original +# files +# +# The parsing tries to stay close to python's ConfigParser + +require 'puppet/util/filetype' + +module Puppet::Util::IniConfig + # A section in a .ini file + class Section + attr_reader :name, :file + + def initialize(name, file) + @name = name + @file = file + @dirty = false + @entries = [] + end + + # Has this section been modified since it's been read in + # or written back to disk + def dirty? + @dirty + end + + # Should only be used internally + def mark_clean + @dirty = false + end + + # Add a line of text (e.g., a comment) Such lines + # will be written back out in exactly the same + # place they were read in + def add_line(line) + @entries << line + end + + # Set the entry 'key=value'. If no entry with the + # given key exists, one is appended to teh end of the section + def []=(key, value) + entry = find_entry(key) + @dirty = true + if entry.nil? + @entries << [key, value] + else + entry[1] = value + end + end + + # Return the value associated with KEY. If no such entry + # exists, return nil + def [](key) + entry = find_entry(key) + if entry.nil? + return nil + end + return entry[1] + end + + # Format the section as text in the way it should be + # written to file + def format + text = "[#{name}]\n" + @entries.each do |entry| + if entry.is_a?(Array) + key, value = entry + unless value.nil? + text << "#{key}=#{value}\n" + end + else + text << entry + end + end + return text + end + + private + def find_entry(key) + @entries.each do |entry| + if entry.is_a?(Array) && entry[0] == key + return entry + end + end + return nil + end + + end + + # A logical .ini-file that can be spread across several physical + # files. For each physical file, call #read with the filename + class File + def initialize + @files = {} + end + + # Add the contents of the file with name FILE to the + # already existing sections + def read(file) + text = Puppet::Util::FileType.filetype(:flat).new(file).read + if text.nil? + raise "Could not find #{file}" + end + + section = nil # The name of the current section + optname = nil # The name of the last option in section + line = 0 + @files[file] = [] + text.each_line do |l| + line += 1 + if l.strip.empty? || "#;".include?(l[0,1]) || + (l.split(nil, 2)[0].downcase == "rem" && + l[0,1].downcase == "r") + # Whitespace or comment + if section.nil? + @files[file] << l + else + section.add_line(l) + end + elsif " \t\r\n\f".include?(l[0,1]) && section && optname + # continuation line + section[optname] += "\n" + l.chomp + elsif l =~ /^\[([^\]]+)\]/ + # section heading + section.mark_clean unless section.nil? + section = add_section($1, file) + optname = nil + elsif l =~ /^\s*([^\s=]+)\s*\=(.+)$/ + # We allow space around the keys, but not the values + # For the values, we don't know if space is significant + if section.nil? + raise "#{file}:#{line}:Key/value pair outside of a section for key #{$1}" + else + section[$1] = $2 + optname = $1 + end + else + raise "#{file}:#{line}: Can't parse '#{l.chomp}'" + end + end + section.mark_clean unless section.nil? + end + + # Store all modifications made to sections in this file back + # to the physical files. If no modifications were made to + # a physical file, nothing is written + def store + @files.each do |file, lines| + text = "" + dirty = false + lines.each do |l| + if l.is_a?(Section) + dirty ||= l.dirty? + text << l.format + l.mark_clean + else + text << l + end + end + if dirty + Puppet::Util::FileType.filetype(:flat).new(file).write(text) + end + end + end + + # Execute BLOCK, passing each section in this file + # as an argument + def each_section(&block) + @files.each do |file, list| + list.each do |entry| + if entry.is_a?(Section) + yield(entry) + end + end + end + end + + # Return the Section with the given name or nil + def [](name) + name = name.to_s + each_section do |section| + return section if section.name == name + end + return nil + end + + # Return true if the file contains a section with name NAME + def include?(name) + return ! self[name].nil? + end + + # Add a section to be stored in FILE when store is called + def add_section(name, file) + if include?(name) + raise "A section with name #{name} already exists" + end + result = Section.new(name, file) + @files[file] ||= [] + @files[file] << result + return result + end + end +end + +# $Id$ diff --git a/lib/puppet/loadedfile.rb b/lib/puppet/util/loadedfile.rb index cfd666e36..362b5df09 100755 --- a/lib/puppet/loadedfile.rb +++ b/lib/puppet/util/loadedfile.rb @@ -5,7 +5,7 @@ require 'puppet' module Puppet class NoSuchFile < Puppet::Error; end - class LoadedFile + class Util::LoadedFile attr_reader :file, :statted # Provide a hook for setting the timestamp during testing, so we don't diff --git a/lib/puppet/util/log.rb b/lib/puppet/util/log.rb new file mode 100644 index 000000000..dd7544dae --- /dev/null +++ b/lib/puppet/util/log.rb @@ -0,0 +1,548 @@ +require 'syslog' + +# Pass feedback to the user. Log levels are modeled after syslog's, and it is +# expected that that will be the most common log destination. Supports +# multiple destinations, one of which is a remote server. +class Puppet::Util::Log + include Puppet::Util + + @levels = [:debug,:info,:notice,:warning,:err,:alert,:emerg,:crit] + @loglevel = 2 + + @desttypes = {} + + # A type of log destination. + class Destination + class << self + attr_accessor :name + end + + def self.initvars + @matches = [] + end + + # Mark the things we're supposed to match. + def self.match(obj) + @matches ||= [] + @matches << obj + end + + # See whether we match a given thing. + def self.match?(obj) + # Convert single-word strings into symbols like :console and :syslog + if obj.is_a? String and obj =~ /^\w+$/ + obj = obj.downcase.intern + end + + @matches.each do |thing| + # Search for direct matches or class matches + return true if thing === obj or thing == obj.class.to_s + end + return false + end + + def name + if defined? @name + return @name + else + return self.class.name + end + end + + # Set how to handle a message. + def self.sethandler(&block) + define_method(:handle, &block) + end + + # Mark how to initialize our object. + def self.setinit(&block) + define_method(:initialize, &block) + end + end + + # Create a new destination type. + def self.newdesttype(name, options = {}, &block) + dest = genclass(name, :parent => Destination, :prefix => "Dest", + :block => block, + :hash => @desttypes, + :attributes => options + ) + dest.match(dest.name) + + return dest + end + + @destinations = {} + + class << self + include Puppet::Util + include Puppet::Util::ClassGen + end + + # Reset all logs to basics. Basically just closes all files and undefs + # all of the other objects. + def Log.close(dest = nil) + if dest + if @destinations.include?(dest) + if @destinations.respond_to?(:close) + @destinations[dest].close + end + @destinations.delete(dest) + end + else + @destinations.each { |name, dest| + if dest.respond_to?(:flush) + dest.flush + end + if dest.respond_to?(:close) + dest.close + end + } + @destinations = {} + end + end + + # Flush any log destinations that support such operations. + def Log.flush + @destinations.each { |type, dest| + if dest.respond_to?(:flush) + dest.flush + end + } + end + + # Create a new log message. The primary role of this method is to + # avoid creating log messages below the loglevel. + def Log.create(hash) + unless hash.include?(:level) + raise Puppet::DevError, "Logs require a level" + end + unless @levels.index(hash[:level]) + raise Puppet::DevError, "Invalid log level %s" % hash[:level] + end + if @levels.index(hash[:level]) >= @loglevel + return Puppet::Util::Log.new(hash) + else + return nil + end + end + + def Log.destinations + return @destinations.keys + end + + # Yield each valid level in turn + def Log.eachlevel + @levels.each { |level| yield level } + end + + # Return the current log level. + def Log.level + return @levels[@loglevel] + end + + # Set the current log level. + def Log.level=(level) + unless level.is_a?(Symbol) + level = level.intern + end + + unless @levels.include?(level) + raise Puppet::DevError, "Invalid loglevel %s" % level + end + + @loglevel = @levels.index(level) + end + + def Log.levels + @levels.dup + end + + newdesttype :syslog do + def close + Syslog.close + end + + def initialize + if Syslog.opened? + Syslog.close + end + name = Puppet.name + name = "puppet-#{name}" unless name =~ /puppet/ + + options = Syslog::LOG_PID | Syslog::LOG_NDELAY + + # XXX This should really be configurable. + str = Puppet[:syslogfacility] + begin + facility = Syslog.const_get("LOG_#{str.upcase}") + rescue NameError + raise Puppet::Error, "Invalid syslog facility %s" % str + end + + @syslog = Syslog.open(name, options, facility) + end + + def handle(msg) + # XXX Syslog currently has a bug that makes it so you + # cannot log a message with a '%' in it. So, we get rid + # of them. + if msg.source == "Puppet" + @syslog.send(msg.level, msg.to_s.gsub("%", '%%')) + else + @syslog.send(msg.level, "(%s) %s" % + [msg.source.to_s.gsub("%", ""), + msg.to_s.gsub("%", '%%') + ] + ) + end + end + end + + newdesttype :file do + match(/^\//) + + def close + if defined? @file + @file.close + @file = nil + end + end + + def flush + if defined? @file + @file.flush + end + end + + def initialize(path) + @name = path + # first make sure the directory exists + # We can't just use 'Config.use' here, because they've + # specified a "special" destination. + unless FileTest.exist?(File.dirname(path)) + Puppet.recmkdir(File.dirname(path)) + Puppet.info "Creating log directory %s" % File.dirname(path) + end + + # create the log file, if it doesn't already exist + file = File.open(path, File::WRONLY|File::CREAT|File::APPEND) + + @file = file + + @autoflush = Puppet[:autoflush] + end + + def handle(msg) + @file.puts("%s %s (%s): %s" % + [msg.time, msg.source, msg.level, msg.to_s]) + + @file.flush if @autoflush + end + end + + newdesttype :console do + + + PINK = {:console => "[0;31m", :html => "FFA0A0"} + GREEN = {:console => "[0;32m", :html => "00CD00"} + YELLOW = {:console => "[0;33m", :html => "FFFF60"} + SLATE = {:console => "[0;34m", :html => "80A0FF"} + ORANGE = {:console => "[0;35m", :html => "FFA500"} + BLUE = {:console => "[0;36m", :html => "40FFFF"} + RESET = {:console => "[0m", :html => ""} + + @@colormap = { + :debug => SLATE, + :info => GREEN, + :notice => PINK, + :warning => ORANGE, + :err => YELLOW, + :alert => BLUE, + :emerg => RESET, + :crit => RESET + } + + def colorize(level, str) + case Puppet[:color] + when false: str + when true, :ansi, "ansi": console_color(level, str) + when :html, "html": html_color(level, str) + end + end + + def console_color(level, str) + @@colormap[level][:console] + str + RESET[:console] + end + + def html_color(level, str) + %{<span style="color: %s">%s</span>} % [@@colormap[level][:html], str] + end + + def initialize + # Flush output immediately. + $stdout.sync = true + end + + def handle(msg) + if msg.source == "Puppet" + puts colorize(msg.level, "%s: %s" % [msg.level, msg.to_s]) + else + puts colorize(msg.level, "%s: %s: %s" % [msg.level, msg.source, msg.to_s]) + end + end + end + + newdesttype :host do + def initialize(host) + Puppet.info "Treating %s as a hostname" % host + args = {} + if host =~ /:(\d+)/ + args[:Port] = $1 + args[:Server] = host.sub(/:\d+/, '') + else + args[:Server] = host + end + + @name = host + + @driver = Puppet::Client::LogClient.new(args) + end + + def handle(msg) + unless msg.is_a?(String) or msg.remote + unless defined? @hostname + @hostname = Facter["hostname"].value + end + unless defined? @domain + @domain = Facter["domain"].value + if @domain + @hostname += "." + @domain + end + end + if msg.source =~ /^\// + msg.source = @hostname + ":" + msg.source + elsif msg.source == "Puppet" + msg.source = @hostname + " " + msg.source + else + msg.source = @hostname + " " + msg.source + end + begin + #puts "would have sent %s" % msg + #puts "would have sent %s" % + # CGI.escape(YAML.dump(msg)) + begin + tmp = CGI.escape(YAML.dump(msg)) + rescue => detail + puts "Could not dump: %s" % detail.to_s + return + end + # Add the hostname to the source + @driver.addlog(tmp) + rescue => detail + if Puppet[:trace] + puts detail.backtrace + end + Puppet.err detail + Puppet::Util::Log.close(self) + end + end + end + end + + # Log to a transaction report. + newdesttype :report do + match "Puppet::Transaction::Report" + + def initialize(report) + @report = report + end + + def handle(msg) + # Only add messages from objects, since anything else is + # probably unrelated to this run. + if msg.objectsource? + @report.newlog(msg) + end + end + end + + # Log to an array, just for testing. + newdesttype :array do + match "Array" + + def initialize(array) + @array = array + end + + def handle(msg) + @array << msg + end + end + + # Create a new log destination. + def Log.newdestination(dest) + # Each destination can only occur once. + if @destinations.find { |name, obj| obj.name == dest } + return + end + + name, type = @desttypes.find do |name, klass| + klass.match?(dest) + end + + unless type + raise Puppet::DevError, "Unknown destination type %s" % dest + end + + begin + if type.instance_method(:initialize).arity == 1 + @destinations[dest] = type.new(dest) + else + @destinations[dest] = type.new() + end + rescue => detail + if Puppet[:debug] + puts detail.backtrace + end + + # If this was our only destination, then add the console back in. + if @destinations.empty? and (dest != :console and dest != "console") + newdestination(:console) + end + end + end + + # Route the actual message. FIXME There are lots of things this method + # should do, like caching, storing messages when there are not yet + # destinations, a bit more. It's worth noting that there's a potential + # for a loop here, if the machine somehow gets the destination set as + # itself. + def Log.newmessage(msg) + if @levels.index(msg.level) < @loglevel + return + end + + @destinations.each do |name, dest| + threadlock(dest) do + dest.handle(msg) + end + end + end + + def Log.sendlevel?(level) + @levels.index(level) >= @loglevel + end + + # Reopen all of our logs. + def Log.reopen + Puppet.notice "Reopening log files" + types = @destinations.keys + @destinations.each { |type, dest| + if dest.respond_to?(:close) + dest.close + end + } + @destinations.clear + # We need to make sure we always end up with some kind of destination + begin + types.each { |type| + Log.newdestination(type) + } + rescue => detail + if @destinations.empty? + Log.newdestination(:syslog) + Puppet.err detail.to_s + end + end + end + + # Is the passed level a valid log level? + def self.validlevel?(level) + @levels.include?(level) + end + + attr_accessor :level, :message, :time, :tags, :remote + attr_reader :source + + def initialize(args) + unless args.include?(:level) && args.include?(:message) + raise Puppet::DevError, "Puppet::Util::Log called incorrectly" + end + + if args[:level].class == String + @level = args[:level].intern + elsif args[:level].class == Symbol + @level = args[:level] + else + raise Puppet::DevError, + "Level is not a string or symbol: #{args[:level].class}" + end + + # Just return unless we're actually at a level we should send + #return unless self.class.sendlevel?(@level) + + @message = args[:message].to_s + @time = Time.now + # this should include the host name, and probly lots of other + # stuff, at some point + unless self.class.validlevel?(level) + raise Puppet::DevError, "Invalid message level #{level}" + end + + if args.include?(:tags) + @tags = args[:tags] + end + + if args.include?(:source) + self.source = args[:source] + else + @source = "Puppet" + end + + Log.newmessage(self) + end + + # Was the source of this log an object? + def objectsource? + if defined? @objectsource and @objectsource + @objectsource + else + false + end + end + + # If they pass a source in to us, we make sure it is a string, and + # we retrieve any tags we can. + def source=(source) + # We can't store the actual source, we just store the path. + # We can't just check for whether it responds to :path, because + # plenty of providers respond to that in their normal function. + if source.is_a?(Puppet::Element) and source.respond_to?(:path) + @objectsource = true + @source = source.path + else + @objectsource = false + @source = source.to_s + end + unless defined? @tags and @tags + if source.respond_to?(:tags) + @tags = source.tags + end + end + end + + def tagged?(tag) + @tags.detect { |t| t.to_s == tag.to_s } + end + + def to_report + "%s %s (%s): %s" % [self.time, self.source, self.level, self.to_s] + end + + def to_s + return @message + end +end + +# $Id$ diff --git a/lib/puppet/util/logging.rb b/lib/puppet/util/logging.rb index 1245e24de..298df93ba 100644 --- a/lib/puppet/util/logging.rb +++ b/lib/puppet/util/logging.rb @@ -1,14 +1,14 @@ # A module to make logging a bit easier. -require 'puppet/log' +require 'puppet/util/log' module Puppet::Util::Logging # Create a method for each log level. - Puppet::Log.eachlevel do |level| + Puppet::Util::Log.eachlevel do |level| define_method(level) do |args| if args.is_a?(Array) args = args.join(" ") end - Puppet::Log.create( + Puppet::Util::Log.create( :level => level, :source => self, :message => args diff --git a/lib/puppet/util/metric.rb b/lib/puppet/util/metric.rb new file mode 100644 index 000000000..4c62df315 --- /dev/null +++ b/lib/puppet/util/metric.rb @@ -0,0 +1,158 @@ +# included so we can test object types +require 'puppet' + +# A class for handling metrics. This is currently ridiculously hackish. +class Puppet::Util::Metric + Puppet.config.setdefaults("metrics", + :rrddir => {:default => "$vardir/rrd", + :owner => "$user", + :group => "$group", + :desc => "The directory where RRD database files are stored. + Directories for each reporting host will be created under + this directory." + }, + :rrdgraph => [false, "Whether RRD information should be graphed."], + :rrdinterval => ["$runinterval", "How often RRD should expect data. + This should match how often the hosts report back to the server."] + ) + + # Load the library as a feature, so we can test its presence. + Puppet.features.add :rrd, :libs => 'RRD' + + attr_accessor :type, :name, :value, :label + + attr_writer :basedir + + def basedir + if defined? @basedir + @basedir + else + Puppet[:rrddir] + end + end + + def create(start = nil) + Puppet.config.use(:metrics) + + start ||= Time.now.to_i - 5 + + path = self.path + args = [ + path, + "--start", start, + "--step", Puppet[:rrdinterval] + ] + + values.each { |value| + # the 7200 is the heartbeat -- this means that any data that isn't + # more frequently than every two hours gets thrown away + args.push "DS:%s:GAUGE:7200:U:U" % [value[0]] + } + args.push "RRA:AVERAGE:0.5:1:300" + + begin + RRD.create(*args) + rescue => detail + raise "Could not create RRD file %s: %s" % [path,detail] + end + end + + def dump + puts RRD.info(self.path) + end + + def graph(range = nil) + unless Puppet.features.rrd? + Puppet.warning "RRD library is missing; cannot graph metrics" + return + end + + unit = 60 * 60 * 24 + colorstack = %w{#ff0000 #00ff00 #0000ff #099000 #000990 #f00990 #0f0f0f} + + {:daily => unit, :weekly => unit * 7, :monthly => unit * 30, :yearly => unit * 365}.each do |name, time| + file = self.path.sub(/\.rrd$/, "-%s.png" % name) + args = [file] + + args.push("--title",self.label) + args.push("--imgformat","PNG") + args.push("--interlace") + i = 0 + defs = [] + lines = [] + #p @values.collect { |s,l| s } + values.zip(colorstack).each { |value,color| + next if value.nil? + # this actually uses the data label + defs.push("DEF:%s=%s:%s:AVERAGE" % [value[0],self.path,value[0]]) + lines.push("LINE2:%s%s:%s" % [value[0],color,value[1]]) + } + args << defs + args << lines + args.flatten! + if range + args.push("--start",range[0],"--end",range[1]) + else + args.push("--start", Time.now.to_i - time, "--end", Time.now.to_i) + end + + begin + RRD.graph(*args) + rescue => detail + Puppet.err "Failed to graph %s: %s" % [self.name,detail] + end + end + end + + def initialize(name,label = nil) + @name = name.to_s + + if label + @label = label + else + @label = name.to_s.capitalize.gsub("_", " ") + end + + @values = [] + end + + def path + return File.join(self.basedir, @name + ".rrd") + end + + def newvalue(name,value,label = nil) + unless label + label = name.to_s.capitalize.gsub("_", " ") + end + @values.push [name,label,value] + end + + def store(time) + unless Puppet.features.rrd? + Puppet.warning "RRD library is missing; cannot store metrics" + return + end + unless FileTest.exists?(self.path) + self.create(time - 5) + end + + # XXX this is not terribly error-resistant + args = [time] + values.each { |value| + args.push value[2] + } + arg = args.join(":") + begin + RRD.update(self.path,arg) + #system("rrdtool updatev %s '%s'" % [self.path, arg]) + rescue => detail + raise Puppet::Error, "Failed to update %s: %s" % [self.name,detail] + end + end + + def values + @values.sort { |a, b| a[1] <=> b[1] } + end +end + +# $Id$ diff --git a/lib/puppet/util/storage.rb b/lib/puppet/util/storage.rb new file mode 100644 index 000000000..d76c67433 --- /dev/null +++ b/lib/puppet/util/storage.rb @@ -0,0 +1,103 @@ +require 'yaml' +require 'sync' + +# a class for storing state +class Puppet::Util::Storage + include Singleton + include Puppet::Util + + def initialize + self.class.load + end + + # Return a hash that will be stored to disk. It's worth noting + # here that we use the object's full path, not just the name/type + # combination. At the least, this is useful for those non-isomorphic + # types like exec, but it also means that if an object changes locations + # in the configuration it will lose its cache. + def self.cache(object) + if object.is_a? Puppet::Type + # We used to store things by path, now we store them by ref. + # In oscar(0.20.0) this changed to using the ref. + if @@state.include?(object.path) + @@state[object.ref] = @@state[object.path] + @@state.delete(object.path) + end + name = object.ref + elsif object.is_a?(Symbol) + name = object + else + raise ArgumentError, "You can only cache information for Types and symbols" + end + + return @@state[name] ||= {} + end + + def self.clear + @@state.clear + Storage.init + end + + def self.init + @@state = {} + @@splitchar = "\t" + end + + self.init + + def self.load + Puppet.config.use(:puppet) + + unless File.exists?(Puppet[:statefile]) + unless defined? @@state and ! @@state.nil? + self.init + end + return + end + Puppet::Util.benchmark(:debug, "Loaded state") do + Puppet::Util.readlock(Puppet[:statefile]) do |file| + begin + @@state = YAML.load(file) + rescue => detail + Puppet.err "Checksumfile %s is corrupt (%s); replacing" % + [Puppet[:statefile], detail] + begin + File.rename(Puppet[:statefile], + Puppet[:statefile] + ".bad") + rescue + raise Puppet::Error, + "Could not rename corrupt %s; remove manually" % + Puppet[:statefile] + end + end + end + end + + unless @@state.is_a?(Hash) + Puppet.err "State got corrupted" + self.init + end + + #Puppet.debug "Loaded state is %s" % @@state.inspect + end + + def self.stateinspect + @@state.inspect + end + + def self.store + Puppet.debug "Storing state" + + unless FileTest.exist?(Puppet[:statefile]) + Puppet.info "Creating state file %s" % Puppet[:statefile] + end + + Puppet::Util.benchmark(:debug, "Stored state") do + Puppet::Util.writelock(Puppet[:statefile], 0660) do |file| + file.print YAML.dump(@@state) + end + end + end +end + +# $Id$ diff --git a/lib/puppet/util/suidmanager.rb b/lib/puppet/util/suidmanager.rb new file mode 100644 index 000000000..7a0c3ae2c --- /dev/null +++ b/lib/puppet/util/suidmanager.rb @@ -0,0 +1,86 @@ +require 'facter' +require 'puppet/util/warnings' + +module Puppet::Util::SUIDManager + include Puppet::Util::Warnings + + platform = Facter["kernel"].value + [:uid=, :gid=, :uid, :gid].each do |method| + define_method(method) do |*args| + # NOTE: 'method' is closed here. + newmethod = method + + if platform == "Darwin" and (method == :uid= or method == :gid=) + Puppet::Util::Warnings.warnonce "Cannot change real UID on Darwin" + newmethod = ("e" + method.to_s).intern + end + + return Process.send(newmethod, *args) + end + module_function method + end + + [:euid=, :euid, :egid=, :egid].each do |method| + define_method(method) do |*args| + Process.send(method, *args) + end + module_function method + end + + def asuser(new_euid=nil, new_egid=nil) + # Unless we're root, don't do a damn thing. + unless Process.uid == 0 + return yield + end + old_egid = old_euid = nil + if new_egid + old_egid = self.egid + self.egid = convert_xid(:gid, new_egid) + end + if new_euid + old_euid = self.euid + self.euid = convert_xid(:uid, new_euid) + end + + return yield + ensure + self.euid = old_euid if old_euid + self.egid = old_egid if old_egid + end + + # Make sure the passed argument is a number. + def convert_xid(type, id) + map = {:gid => :group, :uid => :user} + raise ArgumentError, "Invalid id type %s" % type unless map.include?(type) + ret = Puppet::Util.send(type, id) + if ret == nil + raise Puppet::Error, "Invalid %s: %s" % [map[type], id] + end + return ret + end + + module_function :asuser, :convert_xid + + def run_and_capture(command, new_uid=nil, new_gid=nil) + output = nil + + output = Puppet::Util.execute(command, false, new_uid, new_gid) + + [output, $?.dup] + end + + module_function :run_and_capture + + def system(command, new_uid=nil, new_gid=nil) + status = nil + asuser(new_uid, new_gid) do + Kernel.system(command) + status = $?.dup + end + status + end + + module_function :system +end + +# $Id$ |
