summaryrefslogtreecommitdiffstats
path: root/lib/puppet/application
diff options
context:
space:
mode:
authorJosh Cooper <josh@puppetlabs.com>2011-04-21 14:37:50 -0700
committerJosh Cooper <josh@puppetlabs.com>2011-04-21 14:37:50 -0700
commit01f610bb223b435dc52f491260af3ea002930102 (patch)
tree93565136d5ef2b4156fdd64476792e441bcfbb4e /lib/puppet/application
parentac428b9557e2da251e4b51e48de844833ca0aa2a (diff)
parentfc66e98b84b9a16728af054485883334a5887cca (diff)
Merge branch 'next'
Diffstat (limited to 'lib/puppet/application')
-rw-r--r--lib/puppet/application/cert.rb98
-rw-r--r--lib/puppet/application/device.rb255
-rw-r--r--lib/puppet/application/face_base.rb87
-rw-r--r--lib/puppet/application/kick.rb35
4 files changed, 380 insertions, 95 deletions
diff --git a/lib/puppet/application/cert.rb b/lib/puppet/application/cert.rb
index c08775380..162672b6a 100644
--- a/lib/puppet/application/cert.rb
+++ b/lib/puppet/application/cert.rb
@@ -61,9 +61,8 @@ but mostly used for signing certificate requests from puppet clients.
USAGE
-----
-puppet cert [-h|--help] [-V|--version] [-d|--debug] [-v|--verbose]
- [-g|--generate] [-l|--list] [-s|--sign] [-r|--revoke] [-p|--print]
- [-c|--clean] [--verify] [--digest <digest>] [--fingerprint] [host]
+puppet cert <action> [-h|--help] [-V|--version] [-d|--debug] [-v|--verbose]
+ [--digest <digest>] [<host>]
DESCRIPTION
@@ -73,6 +72,51 @@ certificate requests, this script is available for signing outstanding
requests. It can be used to list outstanding requests and then either
sign them individually or sign all of them.
+ACTIONS
+-------
+
+Every action except 'list' and 'generate' requires a hostname to act on,
+unless the '--all' option is set.
+
+* clean:
+ Revoke a host's certificate (if applicable) and remove all files
+ related to that host from puppet cert's storage. This is useful when
+ rebuilding hosts, since new certificate signing requests will only be
+ honored if puppet cert does not have a copy of a signed certificate
+ for that host. If '--all' is specified then all host certificates,
+ both signed and unsigned, will be removed.
+
+* fingerprint:
+ Print the DIGEST (defaults to md5) fingerprint of a host's
+ certificate.
+
+* generate:
+ Generate a certificate for a named client. A certificate/keypair will
+ be generated for each client named on the command line.
+
+* list:
+ List outstanding certificate requests. If '--all' is specified, signed
+ certificates are also listed, prefixed by '+', and revoked or invalid
+ certificates are prefixed by '-' (the verification outcome is printed
+ in parenthesis).
+
+* print:
+ Print the full-text version of a host's certificate.
+
+* revoke:
+ Revoke the certificate of a client. The certificate can be specified
+ either by its serial number (given as a decimal number or a
+ hexadecimal number prefixed by '0x') or by its hostname. The
+ certificate is revoked by adding it to the Certificate Revocation List
+ given by the 'cacrl' configuration option. Note that the puppet master
+ needs to be restarted after revoking certificates.
+
+* sign:
+ Sign an outstanding certificate request.
+
+* verify:
+ Verify the named certificate against the local CA certificate.
+
OPTIONS
-------
@@ -88,72 +132,32 @@ configuration options can also be generated by running puppet cert with
'--genconfig'.
* --all:
- Operate on all items. Currently only makes sense with '--sign',
- '--clean', or '--list'.
+ Operate on all items. Currently only makes sense with the 'sign',
+ 'clean', 'list', and 'fingerprint' actions.
* --digest:
Set the digest for fingerprinting (defaults to md5). Valid values
depends on your openssl and openssl ruby extension version, but should
contain at least md5, sha1, md2, sha256.
-* --clean:
- Remove all files related to a host from puppet cert's storage. This is
- useful when rebuilding hosts, since new certificate signing requests
- will only be honored if puppet cert does not have a copy of a signed
- certificate for that host. The certificate of the host is also
- revoked. If '--all' is specified then all host certificates, both
- signed and unsigned, will be removed.
-
* --debug:
Enable full debugging.
-* --generate:
- Generate a certificate for a named client. A certificate/keypair will
- be generated for each client named on the command line.
-
* --help:
Print this help message
-* --list:
- List outstanding certificate requests. If '--all' is specified, signed
- certificates are also listed, prefixed by '+', and revoked or invalid
- certificates are prefixed by '-' (the verification outcome is printed
- in parenthesis).
-
-* --print:
- Print the full-text version of a host's certificate.
-
-* --fingerprint:
- Print the DIGEST (defaults to md5) fingerprint of a host's
- certificate.
-
-* --revoke:
- Revoke the certificate of a client. The certificate can be specified
- either by its serial number, given as a decimal number or a
- hexadecimal number prefixed by '0x', or by its hostname. The
- certificate is revoked by adding it to the Certificate Revocation List
- given by the 'cacrl' config parameter. Note that the puppetmasterd
- needs to be restarted after revoking certificates.
-
-* --sign:
- Sign an outstanding certificate request. Unless '--all' is specified,
- hosts must be listed after all flags.
-
* --verbose:
Enable verbosity.
* --version:
Print the puppet version number and exit.
-* --verify:
- Verify the named certificate against the local CA certificate.
-
EXAMPLE
-------
- $ puppet cert -l
+ $ puppet cert list
culain.madstop.com
- $ puppet cert -s culain.madstop.com
+ $ puppet cert sign culain.madstop.com
AUTHOR
diff --git a/lib/puppet/application/device.rb b/lib/puppet/application/device.rb
new file mode 100644
index 000000000..df5bac26a
--- /dev/null
+++ b/lib/puppet/application/device.rb
@@ -0,0 +1,255 @@
+require 'puppet/application'
+require 'puppet/util/network_device'
+
+
+class Puppet::Application::Device < Puppet::Application
+
+ should_parse_config
+ run_mode :agent
+
+ attr_accessor :args, :agent, :host
+
+ def preinit
+ # Do an initial trap, so that cancels don't get a stack trace.
+ trap(:INT) do
+ $stderr.puts "Cancelling startup"
+ exit(0)
+ end
+
+ {
+ :waitforcert => nil,
+ :detailed_exitcodes => false,
+ :verbose => false,
+ :debug => false,
+ :centrallogs => false,
+ :setdest => false,
+ }.each do |opt,val|
+ options[opt] = val
+ end
+
+ @args = {}
+ end
+
+ option("--centrallogging")
+ option("--debug","-d")
+ option("--verbose","-v")
+
+ option("--detailed-exitcodes") do |arg|
+ options[:detailed_exitcodes] = true
+ end
+
+ option("--logdest DEST", "-l DEST") do |arg|
+ begin
+ Puppet::Util::Log.newdestination(arg)
+ options[:setdest] = true
+ rescue => detail
+ puts detail.backtrace if Puppet[:debug]
+ $stderr.puts detail.to_s
+ end
+ end
+
+ option("--waitforcert WAITFORCERT", "-w") do |arg|
+ options[:waitforcert] = arg.to_i
+ end
+
+ option("--port PORT","-p") do |arg|
+ @args[:Port] = arg
+ end
+
+ def help
+ <<-HELP
+
+puppet-device(8) -- Manage remote network devices
+========
+
+SYNOPSIS
+--------
+Retrieves all configurations from the puppet master and apply
+them to the remote devices configured in /etc/puppet/device.conf.
+
+Currently must be run out periodically, using cron or something similar.
+
+USAGE
+-----
+ puppet device [-d|--debug] [--detailed-exitcodes] [-V|--version]
+ [-h|--help] [-l|--logdest syslog|<file>|console]
+ [-v|--verbose] [-w|--waitforcert <seconds>]
+
+
+DESCRIPTION
+-----------
+Once the client has a signed certificate for a given remote device, it will
+retrieve its configuration and apply it.
+
+USAGE NOTES
+-----------
+One need a /etc/puppet/device.conf file with the following content:
+
+[remote.device.fqdn]
+type <type>
+url <url>
+
+where:
+ * type: the current device type (the only value at this time is cisco)
+ * url: an url allowing to connect to the device
+
+Supported url must conforms to:
+ scheme://user:password@hostname/?query
+
+ with:
+ * scheme: either ssh or telnet
+ * user: username, can be omitted depending on the switch/router configuration
+ * password: the connection password
+ * query: this is device specific. Cisco devices supports an enable parameter whose
+ value would be the enable password.
+
+OPTIONS
+-------
+Note that any configuration parameter that's valid in the configuration file
+is also a valid long argument. For example, 'server' is a valid configuration
+parameter, so you can specify '--server <servername>' as an argument.
+
+* --debug:
+ Enable full debugging.
+
+* --detailed-exitcodes:
+ Provide transaction information via exit codes. If this is enabled, an
+ exit code of '2' means there were changes, and an exit code of '4' means
+ that there were failures during the transaction. This option only makes
+ sense in conjunction with --onetime.
+
+* --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.
+
+* --verbose:
+ Turn on verbose reporting.
+
+* --waitforcert:
+ This option only matters for daemons that do not yet have certificates
+ and it is enabled by default, with a value of 120 (seconds). This causes
+ +puppet agent+ to connect to the server every 2 minutes and ask it to sign a
+ certificate request. This is useful for the initial setup of a puppet
+ client. You can turn off waiting for certificates by specifying a time
+ of 0.
+
+EXAMPLE
+-------
+ $ puppet device --server puppet.domain.com
+
+AUTHOR
+------
+Brice Figureau
+
+
+COPYRIGHT
+---------
+Copyright (c) 2011 Puppet Labs, LLC
+Licensed under the Apache 2.0 License
+ HELP
+ end
+
+
+ def main
+ vardir = Puppet[:vardir]
+ confdir = Puppet[:confdir]
+ certname = Puppet[:certname]
+
+ # find device list
+ require 'puppet/util/network_device/config'
+ devices = Puppet::Util::NetworkDevice::Config.devices
+ if devices.empty?
+ Puppet.err "No device found in #{Puppet[:deviceconfig]}"
+ exit(1)
+ end
+ devices.each_value do |device|
+ begin
+ Puppet.info "starting applying configuration to #{device.name} at #{device.url}"
+
+ # override local $vardir and $certname
+ Puppet.settings.set_value(:confdir, File.join(Puppet[:devicedir], device.name), :cli)
+ Puppet.settings.set_value(:vardir, File.join(Puppet[:devicedir], device.name), :cli)
+ Puppet.settings.set_value(:certname, device.name, :cli)
+
+ # this will reload and recompute default settings and create the devices sub vardir, or we hope so :-)
+ Puppet.settings.use :main, :agent, :ssl
+
+ # this init the device singleton, so that the facts terminus
+ # and the various network_device provider can use it
+ Puppet::Util::NetworkDevice.init(device)
+
+ # ask for a ssl cert if needed, but at least
+ # setup the ssl system for this device.
+ setup_host
+
+ require 'puppet/configurer'
+ configurer = Puppet::Configurer.new
+ report = configurer.run(:network_device => true)
+ rescue => detail
+ puts detail.backtrace if Puppet[:trace]
+ Puppet.err detail.to_s
+ ensure
+ Puppet.settings.set_value(:vardir, vardir, :cli)
+ Puppet.settings.set_value(:confdir, confdir, :cli)
+ Puppet.settings.set_value(:certname, certname, :cli)
+ end
+ end
+ end
+
+ # Handle the logging settings.
+ def setup_logs
+ if options[:debug] or options[:verbose]
+ Puppet::Util::Log.newdestination(:console)
+ if options[:debug]
+ Puppet::Util::Log.level = :debug
+ else
+ Puppet::Util::Log.level = :info
+ end
+ end
+
+ Puppet::Util::Log.newdestination(:syslog) unless options[:setdest]
+ end
+
+ def setup_host
+ @host = Puppet::SSL::Host.new
+ waitforcert = options[:waitforcert] || (Puppet[:onetime] ? 0 : 120)
+ cert = @host.wait_for_cert(waitforcert)
+ end
+
+ def setup
+ setup_logs
+
+ args[:Server] = Puppet[:server]
+ if options[:centrallogs]
+ logdest = args[:Server]
+
+ logdest += ":" + args[:Port] if args.include?(:Port)
+ Puppet::Util::Log.newdestination(logdest)
+ end
+
+ Puppet.settings.use :main, :agent, :device, :ssl
+
+ # Always ignoreimport for agent. It really shouldn't even try to import,
+ # but this is just a temporary band-aid.
+ Puppet[:ignoreimport] = true
+
+ # We need to specify a ca location for all of the SSL-related i
+ # indirected classes to work; in fingerprint mode we just need
+ # access to the local files and we don't need a ca.
+ Puppet::SSL::Host.ca_location = :remote
+
+ Puppet::Transaction::Report.indirection.terminus_class = :rest
+
+ # Override the default; puppetd needs this, usually.
+ # You can still override this on the command-line with, e.g., :compiler.
+ Puppet[:catalog_terminus] = :rest
+
+ Puppet[:facts_terminus] = :network_device
+
+ Puppet::Resource::Catalog.indirection.cache_class = :yaml
+ end
+end
diff --git a/lib/puppet/application/face_base.rb b/lib/puppet/application/face_base.rb
index 2a048a532..9da48af55 100644
--- a/lib/puppet/application/face_base.rb
+++ b/lib/puppet/application/face_base.rb
@@ -1,6 +1,7 @@
require 'puppet/application'
require 'puppet/face'
require 'optparse'
+require 'pp'
class Puppet::Application::FaceBase < Puppet::Application
should_parse_config
@@ -14,8 +15,8 @@ class Puppet::Application::FaceBase < Puppet::Application
Puppet::Util::Log.level = :info
end
- option("--format FORMAT") do |arg|
- @format = arg.to_sym
+ option("--render-as FORMAT") do |arg|
+ @render_as = arg.to_sym
end
option("--mode RUNMODE", "-r") do |arg|
@@ -25,7 +26,7 @@ class Puppet::Application::FaceBase < Puppet::Application
end
- attr_accessor :face, :action, :type, :arguments, :format
+ attr_accessor :face, :action, :type, :arguments, :render_as
attr_writer :exit_code
# This allows you to set the exit code if you don't want to just exit
@@ -34,17 +35,49 @@ class Puppet::Application::FaceBase < Puppet::Application
@exit_code || 0
end
- # Override this if you need custom rendering.
def render(result)
- render_method = Puppet::Network::FormatHandler.format(format).render_method
- if render_method == "to_pson"
- jj result
- exit(0)
+ format = render_as || action.render_as || :for_humans
+
+ # Invoke the rendering hook supplied by the user, if appropriate.
+ if hook = action.when_rendering(format) then
+ result = hook.call(result)
+ end
+
+ if format == :for_humans then
+ render_for_humans(result)
else
- result.send(render_method)
+ render_method = Puppet::Network::FormatHandler.format(format).render_method
+ if render_method == "to_pson"
+ PSON::pretty_generate(result, :allow_nan => true, :max_nesting => false)
+ else
+ result.send(render_method)
+ end
end
end
+ def render_for_humans(result)
+ # String to String
+ return result if result.is_a? String
+ return result if result.is_a? Numeric
+
+ # Simple hash to table
+ if result.is_a? Hash and result.keys.all? { |x| x.is_a? String or x.is_a? Numeric }
+ output = ''
+ column_a = result.map do |k,v| k.to_s.length end.max + 2
+ column_b = 79 - column_a
+ result.sort_by { |k,v| k.to_s } .each do |key, value|
+ output << key.to_s.ljust(column_a)
+ output << PP.pp(value, '', column_b).
+ chomp.gsub(/\n */) { |x| x + (' ' * column_a) }
+ output << "\n"
+ end
+ return output
+ end
+
+ # ...or pretty-print the inspect outcome.
+ return result.pretty_inspect
+ end
+
def preinit
super
Signal.trap(:INT) do
@@ -59,9 +92,8 @@ class Puppet::Application::FaceBase < Puppet::Application
# REVISIT: These should be configurable versions, through a global
# '--version' option, but we don't implement that yet... --daniel 2011-03-29
- @type = self.class.name.to_s.sub(/.+:/, '').downcase.to_sym
- @face = Puppet::Face[@type, :current]
- @format = @face.default_format
+ @type = self.class.name.to_s.sub(/.+:/, '').downcase.to_sym
+ @face = Puppet::Face[@type, :current]
# Now, walk the command line and identify the action. We skip over
# arguments based on introspecting the action and all, and find the first
@@ -94,16 +126,13 @@ class Puppet::Application::FaceBase < Puppet::Application
raise OptionParser::InvalidOption.new(item.sub(/=.*$/, ''))
end
else
- action = @face.get_action(item.to_sym)
- if action.nil? then
- raise OptionParser::InvalidArgument.new("#{@face} does not have an #{item} action")
- end
- @action = action
+ @action = @face.get_action(item.to_sym)
end
end
- unless @action
- raise OptionParser::MissingArgument.new("No action given on the command line")
+ if @action.nil?
+ @action = @face.get_default_action()
+ @is_default_action = true
end
# Now we can interact with the default option code to build behaviour
@@ -111,7 +140,7 @@ class Puppet::Application::FaceBase < Puppet::Application
@action.options.each do |option|
option = @action.get_option(option) # make it the object.
self.class.option(*option.optparse) # ...and make the CLI parse it.
- end
+ end if @action
# ...and invoke our parent to parse all the command line options.
super
@@ -138,7 +167,10 @@ class Puppet::Application::FaceBase < Puppet::Application
# with it *always* being the first word of the remaining set of command
# line arguments. So, strip that off when we construct the arguments to
# pass down to the face action. --daniel 2011-04-04
- @arguments.delete_at(0)
+ # Of course, now that we have default actions, we should leave the
+ # "action" name on if we didn't actually consume it when we found our
+ # action.
+ @arguments.delete_at(0) unless @is_default_action
# We copy all of the app options to the end of the call; This allows each
# action to read in the options. This replaces the older model where we
@@ -150,8 +182,17 @@ class Puppet::Application::FaceBase < Puppet::Application
def main
# Call the method associated with the provided action (e.g., 'find').
- if result = @face.send(@action.name, *arguments)
- puts render(result)
+ if @action
+ result = @face.send(@action.name, *arguments)
+ puts render(result) unless result.nil?
+ else
+ if arguments.first.is_a? Hash
+ puts "#{@face} does not have a default action"
+ else
+ puts "#{@face} does not respond to action #{arguments.first}"
+ end
+
+ puts Puppet::Face[:help, :current].help(@face.name, *arguments)
end
exit(exit_code)
end
diff --git a/lib/puppet/application/kick.rb b/lib/puppet/application/kick.rb
index 536699442..4f3ed1802 100644
--- a/lib/puppet/application/kick.rb
+++ b/lib/puppet/application/kick.rb
@@ -76,31 +76,16 @@ copy things like LDAP settings.
USAGE NOTES
-----------
-'puppet kick' is useless unless 'puppet agent' is listening. See its
-documentation for more information, but the gist is that you must enable
-'listen' on the 'puppet agent' daemon, either using '--listen' on the
-command line or adding 'listen = true' in its config file. In addition,
-you need to set the daemons up to specifically allow connections by
-creating the 'namespaceauth' file, normally at
-'/etc/puppet/namespaceauth.conf'. This file specifies who has access to
-each namespace; if you create the file you must add every namespace you
-want any Puppet daemon to allow -- it is currently global to all Puppet
-daemons.
-
-An example file looks like this:
-
- [fileserver]
- allow *.madstop.com
-
- [puppetmaster]
- allow *.madstop.com
-
- [puppetrunner]
- allow culain.madstop.com
-
-This is what you would install on your Puppet master; non-master hosts
-could leave off the 'fileserver' and 'puppetmaster' namespaces.
-
+Puppet kick is useless unless puppet agent is listening for incoming
+connections and allowing access to the `run` endpoint. This entails
+starting the agent with `listen = true` in its puppet.conf file, and
+allowing access to the `/run` path in its auth.conf file; see
+`http://docs.puppetlabs.com/guides/rest_auth_conf.html` for more
+details.
+
+Additionally, due to a known bug, you must make sure a
+namespaceauth.conf file exists in puppet agent's $confdir. This file
+will not be consulted, and may be left empty.
OPTIONS
-------