summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorluke <luke@980ebf18-57e1-0310-9a29-db15c13687c0>2006-05-19 17:29:35 +0000
committerluke <luke@980ebf18-57e1-0310-9a29-db15c13687c0>2006-05-19 17:29:35 +0000
commitd55adda617793c90a0ad38b09ef620b14d2e497b (patch)
tree6e7c7e61e27e17ead5b37ce3051bbbc6411e6c13
parent5671ce8bbc6eb75664190a7a2fe4b195ee31d4b7 (diff)
downloadpuppet-d55adda617793c90a0ad38b09ef620b14d2e497b.tar.gz
puppet-d55adda617793c90a0ad38b09ef620b14d2e497b.tar.xz
puppet-d55adda617793c90a0ad38b09ef620b14d2e497b.zip
First version of puppetrun. It seems to mostly work, but I need to test it with greater parallelization.
git-svn-id: https://reductivelabs.com/svn/puppet/trunk@1214 980ebf18-57e1-0310-9a29-db15c13687c0
-rwxr-xr-xbin/puppetrun298
-rw-r--r--lib/puppet/client.rb1
2 files changed, 299 insertions, 0 deletions
diff --git a/bin/puppetrun b/bin/puppetrun
new file mode 100755
index 000000000..0b602201f
--- /dev/null
+++ b/bin/puppetrun
@@ -0,0 +1,298 @@
+#!/usr/bin/ruby
+
+#
+# = Synopsis
+#
+# Trigger a puppetd run on a set of hosts.
+#
+# = Usage
+#
+# puppet [-c|--class <class>] [-d|--debug] [-h|--help] [-h|--host <host>]
+#
+# = Description
+#
+# This script can be used to connect to a set of machines running +puppetd+
+# and trigger them to run their configurations. The most common usage would
+# be to specify a class of hosts and a set of tags, and +puppetrun+ would
+# look up in LDAP all of the hosts matching that class, then connect to
+# each host and trigger a run of all of the objects with the specified tags.
+#
+# If you are not storing your host configurations in LDAP, you can specify
+# hosts manually.
+#
+# You will most likely have to run +puppetrun+ as root to get access to
+# the SSL certificates.
+#
+# +puppetrun+ reads +puppetmaster+'s configuration file, so that it can copy
+# things like LDAP settings.
+#
+# = 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 <directory>' as an argument.
+#
+# See the configuration file for the full list of acceptable parameters.
+#
+# class::
+# Specify a class of machines to which to connect. This only works if you
+# have LDAP configured, at the moment.
+#
+# debug::
+# Enable full debugging.
+#
+# foreground::
+# Run each configuration in the foreground; that is, when connecting to a host,
+# do not return until the host has finished its run. The default is false.
+#
+# help::
+# Print this help message
+#
+# host::
+# A specific host to which to connect. This flag can be specified more
+# than once.
+#
+# ignoreschedules::
+# Whether the client should ignore schedules when running its configuration.
+# This can be used to force the client to perform work it would not normally
+# perform so soon. The default is false.
+#
+# parallel::
+# How parallel to make the connections. Parallelization is provided by forking
+# for each client to which to connect. The default is 1, meaning serial execution.
+#
+# = Example
+#
+# sudo puppetrun -p 10 -h host1 -h host2 -t remotefile -t webserver
+#
+# = Author
+#
+# Luke Kanies
+#
+# = Copyright
+#
+# Copyright (c) 2005 Reductive Labs, LLC
+# Licensed under the GNU Public License
+
+[:INT, :TERM].each do |signal|
+ trap(signal) do
+ $stderr.puts "Cancelling"
+ exit(1)
+ end
+end
+require 'puppet'
+require 'puppet/server'
+require 'puppet/client'
+require 'getoptlong'
+
+
+# Look up all nodes matching a given class in LDAP.
+def ldapnodes(klass)
+ unless defined? @ldap
+ setupldap()
+ end
+
+ hosts = []
+
+ @ldap.search(Puppet[:ldapbase], 2, "puppetclass=#{klass}", "cn") do |entry|
+ hosts << entry.get_values("cn")[0]
+ end
+
+ return hosts
+end
+
+def setupldap
+ begin
+ @ldap = LDAP::Conn.new(Puppet[:ldapserver], Puppet[:ldapport])
+ @ldap.set_option(LDAP::LDAP_OPT_PROTOCOL_VERSION, 3)
+ @ldap.simple_bind(Puppet[:ldapuser], Puppet[:ldappassword])
+ rescue => detail
+ $stderr.puts "Could not connect to LDAP: %s" % detail
+ exit(34)
+ end
+end
+
+$haveusage = true
+
+begin
+ require 'rdoc/usage'
+rescue LoadError
+ $haveusage = false
+end
+
+flags = [
+ [ "--class", "-c", GetoptLong::REQUIRED_ARGUMENT ],
+ [ "--foreground", "-f", GetoptLong::NO_ARGUMENT ],
+ [ "--debug", "-d", GetoptLong::NO_ARGUMENT ],
+ [ "--help", "-h", GetoptLong::NO_ARGUMENT ],
+ [ "--host", GetoptLong::REQUIRED_ARGUMENT ],
+ [ "--parallel", "-p", GetoptLong::REQUIRED_ARGUMENT ],
+ [ "--version", "-V", GetoptLong::NO_ARGUMENT ]
+]
+
+# Add all of the config parameters as valid options.
+Puppet.config.addargs(flags)
+
+result = GetoptLong.new(*flags)
+
+options = {
+ :ignoreschedules => false,
+ :foreground => false,
+ :parallel => 10,
+ :debug => false,
+ :verbose => true
+}
+
+hosts = []
+classes = []
+tags = []
+
+Puppet::Log.newdestination(:console)
+
+begin
+ result.each { |opt,arg|
+ case opt
+ when "--version"
+ puts "%s" % Puppet.version
+ exit
+ when "--ignoreschedules"
+ options[:ignoreschedules] = true
+ when "--tag"
+ tags << arg
+ when "--class"
+ classes << arg
+ when "--host"
+ hosts << arg
+ when "--help"
+ if $haveusage
+ RDoc::usage && exit
+ else
+ puts "No help available unless you have RDoc::usage installed"
+ exit
+ end
+ when "--parallel"
+ begin
+ options[:parallel] = Integer(arg)
+ rescue
+ $stderr.puts "Could not convert %s to an integer" % arg.inspect
+ exit(23)
+ end
+ when "--foreground"
+ options[:foreground] = true
+ when "--debug"
+ options[:debug] = true
+ else
+ Puppet.config.handlearg(opt, arg)
+ end
+ }
+rescue GetoptLong::InvalidOption => detail
+ $stderr.puts "Try '#{$0} --help'"
+ #if $haveusage
+ # RDoc::usage(1,'usage')
+ #end
+ exit(1)
+end
+
+if options[:debug]
+ Puppet::Log.level = :debug
+else
+ Puppet::Log.level = :info
+end
+
+# Now parse the config
+config = File.join(Puppet[:confdir], "puppetmasterd.conf")
+if File.exists? config
+ Puppet.config.parse(config)
+end
+
+if Puppet[:ldapnodes]
+ classes.each do |klass|
+ list = ldapnodes(klass)
+ puts "%s: %s" % [klass, list.join(", ")]
+
+ hosts += []
+ end
+elsif ! classes.empty?
+ $stderr.puts "You must be using LDAP to specify host classes"
+ exit(24)
+end
+
+if tags.empty?
+ tags = ""
+else
+ tags = tags.join(",")
+end
+
+children = {}
+
+# If we get a signal, then kill all of our children and get out.
+[:INT, :TERM].each do |signal|
+ trap(signal) do
+ Puppet.notice "Caught #{signal}; shutting down"
+ children.each do |pid, host|
+ Process.kill("INT", pid)
+ end
+
+ waitall
+
+ exit(1)
+ end
+end
+
+todo = hosts.dup
+
+failures = []
+
+# Now do the actual work
+go = true
+while go
+ if children.length < options[:parallel] and ! todo.empty?
+ host = todo.shift
+ pid = fork do
+ # First make sure the client is up
+ out = %x{ping -c 1 #{host}}
+
+ unless $? == 0
+ $stderr.puts "Could not contact %s" % host
+ exit(1)
+ end
+ client = Puppet::Client::Runner.new(
+ :Server => host,
+ :Port => Puppet[:puppetport]
+ )
+ begin
+ client.run(tags, options[:ignoreschedules], options[:foreground])
+ rescue => detail
+ $stderr.puts "Host %s failed: %s" % [host, detail]
+ exit(2)
+ end
+ end
+ children[pid] = host
+ else
+ begin
+ pid = Process.wait
+
+ if host = children[pid]
+ if $?.exitstatus != 0
+ failures << host
+ end
+ puts "%s finished with exit code %s" % [host, $?.exitstatus]
+ else
+ $stderr.puts "Could not find host for PID %s with status %s" %
+ [pid, $?.exitstatus]
+ end
+ rescue Errno::ECHILD
+ # There are no children left, so just exit.
+ if failures.empty?
+ puts "Finished"
+ exit(0)
+ else
+ puts "Failed: %s" % failures.join(", ")
+ exit(2)
+ end
+ end
+ end
+end
+
+
+# $Id$
diff --git a/lib/puppet/client.rb b/lib/puppet/client.rb
index fbc9b5e9e..bb1572063 100644
--- a/lib/puppet/client.rb
+++ b/lib/puppet/client.rb
@@ -164,6 +164,7 @@ module Puppet
require 'puppet/client/file'
require 'puppet/client/log'
require 'puppet/client/master'
+ require 'puppet/client/runner'
require 'puppet/client/status'
require 'puppet/client/pelement'
end