diff options
| author | Josh Cooper <josh@puppetlabs.com> | 2011-04-21 14:37:50 -0700 |
|---|---|---|
| committer | Josh Cooper <josh@puppetlabs.com> | 2011-04-21 14:37:50 -0700 |
| commit | 01f610bb223b435dc52f491260af3ea002930102 (patch) | |
| tree | 93565136d5ef2b4156fdd64476792e441bcfbb4e /lib/puppet/application | |
| parent | ac428b9557e2da251e4b51e48de844833ca0aa2a (diff) | |
| parent | fc66e98b84b9a16728af054485883334a5887cca (diff) | |
Merge branch 'next'
Diffstat (limited to 'lib/puppet/application')
| -rw-r--r-- | lib/puppet/application/cert.rb | 98 | ||||
| -rw-r--r-- | lib/puppet/application/device.rb | 255 | ||||
| -rw-r--r-- | lib/puppet/application/face_base.rb | 87 | ||||
| -rw-r--r-- | lib/puppet/application/kick.rb | 35 |
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 ------- |
