simp-cli 1.0.12
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +15 -0
- data/LICENSE +27 -0
- data/README.md +48 -0
- data/Rakefile +142 -0
- data/bin/simp +5 -0
- data/lib/simp/cli.rb +88 -0
- data/lib/simp/cli/commands/bootstrap.rb +275 -0
- data/lib/simp/cli/commands/check.rb +163 -0
- data/lib/simp/cli/commands/cleancerts.rb +114 -0
- data/lib/simp/cli/commands/config.rb +235 -0
- data/lib/simp/cli/commands/doc.rb +14 -0
- data/lib/simp/cli/commands/passgen.rb +128 -0
- data/lib/simp/cli/commands/puppeteval.rb +82 -0
- data/lib/simp/cli/commands/runpuppet.rb +95 -0
- data/lib/simp/cli/config/item.rb +456 -0
- data/lib/simp/cli/config/item/add_ldap_to_hiera.rb +43 -0
- data/lib/simp/cli/config/item/answers_yaml_file_writer.rb +58 -0
- data/lib/simp/cli/config/item/certificates.rb +39 -0
- data/lib/simp/cli/config/item/client_nets.rb +65 -0
- data/lib/simp/cli/config/item/common_runlevel_default.rb +32 -0
- data/lib/simp/cli/config/item/dns_search.rb +48 -0
- data/lib/simp/cli/config/item/dns_servers.rb +57 -0
- data/lib/simp/cli/config/item/failover_log_servers.rb +27 -0
- data/lib/simp/cli/config/item/gateway.rb +32 -0
- data/lib/simp/cli/config/item/grub_password.rb +51 -0
- data/lib/simp/cli/config/item/hostname.rb +24 -0
- data/lib/simp/cli/config/item/hostname_conf.rb +48 -0
- data/lib/simp/cli/config/item/ipaddress.rb +46 -0
- data/lib/simp/cli/config/item/is_master_yum_server.rb +23 -0
- data/lib/simp/cli/config/item/ldap_base_dn.rb +38 -0
- data/lib/simp/cli/config/item/ldap_bind_dn.rb +34 -0
- data/lib/simp/cli/config/item/ldap_bind_hash.rb +28 -0
- data/lib/simp/cli/config/item/ldap_bind_pw.rb +24 -0
- data/lib/simp/cli/config/item/ldap_master.rb +33 -0
- data/lib/simp/cli/config/item/ldap_root_dn.rb +42 -0
- data/lib/simp/cli/config/item/ldap_root_hash.rb +35 -0
- data/lib/simp/cli/config/item/ldap_sync_dn.rb +24 -0
- data/lib/simp/cli/config/item/ldap_sync_hash.rb +28 -0
- data/lib/simp/cli/config/item/ldap_sync_pw.rb +26 -0
- data/lib/simp/cli/config/item/ldap_uri.rb +43 -0
- data/lib/simp/cli/config/item/log_servers.rb +27 -0
- data/lib/simp/cli/config/item/netmask.rb +39 -0
- data/lib/simp/cli/config/item/network_conf.rb +63 -0
- data/lib/simp/cli/config/item/network_dhcp.rb +27 -0
- data/lib/simp/cli/config/item/network_interface.rb +41 -0
- data/lib/simp/cli/config/item/network_setup_nic.rb +28 -0
- data/lib/simp/cli/config/item/ntp_servers.rb +69 -0
- data/lib/simp/cli/config/item/puppet_autosign.rb +66 -0
- data/lib/simp/cli/config/item/puppet_ca.rb +31 -0
- data/lib/simp/cli/config/item/puppet_ca_port.rb +28 -0
- data/lib/simp/cli/config/item/puppet_conf.rb +98 -0
- data/lib/simp/cli/config/item/puppet_fileserver.rb +104 -0
- data/lib/simp/cli/config/item/puppet_hosts_entry.rb +44 -0
- data/lib/simp/cli/config/item/puppet_server.rb +30 -0
- data/lib/simp/cli/config/item/puppet_server_ip.rb +25 -0
- data/lib/simp/cli/config/item/puppetdb_port.rb +25 -0
- data/lib/simp/cli/config/item/puppetdb_server.rb +26 -0
- data/lib/simp/cli/config/item/remove_ldap_from_hiera.rb +47 -0
- data/lib/simp/cli/config/item/rename_fqdn_yaml.rb +40 -0
- data/lib/simp/cli/config/item/rsync_base.rb +37 -0
- data/lib/simp/cli/config/item/rsync_server.rb +44 -0
- data/lib/simp/cli/config/item/rsync_timeout.rb +26 -0
- data/lib/simp/cli/config/item/set_grub_password.rb +19 -0
- data/lib/simp/cli/config/item/simp_yum_servers.rb +30 -0
- data/lib/simp/cli/config/item/use_auditd.rb +19 -0
- data/lib/simp/cli/config/item/use_fips.rb +46 -0
- data/lib/simp/cli/config/item/use_iptables.rb +22 -0
- data/lib/simp/cli/config/item/use_ldap.rb +19 -0
- data/lib/simp/cli/config/item/use_selinux.rb +32 -0
- data/lib/simp/cli/config/item/yum_repositories.rb +75 -0
- data/lib/simp/cli/config/item_list_factory.rb +236 -0
- data/lib/simp/cli/config/questionnaire.rb +86 -0
- data/lib/simp/cli/config/utils.rb +128 -0
- data/lib/simp/cli/lib/utils.rb +114 -0
- data/lib/simp/simp.rb +77 -0
- data/spec/lib/simp/cli/commands/config_spec.rb +42 -0
- data/spec/lib/simp/cli/config/item/add_ldap_to_hiera_spec.rb +58 -0
- data/spec/lib/simp/cli/config/item/answers_yaml_file_writer_spec.rb +86 -0
- data/spec/lib/simp/cli/config/item/certificates_spec.rb +50 -0
- data/spec/lib/simp/cli/config/item/client_nets_spec.rb +66 -0
- data/spec/lib/simp/cli/config/item/common_runlevel_default_spec.rb +27 -0
- data/spec/lib/simp/cli/config/item/dns_search_spec.rb +74 -0
- data/spec/lib/simp/cli/config/item/dns_servers_spec.rb +76 -0
- data/spec/lib/simp/cli/config/item/failover_log_servers_spec.rb +49 -0
- data/spec/lib/simp/cli/config/item/files/FakeCA/cacertkey +1 -0
- data/spec/lib/simp/cli/config/item/files/FakeCA/gencerts_nopass.sh +10 -0
- data/spec/lib/simp/cli/config/item/files/autosign.conf.new +11 -0
- data/spec/lib/simp/cli/config/item/files/autosign.conf.used +15 -0
- data/spec/lib/simp/cli/config/item/files/fileserver.conf +41 -0
- data/spec/lib/simp/cli/config/item/files/hosts +2 -0
- data/spec/lib/simp/cli/config/item/files/hosts.old_puppet_entry +3 -0
- data/spec/lib/simp/cli/config/item/files/puppet.conf +25 -0
- data/spec/lib/simp/cli/config/item/files/puppet.your.domain.yaml +21 -0
- data/spec/lib/simp/cli/config/item/files/resolv.conf__multiple +10 -0
- data/spec/lib/simp/cli/config/item/files/resolv.conf__single +4 -0
- data/spec/lib/simp/cli/config/item/files/rsyncd.conf +225 -0
- data/spec/lib/simp/cli/config/item/gateway_spec.rb +23 -0
- data/spec/lib/simp/cli/config/item/grub_password_spec.rb +24 -0
- data/spec/lib/simp/cli/config/item/hostname_conf_spec.rb +27 -0
- data/spec/lib/simp/cli/config/item/hostname_spec.rb +22 -0
- data/spec/lib/simp/cli/config/item/ipaddress_spec.rb +40 -0
- data/spec/lib/simp/cli/config/item/is_master_yum_server_spec.rb +29 -0
- data/spec/lib/simp/cli/config/item/ldap_base_dn_spec.rb +23 -0
- data/spec/lib/simp/cli/config/item/ldap_bind_dn_spec.rb +23 -0
- data/spec/lib/simp/cli/config/item/ldap_bind_hash_spec.rb +23 -0
- data/spec/lib/simp/cli/config/item/ldap_bind_pw_spec.rb +21 -0
- data/spec/lib/simp/cli/config/item/ldap_master_spec.rb +37 -0
- data/spec/lib/simp/cli/config/item/ldap_root_dn_spec.rb +23 -0
- data/spec/lib/simp/cli/config/item/ldap_root_hash_spec.rb +23 -0
- data/spec/lib/simp/cli/config/item/ldap_sync_dn_spec.rb +22 -0
- data/spec/lib/simp/cli/config/item/ldap_sync_hash_spec.rb +23 -0
- data/spec/lib/simp/cli/config/item/ldap_sync_pw_spec.rb +21 -0
- data/spec/lib/simp/cli/config/item/ldap_uri_spec.rb +32 -0
- data/spec/lib/simp/cli/config/item/log_servers_spec.rb +49 -0
- data/spec/lib/simp/cli/config/item/netmask_spec.rb +28 -0
- data/spec/lib/simp/cli/config/item/network_conf_spec.rb +63 -0
- data/spec/lib/simp/cli/config/item/network_dhcp_spec.rb +11 -0
- data/spec/lib/simp/cli/config/item/network_interface_spec.rb +26 -0
- data/spec/lib/simp/cli/config/item/network_setup_nic_spec.rb +29 -0
- data/spec/lib/simp/cli/config/item/ntp_servers_spec.rb +43 -0
- data/spec/lib/simp/cli/config/item/puppet_autosign_spec.rb +55 -0
- data/spec/lib/simp/cli/config/item/puppet_ca_port_spec.rb +23 -0
- data/spec/lib/simp/cli/config/item/puppet_ca_spec.rb +22 -0
- data/spec/lib/simp/cli/config/item/puppet_conf_spec.rb +110 -0
- data/spec/lib/simp/cli/config/item/puppet_fileserver_spec.rb +53 -0
- data/spec/lib/simp/cli/config/item/puppet_hosts_entry_spec.rb +85 -0
- data/spec/lib/simp/cli/config/item/puppet_server_ip_spec.rb +24 -0
- data/spec/lib/simp/cli/config/item/puppet_server_spec.rb +22 -0
- data/spec/lib/simp/cli/config/item/puppetdb_port_spec.rb +25 -0
- data/spec/lib/simp/cli/config/item/puppetdb_server_spec.rb +25 -0
- data/spec/lib/simp/cli/config/item/remove_ldap_from_hiera_spec.rb +58 -0
- data/spec/lib/simp/cli/config/item/rename_fqdn_yaml_spec.rb +63 -0
- data/spec/lib/simp/cli/config/item/rsync_base_spec.rb +28 -0
- data/spec/lib/simp/cli/config/item/rsync_server_spec.rb +41 -0
- data/spec/lib/simp/cli/config/item/rsync_timeout_spec.rb +21 -0
- data/spec/lib/simp/cli/config/item/set_grub_password_spec.rb +29 -0
- data/spec/lib/simp/cli/config/item/simp_yum_servers_spec.rb +41 -0
- data/spec/lib/simp/cli/config/item/spec_helper.rb +22 -0
- data/spec/lib/simp/cli/config/item/use_auditd_spec.rb +29 -0
- data/spec/lib/simp/cli/config/item/use_fips_spec.rb +29 -0
- data/spec/lib/simp/cli/config/item/use_iptables_spec.rb +29 -0
- data/spec/lib/simp/cli/config/item/use_ldap_spec.rb +29 -0
- data/spec/lib/simp/cli/config/item/use_selinux_spec.rb +24 -0
- data/spec/lib/simp/cli/config/item/yum_repositories_spec.rb +94 -0
- data/spec/lib/simp/cli/config/item_spec.rb +106 -0
- data/spec/lib/simp/cli/config/spec_helper.rb +1 -0
- data/spec/lib/simp/cli/config/utils_spec.rb +131 -0
- data/spec/lib/simp/cli/spec_helper.rb +1 -0
- data/spec/spec_helper.rb +91 -0
- metadata +391 -0
@@ -0,0 +1,14 @@
|
|
1
|
+
module Simp::Cli::Commands; end
|
2
|
+
|
3
|
+
class Simp::Cli::Commands::Doc < Simp::Cli
|
4
|
+
def self.run(args = Array.new)
|
5
|
+
raise "Package 'simp-doc' is not installed, cannot continue" unless system("rpm -q --quiet simp-doc")
|
6
|
+
pupdoc = %x{rpm -ql simp-doc | grep html/index.html$ | head -1}.strip.chomp
|
7
|
+
raise "Could not find the SIMP documentation. Please ensure that you can access '#{pupdoc}'." unless File.exists?(pupdoc)
|
8
|
+
exec("links #{pupdoc}")
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.help
|
12
|
+
puts "Show SIMP documentation in elinks"
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,128 @@
|
|
1
|
+
module Simp::Cli::Commands; end
|
2
|
+
|
3
|
+
class Simp::Cli::Commands::Passgen < Simp::Cli
|
4
|
+
require 'fileutils'
|
5
|
+
|
6
|
+
@target_dir = '/etc/puppet/modules/site/files/gen_passwd'
|
7
|
+
@show_list = false
|
8
|
+
@show_users = Array.new
|
9
|
+
@set_users = Array.new
|
10
|
+
@remove_users = Array.new
|
11
|
+
|
12
|
+
@opt_parser = OptionParser.new do |opts|
|
13
|
+
opts.banner = "\n === The SIMP Passgen Tool === "
|
14
|
+
opts.separator ""
|
15
|
+
opts.separator "The SIMP Passgen Tool is a simple password control utility. It allows the"
|
16
|
+
opts.separator "viewing, setting, and removal of user passwords."
|
17
|
+
opts.separator ""
|
18
|
+
opts.separator "OPTIONS:\n"
|
19
|
+
|
20
|
+
opts.on("-d", "--dir DIRECTORY", "Where the passgen passwords are stored.") do |dir|
|
21
|
+
@target_dir = dir
|
22
|
+
end
|
23
|
+
|
24
|
+
opts.on("-l", "--list", "List possible usernames upon whic to operate") do
|
25
|
+
@show_list = true
|
26
|
+
end
|
27
|
+
|
28
|
+
opts.on("-u", "--user USER1[,USER2,USER3]", Array, "Show password(s) for USERNAME") do |name|
|
29
|
+
@show_users = name
|
30
|
+
end
|
31
|
+
|
32
|
+
opts.on("-s", "--set USER1[,USER2,USER3]", Array, "Set password for USERNAME") do |name|
|
33
|
+
@set_users = name
|
34
|
+
end
|
35
|
+
|
36
|
+
opts.on("-r", "--remove USER1[,USER2,USER3]", Array, "Remove all passwords for USERNAME") do |name|
|
37
|
+
@remove_users = name
|
38
|
+
end
|
39
|
+
|
40
|
+
opts.on("-h", "--help", "Print this message.") do
|
41
|
+
puts opts
|
42
|
+
exit 0
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def self.run(args = Array.new)
|
47
|
+
super
|
48
|
+
|
49
|
+
raise "The SIMP Passgen Tool requires at least one argument to work" if args.empty?
|
50
|
+
raise "Target directory '#{@target_dir}' does not exist" unless File.directory?(@target_dir)
|
51
|
+
|
52
|
+
begin
|
53
|
+
Dir.chdir(@target_dir) do
|
54
|
+
@user_names = Dir.glob("*").map { |x| x = File.basename(x, '.last') }.sort.uniq.select do |name|
|
55
|
+
File.ftype("#{@target_dir}/#{name}").eql?("file")
|
56
|
+
end
|
57
|
+
end
|
58
|
+
rescue => err
|
59
|
+
raise "Error occured while accessing '#{@target_dir}':\n Causing Error: #{err}"
|
60
|
+
end
|
61
|
+
|
62
|
+
if @show_list
|
63
|
+
puts "Usernames:\n\t#{@user_names.join("\n\t")}"
|
64
|
+
puts
|
65
|
+
end
|
66
|
+
|
67
|
+
@show_users.each do |user|
|
68
|
+
if @user_names.include?(user)
|
69
|
+
Dir.chdir(@target_dir) do
|
70
|
+
puts "Username: #{user}"
|
71
|
+
current_password = File.open("#{@target_dir}/#{user}", 'r').gets
|
72
|
+
puts " Current: #{current_password}"
|
73
|
+
last_password = nil
|
74
|
+
last_password_file = "#{@target_dir}/#{user}.last"
|
75
|
+
if File.exists?(last_password_file)
|
76
|
+
last_password = File.open(last_password_file, 'r').gets
|
77
|
+
end
|
78
|
+
puts " Previous: #{lass_password}" if last_password
|
79
|
+
end
|
80
|
+
else
|
81
|
+
raise "Invalid username '#{user}' selected.\n\n Valid: #{@user_names.join(', ')}"
|
82
|
+
end
|
83
|
+
puts
|
84
|
+
end
|
85
|
+
|
86
|
+
@set_users.each do |user|
|
87
|
+
password_filename = "#{@target_dir}/#{user}"
|
88
|
+
|
89
|
+
puts "Username: #{user}"
|
90
|
+
password = Utils.get_password
|
91
|
+
if File.exists?(password_filename)
|
92
|
+
if Utils.yes_or_no("Would you like to rotate the old password?", false)
|
93
|
+
begin
|
94
|
+
FileUtils.mv(password_filename, password_filename + '.last')
|
95
|
+
rescue => err
|
96
|
+
raise "Error occurred while moving '#{password_filename}' to '#{password_filename + '.last'}'\n Causing Error: #{err}"
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
begin
|
101
|
+
File.open(password_filename, 'w') { |file| file.puts password }
|
102
|
+
rescue => err
|
103
|
+
raise "Error occurred while writing '#{password_filename}'\n Causing Error: #{err}"
|
104
|
+
end
|
105
|
+
puts
|
106
|
+
end
|
107
|
+
|
108
|
+
@remove_users.each do |user|
|
109
|
+
password_filename = "#{@target_dir}/#{user}"
|
110
|
+
|
111
|
+
if File.exists?(password_filename)
|
112
|
+
if Utils.yes_or_no("Are you sure you want to remove all entries for #{user}?", false)
|
113
|
+
show_password(user)
|
114
|
+
|
115
|
+
last_password_filename = password_filename + '.last'
|
116
|
+
if File.exists?(last_password_filename)
|
117
|
+
File.delete(last_password_filename)
|
118
|
+
puts "#{last_password_filename} deleted"
|
119
|
+
end
|
120
|
+
|
121
|
+
File.delete(password_filename)
|
122
|
+
puts "#{password_filename} deleted"
|
123
|
+
end
|
124
|
+
end
|
125
|
+
puts
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
@@ -0,0 +1,82 @@
|
|
1
|
+
module Simp::Cli::Commands; end
|
2
|
+
|
3
|
+
class Simp::Cli::Commands::Puppeteval < Simp::Cli
|
4
|
+
require 'facter'
|
5
|
+
|
6
|
+
def self.help
|
7
|
+
puts "This tool gathers metric information for a Puppet run that it will run."
|
8
|
+
end
|
9
|
+
|
10
|
+
def self.run(args = Array.new)
|
11
|
+
|
12
|
+
data = {
|
13
|
+
:facter => Facter.to_hash,
|
14
|
+
:puppet_tags => ['--test','--evaltrace','--summarize'],
|
15
|
+
:cpuinfo => [],
|
16
|
+
:meminfo => {},
|
17
|
+
:summarize => {},
|
18
|
+
:evaltrace => []
|
19
|
+
}
|
20
|
+
|
21
|
+
proc_hash = Hash.new
|
22
|
+
File.open('/proc/cpuinfo', 'r').each do |line|
|
23
|
+
if line =~ /(.*)\s*: (.*)/
|
24
|
+
proc_hash[$1.strip] = $2
|
25
|
+
elsif line =~ /\A\s*\z/
|
26
|
+
data[:cpuinfo] << proc_hash
|
27
|
+
proc_hash = Hash.new
|
28
|
+
end
|
29
|
+
end
|
30
|
+
data[:cpuinfo] << proc_hash if !proc_hash.empty?
|
31
|
+
|
32
|
+
File.open('/proc/meminfo', 'r').each do |line|
|
33
|
+
if line =~ /(.*):\s*([0-9]*( kB)?)/
|
34
|
+
data[:meminfo][$1] = $2
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
# Wait for puppet to not be currently running...
|
39
|
+
puppet_running = true
|
40
|
+
found_puppet = false
|
41
|
+
while puppet_running do
|
42
|
+
ps_output = %x{/bin/ps aux | /bin/grep puppet}.each_line do |l|
|
43
|
+
if l =~ /\/usr\/s?bin\/puppet(d|\s+agent)/
|
44
|
+
sleep(2)
|
45
|
+
found_puppet = true
|
46
|
+
break
|
47
|
+
end
|
48
|
+
end
|
49
|
+
puppet_running = found_puppet
|
50
|
+
found_puppet = false
|
51
|
+
end
|
52
|
+
|
53
|
+
PTY.spawn("/usr/bin/puppet agent --test --evaltrace --summarize 2> /dev/null") do |read, write, pid|
|
54
|
+
begin
|
55
|
+
at_summary = false
|
56
|
+
read.each do |line|
|
57
|
+
if at_summary
|
58
|
+
if line =~ /\A(\S+.*):/
|
59
|
+
summary_hash = $1
|
60
|
+
data[:summarize][summary_hash] = Hash.new
|
61
|
+
elsif line =~ /\s+(\S+.*): ([0-9\.]*)/
|
62
|
+
data[:summarize][summary_hash][$1] = $2
|
63
|
+
end
|
64
|
+
end
|
65
|
+
if line =~ /info: (.*): Evaluated in (.*) seconds/
|
66
|
+
data[:evaltrace] << { :resource => $1, :time => $2 }
|
67
|
+
elsif line =~ /notice: Finished catalog run in ([0-9\.]*) seconds/
|
68
|
+
data[:evaltrace] << { :resource => "catalog run", :time => $1 }
|
69
|
+
at_summary = true
|
70
|
+
end
|
71
|
+
puts line
|
72
|
+
end
|
73
|
+
rescue Errno::EIO
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
FileUtils.mkdir_p("/var/tmp/simp_mit/")
|
78
|
+
File.open( "/var/tmp/simp_mit/simp_#{Time.now.to_i}_#{Socket.gethostname}.yml", 'w') do |file|
|
79
|
+
YAML::dump(data, file)
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
@@ -0,0 +1,95 @@
|
|
1
|
+
module Simp::Cli::Commands; end
|
2
|
+
|
3
|
+
class Simp::Cli::Commands::Runpuppet < Simp::Cli
|
4
|
+
require 'fileutils'
|
5
|
+
|
6
|
+
@conf_dir = File.expand_path('~/.simp')
|
7
|
+
@host_file = "#{@conf_dir}/hosts"
|
8
|
+
@gen_host_list = true
|
9
|
+
@max_parallel = 10
|
10
|
+
@timeout = -1
|
11
|
+
|
12
|
+
@opt_parser = OptionParser.new do |opts|
|
13
|
+
opts.banner = "\n === The SIMP RunPuppet Tool ==="
|
14
|
+
opts.separator ""
|
15
|
+
opts.separator "The SIMP RunPuppet Tool allows you to run the Puppet agent on a list of hosts."
|
16
|
+
opts.separator ""
|
17
|
+
opts.separator "Some requirements to use the tool:"
|
18
|
+
opts.separator " * the user must have SSH access to all of the list hosts"
|
19
|
+
opts.separator " * the user cannot be root"
|
20
|
+
opts.separator " * each target host must be able to run, with sudo, the following commands:"
|
21
|
+
opts.separator " - /usr/sbin/puppetd"
|
22
|
+
opts.separator " - /usr/sbin/puppetca"
|
23
|
+
opts.separator ""
|
24
|
+
opts.separator "OPTIONS:\n"
|
25
|
+
|
26
|
+
opts.on("-H", "--hosts FILE", "FILE containing a list of hosts that Puppet should be run on") do |file|
|
27
|
+
@host_file = file
|
28
|
+
@gen_host_list = false
|
29
|
+
end
|
30
|
+
|
31
|
+
opts.on("-p", "--par NUM", "Maximum number of parallel threads. Defaults to 10.") do |num|
|
32
|
+
@max_parallel = num
|
33
|
+
end
|
34
|
+
|
35
|
+
opts.on("-t", "--timeout SEC", "Set timeout to SEC seconds. Defaults to -1 (no timeout).",
|
36
|
+
"\033[1mWARNING\033[0m: If your Puppet run takes more than SEC seconds, very bad things can happen!",
|
37
|
+
"(i.e. your Puppet run will be killed)") do |sec|
|
38
|
+
@timeout = sec
|
39
|
+
end
|
40
|
+
|
41
|
+
opts.on("-h", "--help", "Print this message") do
|
42
|
+
puts opts
|
43
|
+
exit 0
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def self.run(args = Array.new)
|
48
|
+
super
|
49
|
+
|
50
|
+
raise Simp::Runpuppet::Error.new("SIMP RunPuppet cannot be run as 'root'.") if Process.uid == 0
|
51
|
+
|
52
|
+
host_list = Array.new
|
53
|
+
if @gen_host_list
|
54
|
+
host_list = %x{cd /;sudo /usr/sbin/puppetca --list --all}.split("\n").map do |host|
|
55
|
+
host.split(/\(.*\)/).first.split(/\s+/).last.delete('"')
|
56
|
+
end
|
57
|
+
else
|
58
|
+
File.open(@host_file).each_line do |line|
|
59
|
+
host_list << line.chomp
|
60
|
+
end
|
61
|
+
end
|
62
|
+
host_list.compact!
|
63
|
+
|
64
|
+
system("echo '#{ "Please review the lists of hosts to run puppet on:\n - #{host_list.join("\n - ")}" }' | less -F")
|
65
|
+
|
66
|
+
if Utils.yes_or_no("Run Puppet on all of the listed hosts?", false)
|
67
|
+
host_errors = Array.new
|
68
|
+
if @gen_host_list
|
69
|
+
File.open(@host_file, 'w') do
|
70
|
+
host_list.each { |host| file.puts host }
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
puts "This may take some time..."
|
75
|
+
%x{pssh -f -t #{@timeout} -p #{@max_parallel} -h #{@host_file} -OStrictHostKeyChecking=no "cd /; sudo /usr/sbin/puppetd --test"}.each_line do |line|
|
76
|
+
puts line
|
77
|
+
if line =~ /\[\d+\].*\[FAILURE\]\s([A-Za-z0-9\-\.]+).*/
|
78
|
+
host_errors << $1
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
if host_errors.empty?
|
83
|
+
puts "Successfully ran Puppet for the #{host_list.size} hosts listed in #{@host_file}."
|
84
|
+
else
|
85
|
+
timestamp = Time.new.strftime("%Y%m%d%H%M")
|
86
|
+
filepath = File.expand_path("#{@conf_dir}/pssh_error#{timestamp}")
|
87
|
+
FileUtils.mkpath(File.dirname(filepath))
|
88
|
+
File.open(filepath, 'w') do file
|
89
|
+
host_errors.each { |err| file.puts err }
|
90
|
+
end
|
91
|
+
raise "Errors while running Puppet, outputting list of hosts with errors to #{@conf_dir}/pssh_error#{timestamp}"
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
@@ -0,0 +1,456 @@
|
|
1
|
+
require 'highline/import'
|
2
|
+
require 'puppet'
|
3
|
+
require 'yaml'
|
4
|
+
require File.expand_path( 'utils', File.dirname(__FILE__) )
|
5
|
+
require 'highline'
|
6
|
+
|
7
|
+
module Simp; end
|
8
|
+
class Simp::Cli; end
|
9
|
+
module Simp::Cli::Config
|
10
|
+
class Item
|
11
|
+
attr_accessor :key, :value, :description, :fact
|
12
|
+
attr_accessor :skip_query, :skip_apply, :skip_yaml, :silent
|
13
|
+
attr_accessor :die_on_apply_fail, :allow_user_apply
|
14
|
+
attr_accessor :config_items
|
15
|
+
attr_accessor :next_items_tree
|
16
|
+
attr_accessor :fail_on_missing_answer
|
17
|
+
|
18
|
+
def initialize(key = nil, description = nil)
|
19
|
+
@key = key # answers file key for the config Item
|
20
|
+
@description = description # A text description of the Item
|
21
|
+
@value = nil # value (decided by user)
|
22
|
+
@fact = nil # Facter fact to query OS value
|
23
|
+
|
24
|
+
@skip_query = false # skip the query and use the default_value
|
25
|
+
@skip_apply = false # skip the apply
|
26
|
+
@skip_yaml = false # skip yaml output
|
27
|
+
@silent = false # no output to stdout/Highline
|
28
|
+
@die_on_apply_fail = false # halt simp config if apply fails
|
29
|
+
@allow_user_apply = false # allow non-superuser to apply
|
30
|
+
@fail_on_missing_answer = false # error out if @value is not pre-populated
|
31
|
+
|
32
|
+
@config_items = {} # a hash of all previous Config::Items
|
33
|
+
# a Hash of additional Items that this Item may need to add to the Queue
|
34
|
+
# the keys of the Has are used to look up the queue
|
35
|
+
# format:
|
36
|
+
# 'answer1' => [ Item1, Item2, .. ]
|
37
|
+
# 'answer2' => [ Item3, Item4, .. ]
|
38
|
+
@next_items_tree = {}
|
39
|
+
end
|
40
|
+
|
41
|
+
# methods used to infer Item#value
|
42
|
+
# --------------------------------------------------------------------------
|
43
|
+
|
44
|
+
# value of item as read from OS (via Facter)
|
45
|
+
def os_value
|
46
|
+
Facter.value( @fact ) unless @fact.nil?
|
47
|
+
end
|
48
|
+
|
49
|
+
|
50
|
+
# value of Item as read from puppet (via Hiera) #TODO: not used yet
|
51
|
+
def puppet_value; nil; end
|
52
|
+
|
53
|
+
|
54
|
+
# value of Item as recommended by Very Clever Logic (tm)
|
55
|
+
def recommended_value; nil; end
|
56
|
+
# --------------------------------------------------------------------------
|
57
|
+
|
58
|
+
|
59
|
+
# String in yaml answer file format, with comments (if any)
|
60
|
+
def to_yaml_s
|
61
|
+
fail '@key is empty' if "#{@key}".empty?
|
62
|
+
|
63
|
+
x = "=== #{@key} ===\n"
|
64
|
+
x += "#{(description || 'FIXME: NO DESCRIPTION GIVEN')}\n"
|
65
|
+
|
66
|
+
# comment every line that describes the item:
|
67
|
+
x = x.each_line.map{ |y| "# #{y}" }.join
|
68
|
+
|
69
|
+
# add yaml (but stripped of frontmatter and first indent)
|
70
|
+
# TODO: should we be using SafeYAML? http://danieltao.com/safe_yaml/
|
71
|
+
x += { @key => @value }.to_yaml.gsub(/^---\s*\n/m, '').gsub(/^ /, '' )
|
72
|
+
x += "\n"
|
73
|
+
|
74
|
+
if @skip_yaml
|
75
|
+
x.gsub( /^/, '### ' )
|
76
|
+
else
|
77
|
+
x
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
|
82
|
+
# --------------------------------------------------------------------------
|
83
|
+
# Pretty stdout/stdin methods
|
84
|
+
# --------------------------------------------------------------------------
|
85
|
+
# print a pretty banner to describe an item
|
86
|
+
def print_banner
|
87
|
+
return if @silent
|
88
|
+
say_blue "=== #{@key} ===", ['BOLD']
|
89
|
+
say_blue description
|
90
|
+
say_blue " - os value: #{os_value}" if os_value
|
91
|
+
say_blue " - os value: #{puppet_value}" if puppet_value
|
92
|
+
say_blue " - recommended value: #{recommended_value}" if recommended_value
|
93
|
+
say_blue " - chosen value: #{@value}" if @value
|
94
|
+
end
|
95
|
+
|
96
|
+
|
97
|
+
# print a pretty summary of the Item's key+value, printed to stdout
|
98
|
+
def print_summary
|
99
|
+
return if @silent
|
100
|
+
fail '@key is empty' if "#{@key}".empty?
|
101
|
+
say( "#{@key} = '<%= color( %q{#{value}}, BOLD )%>'\n" )
|
102
|
+
end
|
103
|
+
|
104
|
+
|
105
|
+
# choose @value of Item
|
106
|
+
def query
|
107
|
+
extra = query_status
|
108
|
+
|
109
|
+
if @value.nil? && @fail_on_missing_answer
|
110
|
+
say( "<%= color(%q(FATAL: no answer for '), RED) %>" +
|
111
|
+
"<%= color(%q{#{extra}#{@key}}, BOLD, RED)%>" +
|
112
|
+
"<%= color(%q('; exiting!), RED)%>\n" )
|
113
|
+
exit 1
|
114
|
+
end
|
115
|
+
|
116
|
+
if !@skip_query && @value.nil?
|
117
|
+
print_banner
|
118
|
+
@value = query_ask
|
119
|
+
end
|
120
|
+
|
121
|
+
# summarize the item's status after the query is complete
|
122
|
+
say( "#{extra}#{@key} = '<%= color( %q{#{@value}}, BOLD )%>'\n" ) unless @silent
|
123
|
+
end
|
124
|
+
|
125
|
+
|
126
|
+
def query_status
|
127
|
+
extra = ''
|
128
|
+
if !@value.nil?
|
129
|
+
extra = "<%= color( %q{(answered)}, CYAN, BOLD)%> "
|
130
|
+
elsif @skip_query
|
131
|
+
extra = "<%= color( %q{(noninteractive)}, CYAN, BOLD)%> "
|
132
|
+
@value = default_value
|
133
|
+
end
|
134
|
+
extra
|
135
|
+
end
|
136
|
+
|
137
|
+
|
138
|
+
# ask an interactive question (via stdout/stdin)
|
139
|
+
def query_ask
|
140
|
+
# NOTE: This trailing space at the end of the String obliquely instructs
|
141
|
+
# Highline to keep the prompt on the same line as the question. If the
|
142
|
+
# String did not end with a space or tab, Highline would move the input
|
143
|
+
# prompt to the next line (which, for our purposes, looks confusing)
|
144
|
+
value = ask( "<%= color('#{@key}', WHITE, BOLD) %>: ",
|
145
|
+
highline_question_type ) do |q|
|
146
|
+
q.default = default_value unless default_value.to_s.empty?
|
147
|
+
|
148
|
+
# validate input via the validate() method
|
149
|
+
q.validate = lambda{ |x| validate( x )}
|
150
|
+
|
151
|
+
# if the answer is not valid, construct a reply:
|
152
|
+
q.responses[:not_valid] = "<%= color( %q{Invalid answer!}, RED ) %>\n"
|
153
|
+
q.responses[:not_valid] += "<%= color( %q{#{ (not_valid_message || description) }}, YELLOW) %>\n"
|
154
|
+
q.responses[:not_valid] += "#{q.question} |#{q.default}|"
|
155
|
+
|
156
|
+
query_extras q
|
157
|
+
end
|
158
|
+
value
|
159
|
+
end
|
160
|
+
|
161
|
+
|
162
|
+
# returns the default answer to Item#query
|
163
|
+
def default_value
|
164
|
+
@value || recommended_value
|
165
|
+
end
|
166
|
+
|
167
|
+
|
168
|
+
def query_extras( q ); q; end
|
169
|
+
|
170
|
+
|
171
|
+
# returns true if x is a valid value
|
172
|
+
def validate( _x )
|
173
|
+
msg = 'ERROR: Item.validate() not implemented!'
|
174
|
+
msg += "\nTODO: cover common type-based validations?"
|
175
|
+
msg += "\nTODO: Offer validation objects?"
|
176
|
+
fail msg
|
177
|
+
end
|
178
|
+
|
179
|
+
|
180
|
+
def next_items
|
181
|
+
@next_items_tree.fetch( @value, [] )
|
182
|
+
end
|
183
|
+
|
184
|
+
|
185
|
+
# optional message to show users when invalid input is entered
|
186
|
+
def not_valid_message; nil; end
|
187
|
+
|
188
|
+
|
189
|
+
# A helper method that highline can use to cast String answers to the ask
|
190
|
+
# in query(). nil means don't cast, Date casts into a date, etc.
|
191
|
+
# A lambda can be used for sanitization.
|
192
|
+
#
|
193
|
+
# Descendants of Item are very likely to override this method.
|
194
|
+
def highline_question_type; nil; end
|
195
|
+
|
196
|
+
# convenience_method to print formatted information
|
197
|
+
def say_blue( msg, options=[] )
|
198
|
+
options = options.unshift( '' ) unless options.empty?
|
199
|
+
say "<%= color(%q{#{msg}}, CYAN #{options.join(', ')}) %>\n" unless @silent
|
200
|
+
end
|
201
|
+
def say_yellow( msg, options=[] )
|
202
|
+
options = options.unshift( '' ) unless options.empty?
|
203
|
+
say "<%= color(%q{#{msg}}, YELLOW #{options.join(', ')}) %>\n" unless @silent
|
204
|
+
end
|
205
|
+
def say_red( msg, options=[] )
|
206
|
+
options = options.unshift( '' ) unless options.empty?
|
207
|
+
say "<%= color(%q{#{msg}}, RED #{options.join(', ')}) %>\n" unless @silent
|
208
|
+
end
|
209
|
+
def say_green( msg, options=[] )
|
210
|
+
options = options.unshift( '' ) unless options.empty?
|
211
|
+
say "<%= color(%q{#{msg}}, GREEN #{options.join(', ')}) %>\n" unless @silent
|
212
|
+
end
|
213
|
+
|
214
|
+
|
215
|
+
def safe_apply; nil; end
|
216
|
+
def apply; nil; end
|
217
|
+
end
|
218
|
+
|
219
|
+
|
220
|
+
|
221
|
+
# A Item that asks for lists instead of Strings
|
222
|
+
#
|
223
|
+
# note that @value is a Strin
|
224
|
+
class YesNoItem < Item
|
225
|
+
def not_valid_message
|
226
|
+
"enter 'yes' or 'no'"
|
227
|
+
end
|
228
|
+
|
229
|
+
def validate( v )
|
230
|
+
return true if (v.class == TrueClass || v.class == FalseClass)
|
231
|
+
( v =~ /^(y(es)?|true|false|no?)$/i ) ? true : false
|
232
|
+
end
|
233
|
+
|
234
|
+
# NOTE: Highline should transform the input to a boolean but doesn't. Why?
|
235
|
+
# REJECTED: Override #query_ask using Highline's #agree? *** no, can't bool
|
236
|
+
def highline_question_type
|
237
|
+
lambda do |str|
|
238
|
+
return true if ( str =~ /^(y(es)?|true)$/i ? true : false || str.class == TrueClass )
|
239
|
+
return false if ( str =~ /^(n(o)?|false)$/i ? true : false || str.class == FalseClass )
|
240
|
+
nil
|
241
|
+
end
|
242
|
+
end
|
243
|
+
|
244
|
+
# NOTE: when used from query_ask, the highline_question_type lamba doesn't
|
245
|
+
# always cast internal type of @value to a boolean. As a workaround, we
|
246
|
+
# cast it here before it is committed to the super's YAML output.
|
247
|
+
def to_yaml_s
|
248
|
+
_value = @value
|
249
|
+
@value = highline_question_type.call @value
|
250
|
+
x = super
|
251
|
+
@value = _value
|
252
|
+
x
|
253
|
+
end
|
254
|
+
|
255
|
+
def next_items
|
256
|
+
@next_items_tree.fetch( highline_question_type.call( @value ), [] )
|
257
|
+
end
|
258
|
+
end
|
259
|
+
|
260
|
+
|
261
|
+
|
262
|
+
|
263
|
+
# An Item that asks for Passwords, with:
|
264
|
+
# - special validation
|
265
|
+
# - invisible input
|
266
|
+
# - optional password generation
|
267
|
+
class PasswordItem < Item
|
268
|
+
attr_accessor :can_generate, :generate_by_default
|
269
|
+
def initialize
|
270
|
+
super
|
271
|
+
@can_generate = true
|
272
|
+
@generate_by_default = true
|
273
|
+
end
|
274
|
+
|
275
|
+
|
276
|
+
def query_extras( q )
|
277
|
+
q.echo = '*' # don't print password characters to stdout
|
278
|
+
end
|
279
|
+
|
280
|
+
|
281
|
+
def encrypt( password, salt=nil )
|
282
|
+
say_yellow 'WARNING: password not encrypted; override in child class'
|
283
|
+
password
|
284
|
+
end
|
285
|
+
|
286
|
+
|
287
|
+
def query_generate_password
|
288
|
+
password = false
|
289
|
+
default = @generate_by_default ? 'yes' : 'no'
|
290
|
+
if agree( "auto-generate a password? " ){ |q| q.default = default }
|
291
|
+
password = Simp::Cli::Config::Utils.generate_password
|
292
|
+
say "<%= color( %q{#{''.ljust(80,'-')}}, GREEN)%>\n"
|
293
|
+
say '<%= color( %q{NOTE: }, GREEN, BOLD)%>' +
|
294
|
+
"<%= color( %q{ the generated password is: }) %>\n"
|
295
|
+
say "\n"
|
296
|
+
say "<%= color( %q{ #{password}}, YELLOW, BOLD )%> "
|
297
|
+
say "\n"
|
298
|
+
say "\n"
|
299
|
+
say 'Please remember it!'
|
300
|
+
say "<%= color( %q{#{''.ljust(80,'-')}}, GREEN)%>\n"
|
301
|
+
say_blue '*** Press enter to continue ***' , ['BOLD', 'BLINK']
|
302
|
+
ask ''
|
303
|
+
end
|
304
|
+
password
|
305
|
+
end
|
306
|
+
|
307
|
+
|
308
|
+
# ask for the password twice (and verify that both match)
|
309
|
+
def query_ask
|
310
|
+
password = false
|
311
|
+
password = query_generate_password if @can_generate
|
312
|
+
|
313
|
+
while !password
|
314
|
+
answers = []
|
315
|
+
[0,1].each{ |x|
|
316
|
+
say "please enter a password:" if x == 0
|
317
|
+
say "please confirm the password:" if x == 1
|
318
|
+
answers[x] = super
|
319
|
+
}
|
320
|
+
if answers.first == answers.last
|
321
|
+
password = answers.first
|
322
|
+
else
|
323
|
+
say_yellow( 'WARNING: passwords did not match! Please try again.' )
|
324
|
+
end
|
325
|
+
end
|
326
|
+
|
327
|
+
encrypt password
|
328
|
+
end
|
329
|
+
|
330
|
+
|
331
|
+
def validate x
|
332
|
+
result = true
|
333
|
+
begin
|
334
|
+
Simp::Cli::Config::Utils.validate_password x
|
335
|
+
rescue Simp::Cli::Config::PasswordError => e
|
336
|
+
say_yellow "WARNING: Invalid Password: #{e.message}"
|
337
|
+
result = false
|
338
|
+
end
|
339
|
+
result
|
340
|
+
end
|
341
|
+
end
|
342
|
+
|
343
|
+
|
344
|
+
# A Item that asks for lists instead of Strings
|
345
|
+
#
|
346
|
+
# note that @value is now an Array
|
347
|
+
class ListItem < Item
|
348
|
+
attr_accessor :allow_empty_list
|
349
|
+
|
350
|
+
def initialize
|
351
|
+
super
|
352
|
+
@allow_empty_list = false
|
353
|
+
end
|
354
|
+
|
355
|
+
def not_valid_message
|
356
|
+
extra = 'hit enter to skip'
|
357
|
+
extra = "hit enter to accept default value" if default_value
|
358
|
+
"enter a comma or space-delimited list (#{extra})"
|
359
|
+
end
|
360
|
+
|
361
|
+
def query_extras( q )
|
362
|
+
# NOTE: this is a hack to massage Array input to/from a highline query.
|
363
|
+
# It would probably be better (but more complex) to provide native Array
|
364
|
+
# support for highline.
|
365
|
+
# TODO: Override #query_ask using Highline's #gather?
|
366
|
+
q.default = q.default.join( " " ) if q.default.is_a? Array
|
367
|
+
reminder = ::HighLine.color( not_valid_message,
|
368
|
+
::HighLine.const_get('YELLOW') )
|
369
|
+
q.question = "#{reminder}\n#{q.question}"
|
370
|
+
q
|
371
|
+
end
|
372
|
+
|
373
|
+
def highline_question_type
|
374
|
+
# Convert the String (delimited by comma and/or whitespace) answer into an array
|
375
|
+
lambda { |str|
|
376
|
+
str = str.split(/,\s*|,?\s+/) if str.is_a? String
|
377
|
+
str
|
378
|
+
}
|
379
|
+
end
|
380
|
+
|
381
|
+
# validate the list and each item in the list
|
382
|
+
def validate( list )
|
383
|
+
# reuse the highline lambda to santize input
|
384
|
+
return true if (@allow_empty_list && list.nil?)
|
385
|
+
list = highline_question_type.call( list ) if !list.is_a? Array
|
386
|
+
return false if !list.is_a?(Array)
|
387
|
+
return false if (!@allow_empty_list && list.empty? )
|
388
|
+
list.each{ |item|
|
389
|
+
return false if !validate_item( item )
|
390
|
+
}
|
391
|
+
true
|
392
|
+
end
|
393
|
+
|
394
|
+
# validate a single list item
|
395
|
+
def validate_item( x )
|
396
|
+
fail 'not implemented!'
|
397
|
+
end
|
398
|
+
end
|
399
|
+
|
400
|
+
|
401
|
+
# mixin that provides common logic for safe_apply()
|
402
|
+
module SafeApplying
|
403
|
+
def safe_apply
|
404
|
+
extra = ''
|
405
|
+
not_root_msg = ''
|
406
|
+
if !@allow_user_apply
|
407
|
+
not_root_msg = ENV.fetch('USER') == 'root' ? '' : ' [**user is not root**] '
|
408
|
+
end
|
409
|
+
|
410
|
+
if @skip_apply || (not_root_msg.size != 0)
|
411
|
+
extra = "<%= color( %q{(skipping apply#{not_root_msg})}, MAGENTA, BOLD)%> "
|
412
|
+
say( "#{extra}#{@key}" ) unless @silent
|
413
|
+
if !(@value.nil? || @value.class == TrueClass || @value.class == FalseClass || @value.empty?)
|
414
|
+
say( "= '<%= color( %q{#{@value}}, BOLD )%>'\n" ) unless @silent
|
415
|
+
end
|
416
|
+
else
|
417
|
+
extra = "<%= color( %q{(applying changes)}, GREEN, BOLD)%> "
|
418
|
+
say( "#{extra}for #{@key}\n" ) unless @silent
|
419
|
+
begin
|
420
|
+
result = apply
|
421
|
+
if result
|
422
|
+
extra = "<%= color( %q{(change applied)}, GREEN, BOLD)%> "
|
423
|
+
else
|
424
|
+
extra = "<%= color( %q{(change failed)}, RED, BOLD)%> "
|
425
|
+
end
|
426
|
+
say( "#{extra}for #{@key}\n" ) unless @silent
|
427
|
+
rescue Exception => e
|
428
|
+
extra = "<%= color( %q{(change failed)}, RED, BOLD) %> "
|
429
|
+
say( "#{extra}for #{@key}:\n#{e.message}" )
|
430
|
+
say "<%= color( %q{#{e.message.to_s.gsub( /^/, ' ' )}}, RED) %> \n"
|
431
|
+
|
432
|
+
# Some failures should be punished by death
|
433
|
+
fail e if @die_on_apply_fail
|
434
|
+
end
|
435
|
+
end
|
436
|
+
end
|
437
|
+
end
|
438
|
+
|
439
|
+
|
440
|
+
# A special Item that is never interactive, but applies some configuration
|
441
|
+
class ActionItem < Item
|
442
|
+
include Simp::Cli::Config::SafeApplying
|
443
|
+
|
444
|
+
def initialize
|
445
|
+
super
|
446
|
+
end
|
447
|
+
|
448
|
+
# internal method to change the system (returns the result of the apply)
|
449
|
+
def apply; nil; end
|
450
|
+
|
451
|
+
# don't be interactive!
|
452
|
+
def validate( x ); true; end
|
453
|
+
def query; nil; end
|
454
|
+
def to_yaml_s; nil; end
|
455
|
+
end
|
456
|
+
end
|