1
require 'puppet/application'
3
class Puppet::Application::Kick < Puppet::Application
5
should_not_parse_config
7
attr_accessor :hosts, :tags, :classes
10
option("--foreground","-f")
11
option("--debug","-d")
15
option("--host HOST") do |arg|
19
option("--tag TAG", "-t") do |arg|
23
option("--class CLASS", "-c") do |arg|
27
option("--no-fqdn", "-n") do |arg|
28
options[:fqdn] = false
31
option("--parallel PARALLEL", "-p") do |arg|
33
options[:parallel] = Integer(arg)
35
$stderr.puts "Could not convert #{arg.inspect} to an integer"
43
puppet-kick(8) -- Remotely control puppet agent
48
Trigger a puppet agent run on a set of hosts.
53
puppet kick [-a|--all] [-c|--class <class>] [-d|--debug] [-f|--foreground]
54
[-h|--help] [--host <host>] [--no-fqdn] [--ignoreschedules]
55
[-t|--tag <tag>] [--test] [-p|--ping] <host> [<host> [...]]
60
This script can be used to connect to a set of machines running 'puppet
61
agent' and trigger them to run their configurations. The most common
62
usage would be to specify a class of hosts and a set of tags, and
63
'puppet kick' would look up in LDAP all of the hosts matching that
64
class, then connect to each host and trigger a run of all of the objects
65
with the specified tags.
67
If you are not storing your host configurations in LDAP, you can specify
70
You will most likely have to run 'puppet kick' as root to get access to
73
'puppet kick' reads 'puppet master''s configuration file, so that it can
74
copy things like LDAP settings.
79
Puppet kick is useless unless puppet agent is listening for incoming
80
connections and allowing access to the `run` endpoint. This entails
81
starting the agent with `listen = true` in its puppet.conf file, and
82
allowing access to the `/run` path in its auth.conf file; see
83
`http://docs.puppetlabs.com/guides/rest_auth_conf.html` for more
86
Additionally, due to a known bug, you must make sure a
87
namespaceauth.conf file exists in puppet agent's $confdir. This file
88
will not be consulted, and may be left empty.
92
Note that any configuration parameter that's valid in the configuration
93
file is also a valid long argument. For example, 'ssldir' is a valid
94
configuration parameter, so you can specify '--ssldir <directory>' as an
97
See the configuration file documentation at
98
http://docs.puppetlabs.com/references/latest/configuration.html for
99
the full list of acceptable parameters. A commented list of all
100
configuration options can also be generated by running puppet master
104
Connect to all available hosts. Requires LDAP support at this point.
107
Specify a class of machines to which to connect. This only works if
108
you have LDAP configured, at the moment.
111
Enable full debugging.
114
Run each configuration in the foreground; that is, when connecting to
115
a host, do not return until the host has finished its run. The default
119
Print this help message
122
A specific host to which to connect. This flag can be specified more
126
Whether the client should ignore schedules when running its
127
configuration. This can be used to force the client to perform work it
128
would not normally perform so soon. The default is false.
131
How parallel to make the connections. Parallelization is provided by
132
forking for each client to which to connect. The default is 1, meaning
136
Specify a tag for selecting the objects to apply. Does not work with
140
Print the hosts you would connect to but do not actually connect. This
141
option requires LDAP support at this point.
144
Do a ICMP echo against the target host. Skip hosts that don't respond
150
$ sudo puppet kick -p 10 -t remotefile -t webserver host1 host2
160
Copyright (c) 2011 Puppet Labs, LLC Licensed under the Apache 2.0 License
166
@hosts += command_line.args
167
options[:test] ? test : main
171
puts "Skipping execution in test mode"
176
require 'puppet/network/client'
178
Puppet.warning "Failed to load ruby LDAP library. LDAP functionality will not be available" unless Puppet.features.ldap?
179
require 'puppet/util/ldap/connection'
185
# Now do the actual work
188
# If we don't have enough children in process and we still have hosts left to
189
# do, then do the next host.
190
if @children.length < options[:parallel] and ! todo.empty?
195
@children[pid] = host
197
# Else, see if we can reap a process.
201
if host = @children[pid]
202
# Remove our host from the list of children, so the parallelization
204
@children.delete(pid)
205
failures << host if $CHILD_STATUS.exitstatus != 0
206
print "#{host} finished with exit code #{$CHILD_STATUS.exitstatus}\n"
208
$stderr.puts "Could not find host for PID #{pid} with status #{$CHILD_STATUS.exitstatus}"
211
# There are no children left, so just exit unless there are still
212
# children left to do.
213
next unless todo.empty?
219
puts "Failed: #{failures.join(", ")}"
227
def run_for_host(host)
229
out = %x{ping -c 1 #{host}}
230
unless $CHILD_STATUS == 0
231
$stderr.print "Could not contact #{host}\n"
237
Puppet::Run.indirection.terminus_class = :rest
238
port = Puppet[:puppetport]
239
url = ["https://#{host}:#{port}", "production", "run", host].join('/')
241
print "Triggering #{host}\n"
245
:background => ! options[:foreground],
246
:ignoreschedules => options[:ignoreschedules]
248
run = Puppet::Run.indirection.save(Puppet::Run.new( run_options ), url)
249
puts "Getting status"
251
puts "status is #{result}"
253
puts detail.backtrace if Puppet[:trace]
254
$stderr.puts "Host #{host} failed: #{detail}\n"
262
$stderr.puts "Host #{host} is already running"
265
$stderr.puts "Host #{host} returned unknown answer '#{result}'"
270
def initialize(*args)
278
[:INT, :TERM].each do |signal|
279
Signal.trap(signal) do
280
$stderr.puts "Cancelling"
284
options[:parallel] = 1
285
options[:verbose] = true
286
options[:fqdn] = true
287
options[:ignoreschedules] = false
288
options[:foreground] = false
293
Puppet::Util::Log.level = :debug
295
Puppet::Util::Log.level = :info
298
# Now parse the config
301
if Puppet[:node_terminus] == "ldap" and (options[:all] or @classes)
303
@hosts = Puppet::Node.indirection.search("whatever", :fqdn => options[:fqdn]).collect { |node| node.name }
304
puts "all: #{@hosts.join(", ")}"
307
@classes.each do |klass|
308
list = Puppet::Node.indirection.search("whatever", :fqdn => options[:fqdn], :class => klass).collect { |node| node.name }
309
puts "#{klass}: #{list.join(", ")}"
314
elsif ! @classes.empty?
315
$stderr.puts "You must be using LDAP to specify host classes"
321
# If we get a signal, then kill all of our children and get out.
322
[:INT, :TERM].each do |signal|
323
Signal.trap(signal) do
324
Puppet.notice "Caught #{signal}; shutting down"
325
@children.each do |pid, host|
326
Process.kill("INT", pid)