diff --git a/bin/puppetmasterd b/bin/puppetmasterd index a00186b68..33e4f436d 100755 --- a/bin/puppetmasterd +++ b/bin/puppetmasterd @@ -1,284 +1,285 @@ #!/usr/bin/env ruby # # = Synopsis # # The central puppet server. Functions as a certificate authority by default. # # = Usage # # puppetmasterd [-D|--daemonize|--no-daemonize] [-d|--debug] [-h|--help] # [-l|--logdest |console|syslog] [--nobucket] [--nonodes] # [-v|--verbose] [-V|--version] # # = Description # # This is the puppet central daemon. # # = Options # # Note that any configuration parameter that's valid in the configuration file # is also a valid long argument. For example, 'ssldir' is a valid configuration # parameter, so you can specify '--ssldir ' as an argument. # # See the configuration file documentation at # http://reductivelabs.com/projects/puppet/reference/configref.html for # the full list of acceptable parameters. A commented list of all # configuration options can also be generated by running puppetmasterdd with # '--genconfig'. # # daemonize:: # Send the process into the background. This is the default. # # no-daemonize:: # Do not send the process into the background. # # debug:: # Enable full debugging. # # help:: # Print this help message. # # logdest:: # Where to send messages. Choose between syslog, the console, and a log file. # Defaults to sending messages to syslog, or the console # if debugging or verbosity is enabled. # # nobucket:: # Do not function as a file bucket. # # nonodes:: # Do not use individual node designations; each node will receive the result # of evaluating the entire configuration. # # noreports:: # Do not start the reports server. # # verbose:: # Enable verbosity. # # version:: # Print the puppet version number and exit. # # = Example # # puppetmasterd # # = Author # # Luke Kanies # # = Copyright # # Copyright (c) 2005 Reductive Labs, LLC # Licensed under the GNU Public License # Do an initial trap, so that cancels don't get a stack trace. trap(:INT) do $stderr.puts "Cancelling startup" exit(0) end require 'getoptlong' require 'puppet' +require 'puppet/network/handler' require 'puppet/sslcertificates' options = [ [ "--debug", "-d", GetoptLong::NO_ARGUMENT ], [ "--help", "-h", GetoptLong::NO_ARGUMENT ], [ "--logdest", "-l", GetoptLong::REQUIRED_ARGUMENT ], [ "--nobucket", GetoptLong::NO_ARGUMENT ], [ "--noreports", GetoptLong::NO_ARGUMENT ], [ "--nonodes", GetoptLong::NO_ARGUMENT ], [ "--verbose", "-v", GetoptLong::NO_ARGUMENT ], [ "--version", "-V", GetoptLong::NO_ARGUMENT ] ] # Add all of the config parameters as valid options. Puppet.settings.addargs(options) result = GetoptLong.new(*options) master = {} ca = {} report = {} bucket = {} options = { :havereport => true, :havebucket => true, :havemaster => true, :setdest => false, :verbose => false, :debug => false } begin result.each { |opt,arg| case opt # First check to see if the argument is a valid configuration parameter; # if so, set it. NOTE: there is a catch-all at the bottom for defaults.rb when "--debug" options[:debug] = true when "--help" if Puppet.features.usage? RDoc::usage && exit else puts "No help available unless you have RDoc::usage installed" exit end when "--noreports" options[:havereport] = false when "--nomaster" options[:havemaster] = false when "--nobucket" options[:havebucket] = false when "--nonodes" master[:UseNodes] = false when "--logdest" begin Puppet::Util::Log.newdestination(arg) options[:setdest] = true rescue => detail if Puppet[:debug] puts detail.backtrace end $stderr.puts detail.to_s end when "--version" puts "%s" % Puppet.version exit when "--verbose" options[:verbose] = true else Puppet.settings.handlearg(opt, arg) end } rescue GetoptLong::InvalidOption => detail $stderr.puts "Try '#{$0} --help'" #$stderr.puts detail exit(1) end # Now parse the config Puppet.parse_config # Handle the logging settings. if options[:debug] or options[:verbose] if options[:debug] Puppet::Util::Log.level = :debug else Puppet::Util::Log.level = :info end unless Puppet[:daemonize] Puppet::Util::Log.newdestination(:console) options[:setdest] = true end end unless options[:setdest] Puppet::Util::Log.newdestination(:syslog) end Puppet.genconfig Puppet.genmanifest # A temporary solution, to at least make the master work for now. Puppet::Node::Facts.terminus_class = :yaml require 'etc' handlers = { :Status => {}, :FileServer => {} } if options[:havemaster] handlers[:Master] = master end if options[:havereport] handlers[:Report] = report end if Puppet[:ca] handlers[:CA] = ca end if options[:havebucket] handlers[:FileBucket] = bucket end if Puppet[:parseonly] begin Puppet::Network::Handler.master.new(master) rescue => detail if Puppet[:trace] puts detail.backtrace end $stderr.puts detail exit(32) end # we would have already exited if the file weren't syntactically correct exit(0) end webserver = server = nil begin case Puppet[:servertype] when "webrick" # use the default, um, everything require 'puppet/network/http_server/webrick' webserver = server = Puppet::Network::HTTPServer::WEBrick.new(:Handlers => handlers) when "mongrel": require 'puppet/network/http_server/mongrel' server = Puppet::Network::HTTPServer::Mongrel.new(handlers) addr = Puppet[:bindaddress] if addr == "" addr = "127.0.0.1" end webserver = Mongrel::HttpServer.new(addr, Puppet[:masterport]) webserver.register("/", server) else Puppet.err "Invalid server type %s" % Puppet[:servertype] exit(45) end rescue => detail if Puppet[:trace] puts detail.backtrace end $stderr.puts detail exit(1) end if Process.uid == 0 begin Puppet::Util.chuser rescue => detail if Puppet[:debug] puts detail.backtrace end $stderr.puts "Could not change user to %s: %s" % [Puppet[:user], detail] exit(39) end end # Mongrel doesn't shut down like webrick; we really need to write plugins for it. if Puppet[:servertype] == "webrick" Puppet.newservice(server) end Puppet.settraps if Puppet[:daemonize] server.daemonize end Puppet.notice "Starting Puppet server version %s" % [Puppet.version] case Puppet[:servertype] when "webrick" Puppet.start when "mongrel": webserver.run.join end diff --git a/lib/puppet.rb b/lib/puppet.rb index 18037cdc1..57f84d5f7 100644 --- a/lib/puppet.rb +++ b/lib/puppet.rb @@ -1,432 +1,433 @@ # Try to load rubygems. Hey rubygems, I hate you. begin require 'rubygems' rescue LoadError end # see the bottom of the file for further inclusions require 'singleton' require 'facter' require 'puppet/error' require 'puppet/external/event-loop' require 'puppet/util' require 'puppet/util/log' require 'puppet/util/autoload' require 'puppet/util/settings' require 'puppet/util/feature' require 'puppet/util/suidmanager' #------------------------------------------------------------ # the top-level module # # all this really does is dictate how the whole system behaves, through # preferences for things like debugging # # it's also a place to find top-level commands like 'debug' module Puppet PUPPETVERSION = '0.24.1' def Puppet.version return PUPPETVERSION end class << self # So we can monitor signals and such. include SignalObserver include Puppet::Util # To keep a copy of arguments. Set within Config#addargs, because I'm # lazy. attr_accessor :args attr_reader :features attr_writer :name end # the hash that determines how our system behaves @@settings = Puppet::Util::Settings.new # The services running in this process. @services ||= [] # define helper messages for each of the message levels Puppet::Util::Log.eachlevel { |level| define_method(level,proc { |args| if args.is_a?(Array) args = args.join(" ") end Puppet::Util::Log.create( :level => level, :message => args ) }) module_function level } # I keep wanting to use Puppet.error # XXX this isn't actually working right now alias :error :err # The feature collection @features = Puppet::Util::Feature.new('puppet/feature') # Load the base features. require 'puppet/feature/base' # Store a new default value. def self.setdefaults(section, hash) @@settings.setdefaults(section, hash) end # configuration parameter access and stuff def self.[](param) case param when :debug: if Puppet::Util::Log.level == :debug return true else return false end else return @@settings[param] end end # configuration parameter access and stuff def self.[]=(param,value) @@settings[param] = value end def self.clear @@settings.clear end def self.debug=(value) if value Puppet::Util::Log.level=(:debug) else Puppet::Util::Log.level=(:notice) end end def self.settings @@settings end # Load all of the configuration parameters. require 'puppet/defaults' # Prints the contents of a config file with the available config elements, or it # prints a single value of a config element. def self.genconfig if Puppet[:configprint] != "" val = Puppet[:configprint] if val == "all" hash = {} Puppet.settings.each do |name, obj| val = obj.value case val when true, false, "": val = val.inspect end hash[name] = val end hash.sort { |a,b| a[0].to_s <=> b[0].to_s }.each do |name, val| puts "%s = %s" % [name, val] end elsif val =~ /,/ val.split(/\s*,\s*/).sort.each do |v| if Puppet.settings.include?(v) puts "%s = %s" % [v, Puppet[v]] else puts "invalid parameter: %s" % v exit(1) end end else val.split(/\s*,\s*/).sort.each do |v| if Puppet.settings.include?(v) puts Puppet[val] else puts "invalid parameter: %s" % v exit(1) end end end exit(0) end if Puppet[:genconfig] puts Puppet.settings.to_config exit(0) end end def self.genmanifest if Puppet[:genmanifest] puts Puppet.settings.to_manifest exit(0) end end # Run all threads to their ends def self.join defined? @threads and @threads.each do |t| t.join end end # Create a new service that we're supposed to run def self.newservice(service) @services ||= [] @services << service end def self.newthread(&block) @threads ||= [] @threads << Thread.new do yield end end def self.newtimer(hash, &block) timer = nil threadlock(:timers) do @timers ||= [] timer = EventLoop::Timer.new(hash) @timers << timer if block_given? observe_signal(timer, :alarm, &block) end end # In case they need it for something else. timer end # Parse the config file for this process. def self.parse_config(oldconfig = nil) # First look for the old configuration file. oldconfig ||= File.join(Puppet[:confdir], Puppet[:name].to_s + ".conf") if FileTest.exists?(oldconfig) and Puppet[:name] != "puppet" Puppet.warning "Individual config files are deprecated; remove %s and use puppet.conf" % oldconfig Puppet.settings.old_parse(oldconfig) return end # Now check for the normal config. if Puppet[:config] and File.exists? Puppet[:config] Puppet.debug "Parsing %s" % Puppet[:config] Puppet.settings.parse(Puppet[:config]) end end # Relaunch the executable. def self.restart command = $0 + " " + self.args.join(" ") Puppet.notice "Restarting with '%s'" % command Puppet.shutdown(false) Puppet::Util::Log.reopen exec(command) end # Trap a couple of the main signals. This should probably be handled # in a way that anyone else can register callbacks for traps, but, eh. def self.settraps [:INT, :TERM].each do |signal| trap(signal) do Puppet.notice "Caught #{signal}; shutting down" Puppet.debug "Signal caught here:" caller.each { |l| Puppet.debug l } Puppet.shutdown end end # Handle restarting. trap(:HUP) do if client = @services.find { |s| s.is_a? Puppet::Network::Client.master } and client.running? client.restart else Puppet.restart end end # Provide a hook for running clients where appropriate trap(:USR1) do done = 0 Puppet.notice "Caught USR1; triggering client run" @services.find_all { |s| s.is_a? Puppet::Network::Client }.each do |client| if client.respond_to? :running? if client.running? Puppet.info "Ignoring running %s" % client.class else done += 1 begin client.runnow rescue => detail Puppet.err "Could not run client: %s" % detail end end else Puppet.info "Ignoring %s; cannot test whether it is running" % client.class end end unless done > 0 Puppet.notice "No clients were run" end end trap(:USR2) do Puppet::Util::Log.reopen end end # Shutdown our server process, meaning stop all services and all threads. # Optionally, exit. def self.shutdown(leave = true) Puppet.notice "Shutting down" # Unmonitor our timers defined? @timers and @timers.each do |timer| EventLoop.current.ignore_timer timer end # This seems to exit the process, although I can't find where it does # so. Leaving it out doesn't seem to hurt anything. #if EventLoop.current.running? # EventLoop.current.quit #end # Stop our services defined? @services and @services.each do |svc| next unless svc.respond_to?(:shutdown) begin timeout(20) do svc.shutdown end rescue TimeoutError Puppet.err "%s could not shut down within 20 seconds" % svc.class end end # And wait for them all to die, giving a decent amount of time defined? @threads and @threads.each do |thr| begin timeout(20) do thr.join end rescue TimeoutError # Just ignore this, since we can't intelligently provide a warning end end if leave exit(0) end end # Start all of our services and optionally our event loop, which blocks, # waiting for someone, somewhere, to generate events of some kind. def self.start(block = true) # Starting everything in its own thread, fwiw defined? @services and @services.dup.each do |svc| newthread do begin svc.start rescue => detail if Puppet[:trace] puts detail.backtrace end @services.delete svc Puppet.err "Could not start %s: %s" % [svc.class, detail] end end end # We need to give the services a chance to register their timers before # we try to start monitoring them. sleep 0.5 unless @services.length > 0 Puppet.notice "No remaining services; exiting" exit(1) end if defined? @timers and ! @timers.empty? @timers.each do |timer| EventLoop.current.monitor_timer timer end end if block EventLoop.current.run end end # Create the timer that our different objects (uh, mostly the client) # check. def self.timer unless defined? @timer #Puppet.info "Interval is %s" % Puppet[:runinterval] #@timer = EventLoop::Timer.new(:interval => Puppet[:runinterval]) @timer = EventLoop::Timer.new( :interval => Puppet[:runinterval], :tolerance => 1, :start? => true ) EventLoop.current.monitor_timer @timer end @timer end # XXX this should all be done using puppet objects, not using # normal mkdir def self.recmkdir(dir,mode = 0755) if FileTest.exist?(dir) return false else tmp = dir.sub(/^\//,'') path = [File::SEPARATOR] tmp.split(File::SEPARATOR).each { |dir| path.push dir if ! FileTest.exist?(File.join(path)) begin Dir.mkdir(File.join(path), mode) rescue Errno::EACCES => detail Puppet.err detail.to_s return false rescue => detail Puppet.err "Could not create %s: %s" % [path, detail.to_s] return false end elsif FileTest.directory?(File.join(path)) next else FileTest.exist?(File.join(path)) raise Puppet::Error, "Cannot create %s: basedir %s is a file" % [dir, File.join(path)] end } return true end end # Create a new type. Just proxy to the Type class. def self.newtype(name, options = {}, &block) Puppet::Type.newtype(name, options, &block) end # Retrieve a type by name. Just proxy to the Type class. def self.type(name) Puppet::Type.type(name) end end require 'puppet/type' +require 'puppet/network' require 'puppet/module' require 'puppet/util/storage' require 'puppet/parser/interpreter' if Puppet[:storeconfigs] require 'puppet/rails' end diff --git a/lib/puppet/network.rb b/lib/puppet/network.rb new file mode 100644 index 000000000..8993b8869 --- /dev/null +++ b/lib/puppet/network.rb @@ -0,0 +1,3 @@ +# Just a stub, so we can correctly scope other classes. +module Puppet::Network # :nodoc: +end