simp-cli 1.0.12

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (150) hide show
  1. checksums.yaml +15 -0
  2. data/LICENSE +27 -0
  3. data/README.md +48 -0
  4. data/Rakefile +142 -0
  5. data/bin/simp +5 -0
  6. data/lib/simp/cli.rb +88 -0
  7. data/lib/simp/cli/commands/bootstrap.rb +275 -0
  8. data/lib/simp/cli/commands/check.rb +163 -0
  9. data/lib/simp/cli/commands/cleancerts.rb +114 -0
  10. data/lib/simp/cli/commands/config.rb +235 -0
  11. data/lib/simp/cli/commands/doc.rb +14 -0
  12. data/lib/simp/cli/commands/passgen.rb +128 -0
  13. data/lib/simp/cli/commands/puppeteval.rb +82 -0
  14. data/lib/simp/cli/commands/runpuppet.rb +95 -0
  15. data/lib/simp/cli/config/item.rb +456 -0
  16. data/lib/simp/cli/config/item/add_ldap_to_hiera.rb +43 -0
  17. data/lib/simp/cli/config/item/answers_yaml_file_writer.rb +58 -0
  18. data/lib/simp/cli/config/item/certificates.rb +39 -0
  19. data/lib/simp/cli/config/item/client_nets.rb +65 -0
  20. data/lib/simp/cli/config/item/common_runlevel_default.rb +32 -0
  21. data/lib/simp/cli/config/item/dns_search.rb +48 -0
  22. data/lib/simp/cli/config/item/dns_servers.rb +57 -0
  23. data/lib/simp/cli/config/item/failover_log_servers.rb +27 -0
  24. data/lib/simp/cli/config/item/gateway.rb +32 -0
  25. data/lib/simp/cli/config/item/grub_password.rb +51 -0
  26. data/lib/simp/cli/config/item/hostname.rb +24 -0
  27. data/lib/simp/cli/config/item/hostname_conf.rb +48 -0
  28. data/lib/simp/cli/config/item/ipaddress.rb +46 -0
  29. data/lib/simp/cli/config/item/is_master_yum_server.rb +23 -0
  30. data/lib/simp/cli/config/item/ldap_base_dn.rb +38 -0
  31. data/lib/simp/cli/config/item/ldap_bind_dn.rb +34 -0
  32. data/lib/simp/cli/config/item/ldap_bind_hash.rb +28 -0
  33. data/lib/simp/cli/config/item/ldap_bind_pw.rb +24 -0
  34. data/lib/simp/cli/config/item/ldap_master.rb +33 -0
  35. data/lib/simp/cli/config/item/ldap_root_dn.rb +42 -0
  36. data/lib/simp/cli/config/item/ldap_root_hash.rb +35 -0
  37. data/lib/simp/cli/config/item/ldap_sync_dn.rb +24 -0
  38. data/lib/simp/cli/config/item/ldap_sync_hash.rb +28 -0
  39. data/lib/simp/cli/config/item/ldap_sync_pw.rb +26 -0
  40. data/lib/simp/cli/config/item/ldap_uri.rb +43 -0
  41. data/lib/simp/cli/config/item/log_servers.rb +27 -0
  42. data/lib/simp/cli/config/item/netmask.rb +39 -0
  43. data/lib/simp/cli/config/item/network_conf.rb +63 -0
  44. data/lib/simp/cli/config/item/network_dhcp.rb +27 -0
  45. data/lib/simp/cli/config/item/network_interface.rb +41 -0
  46. data/lib/simp/cli/config/item/network_setup_nic.rb +28 -0
  47. data/lib/simp/cli/config/item/ntp_servers.rb +69 -0
  48. data/lib/simp/cli/config/item/puppet_autosign.rb +66 -0
  49. data/lib/simp/cli/config/item/puppet_ca.rb +31 -0
  50. data/lib/simp/cli/config/item/puppet_ca_port.rb +28 -0
  51. data/lib/simp/cli/config/item/puppet_conf.rb +98 -0
  52. data/lib/simp/cli/config/item/puppet_fileserver.rb +104 -0
  53. data/lib/simp/cli/config/item/puppet_hosts_entry.rb +44 -0
  54. data/lib/simp/cli/config/item/puppet_server.rb +30 -0
  55. data/lib/simp/cli/config/item/puppet_server_ip.rb +25 -0
  56. data/lib/simp/cli/config/item/puppetdb_port.rb +25 -0
  57. data/lib/simp/cli/config/item/puppetdb_server.rb +26 -0
  58. data/lib/simp/cli/config/item/remove_ldap_from_hiera.rb +47 -0
  59. data/lib/simp/cli/config/item/rename_fqdn_yaml.rb +40 -0
  60. data/lib/simp/cli/config/item/rsync_base.rb +37 -0
  61. data/lib/simp/cli/config/item/rsync_server.rb +44 -0
  62. data/lib/simp/cli/config/item/rsync_timeout.rb +26 -0
  63. data/lib/simp/cli/config/item/set_grub_password.rb +19 -0
  64. data/lib/simp/cli/config/item/simp_yum_servers.rb +30 -0
  65. data/lib/simp/cli/config/item/use_auditd.rb +19 -0
  66. data/lib/simp/cli/config/item/use_fips.rb +46 -0
  67. data/lib/simp/cli/config/item/use_iptables.rb +22 -0
  68. data/lib/simp/cli/config/item/use_ldap.rb +19 -0
  69. data/lib/simp/cli/config/item/use_selinux.rb +32 -0
  70. data/lib/simp/cli/config/item/yum_repositories.rb +75 -0
  71. data/lib/simp/cli/config/item_list_factory.rb +236 -0
  72. data/lib/simp/cli/config/questionnaire.rb +86 -0
  73. data/lib/simp/cli/config/utils.rb +128 -0
  74. data/lib/simp/cli/lib/utils.rb +114 -0
  75. data/lib/simp/simp.rb +77 -0
  76. data/spec/lib/simp/cli/commands/config_spec.rb +42 -0
  77. data/spec/lib/simp/cli/config/item/add_ldap_to_hiera_spec.rb +58 -0
  78. data/spec/lib/simp/cli/config/item/answers_yaml_file_writer_spec.rb +86 -0
  79. data/spec/lib/simp/cli/config/item/certificates_spec.rb +50 -0
  80. data/spec/lib/simp/cli/config/item/client_nets_spec.rb +66 -0
  81. data/spec/lib/simp/cli/config/item/common_runlevel_default_spec.rb +27 -0
  82. data/spec/lib/simp/cli/config/item/dns_search_spec.rb +74 -0
  83. data/spec/lib/simp/cli/config/item/dns_servers_spec.rb +76 -0
  84. data/spec/lib/simp/cli/config/item/failover_log_servers_spec.rb +49 -0
  85. data/spec/lib/simp/cli/config/item/files/FakeCA/cacertkey +1 -0
  86. data/spec/lib/simp/cli/config/item/files/FakeCA/gencerts_nopass.sh +10 -0
  87. data/spec/lib/simp/cli/config/item/files/autosign.conf.new +11 -0
  88. data/spec/lib/simp/cli/config/item/files/autosign.conf.used +15 -0
  89. data/spec/lib/simp/cli/config/item/files/fileserver.conf +41 -0
  90. data/spec/lib/simp/cli/config/item/files/hosts +2 -0
  91. data/spec/lib/simp/cli/config/item/files/hosts.old_puppet_entry +3 -0
  92. data/spec/lib/simp/cli/config/item/files/puppet.conf +25 -0
  93. data/spec/lib/simp/cli/config/item/files/puppet.your.domain.yaml +21 -0
  94. data/spec/lib/simp/cli/config/item/files/resolv.conf__multiple +10 -0
  95. data/spec/lib/simp/cli/config/item/files/resolv.conf__single +4 -0
  96. data/spec/lib/simp/cli/config/item/files/rsyncd.conf +225 -0
  97. data/spec/lib/simp/cli/config/item/gateway_spec.rb +23 -0
  98. data/spec/lib/simp/cli/config/item/grub_password_spec.rb +24 -0
  99. data/spec/lib/simp/cli/config/item/hostname_conf_spec.rb +27 -0
  100. data/spec/lib/simp/cli/config/item/hostname_spec.rb +22 -0
  101. data/spec/lib/simp/cli/config/item/ipaddress_spec.rb +40 -0
  102. data/spec/lib/simp/cli/config/item/is_master_yum_server_spec.rb +29 -0
  103. data/spec/lib/simp/cli/config/item/ldap_base_dn_spec.rb +23 -0
  104. data/spec/lib/simp/cli/config/item/ldap_bind_dn_spec.rb +23 -0
  105. data/spec/lib/simp/cli/config/item/ldap_bind_hash_spec.rb +23 -0
  106. data/spec/lib/simp/cli/config/item/ldap_bind_pw_spec.rb +21 -0
  107. data/spec/lib/simp/cli/config/item/ldap_master_spec.rb +37 -0
  108. data/spec/lib/simp/cli/config/item/ldap_root_dn_spec.rb +23 -0
  109. data/spec/lib/simp/cli/config/item/ldap_root_hash_spec.rb +23 -0
  110. data/spec/lib/simp/cli/config/item/ldap_sync_dn_spec.rb +22 -0
  111. data/spec/lib/simp/cli/config/item/ldap_sync_hash_spec.rb +23 -0
  112. data/spec/lib/simp/cli/config/item/ldap_sync_pw_spec.rb +21 -0
  113. data/spec/lib/simp/cli/config/item/ldap_uri_spec.rb +32 -0
  114. data/spec/lib/simp/cli/config/item/log_servers_spec.rb +49 -0
  115. data/spec/lib/simp/cli/config/item/netmask_spec.rb +28 -0
  116. data/spec/lib/simp/cli/config/item/network_conf_spec.rb +63 -0
  117. data/spec/lib/simp/cli/config/item/network_dhcp_spec.rb +11 -0
  118. data/spec/lib/simp/cli/config/item/network_interface_spec.rb +26 -0
  119. data/spec/lib/simp/cli/config/item/network_setup_nic_spec.rb +29 -0
  120. data/spec/lib/simp/cli/config/item/ntp_servers_spec.rb +43 -0
  121. data/spec/lib/simp/cli/config/item/puppet_autosign_spec.rb +55 -0
  122. data/spec/lib/simp/cli/config/item/puppet_ca_port_spec.rb +23 -0
  123. data/spec/lib/simp/cli/config/item/puppet_ca_spec.rb +22 -0
  124. data/spec/lib/simp/cli/config/item/puppet_conf_spec.rb +110 -0
  125. data/spec/lib/simp/cli/config/item/puppet_fileserver_spec.rb +53 -0
  126. data/spec/lib/simp/cli/config/item/puppet_hosts_entry_spec.rb +85 -0
  127. data/spec/lib/simp/cli/config/item/puppet_server_ip_spec.rb +24 -0
  128. data/spec/lib/simp/cli/config/item/puppet_server_spec.rb +22 -0
  129. data/spec/lib/simp/cli/config/item/puppetdb_port_spec.rb +25 -0
  130. data/spec/lib/simp/cli/config/item/puppetdb_server_spec.rb +25 -0
  131. data/spec/lib/simp/cli/config/item/remove_ldap_from_hiera_spec.rb +58 -0
  132. data/spec/lib/simp/cli/config/item/rename_fqdn_yaml_spec.rb +63 -0
  133. data/spec/lib/simp/cli/config/item/rsync_base_spec.rb +28 -0
  134. data/spec/lib/simp/cli/config/item/rsync_server_spec.rb +41 -0
  135. data/spec/lib/simp/cli/config/item/rsync_timeout_spec.rb +21 -0
  136. data/spec/lib/simp/cli/config/item/set_grub_password_spec.rb +29 -0
  137. data/spec/lib/simp/cli/config/item/simp_yum_servers_spec.rb +41 -0
  138. data/spec/lib/simp/cli/config/item/spec_helper.rb +22 -0
  139. data/spec/lib/simp/cli/config/item/use_auditd_spec.rb +29 -0
  140. data/spec/lib/simp/cli/config/item/use_fips_spec.rb +29 -0
  141. data/spec/lib/simp/cli/config/item/use_iptables_spec.rb +29 -0
  142. data/spec/lib/simp/cli/config/item/use_ldap_spec.rb +29 -0
  143. data/spec/lib/simp/cli/config/item/use_selinux_spec.rb +24 -0
  144. data/spec/lib/simp/cli/config/item/yum_repositories_spec.rb +94 -0
  145. data/spec/lib/simp/cli/config/item_spec.rb +106 -0
  146. data/spec/lib/simp/cli/config/spec_helper.rb +1 -0
  147. data/spec/lib/simp/cli/config/utils_spec.rb +131 -0
  148. data/spec/lib/simp/cli/spec_helper.rb +1 -0
  149. data/spec/spec_helper.rb +91 -0
  150. metadata +391 -0
@@ -0,0 +1,163 @@
1
+ module Simp::Cli::Commands; end
2
+
3
+ class Simp::Cli::Commands::Check < Simp::Cli
4
+ @opt_parser = OptionParser.new do |opts|
5
+ opts.banner = "*Options*"
6
+
7
+ opts.on("-A", "--all", "Run all checks, equivalent to -nkl") do
8
+ @check_network = true
9
+ @check_keys = true
10
+ @check_ldap = true
11
+ end
12
+
13
+ opts.on("-p", "--pre", "Run checks that should pass before first run, equivalent to -nk") do
14
+ @check_network = true
15
+ @check_keys = true
16
+ end
17
+
18
+ opts.on("-n", "--network", "Check network items") do
19
+ @check_network = true
20
+ end
21
+
22
+ opts.on("-k", "--keys", "Check that keys have been generated for the host") do
23
+ @check_keys = true
24
+ end
25
+
26
+ opts.on("-l", "--ldap", "Check validity of ldap passwords") do
27
+ @check_ldap = true
28
+ end
29
+
30
+ opts.on("-v", "--verbose", "Run verbosely") do
31
+ @verbose = true
32
+ end
33
+
34
+ opts.on("-r", "--report FILE", "Create a report in FILE. NOTE: if FILE exists, it will be overwritten!") do |file|
35
+ @report_file = file
36
+ end
37
+
38
+ opts.on("-h", "--help", "Print this message") do
39
+ puts opts
40
+ exit
41
+ end
42
+ end
43
+
44
+ def self.run(args)
45
+ raise "simp check Requires Arguments" if args.empty?
46
+
47
+ super
48
+
49
+ @version = Simp.version
50
+
51
+ report = []
52
+
53
+ system('clear')
54
+
55
+ if @check_network
56
+ report.push "\n***Starting Network Check***\n"
57
+
58
+ hostname = `hostname`.gsub!(/\s+/, '')
59
+
60
+ begin
61
+ network_hostname = `grep HOSTNAME /etc/sysconfig/network`.strip.match(/HOSTNAME\s*=\s*([^ ]*)/)[1]
62
+ rescue
63
+ report.push "ERROR: No hostname in /etc/sysconfig/network"
64
+ end
65
+
66
+ if hostname == network_hostname
67
+ report.push "Hostname matches hostname in /etc/sysconfig/network"
68
+ else
69
+ report.push "ERROR: Hostname does not match hostname in /etc/sysconfig/network"
70
+ end
71
+
72
+ if `grep ^127.0.0.1 /etc/hosts`.split("\n").any? { |line| line =~ /localhost.localdomain[\s+\z]/ and line =~ /localhost[\s+\z]/ }
73
+ report.push "Found valid entry for 127.0.0.1 in /etc/hosts"
74
+ else
75
+ report.push "ERROR: Did not find valid entry for 127.0.0.1 in /etc/hosts"
76
+ end
77
+
78
+ if `grep ^::1 /etc/hosts`.split("\n").any? { |line| line =~ /localhost6\.localdomain6(\s+|$)/ and line =~ /localhost6(\s+|$)/ }
79
+ report.push "Found valid entry for ::1 in /etc/hosts"
80
+ else
81
+ report.push "ERROR: Did not find valid entry for ::1 in /etc/hosts"
82
+ end
83
+ end
84
+
85
+ if @check_keys
86
+ report.push "\n***Starting Keys Check***\n"
87
+
88
+ key_count = 0
89
+ valid_key_count = 0
90
+
91
+ Dir.foreach("/etc/puppet/keydist") do |host|
92
+ if (host !~ /\A\.+\z/) and (host !~ /\Acacerts\z/) and File::directory?("/etc/puppet/keydist/#{host}")
93
+ Dir.foreach("/etc/puppet/keydist/#{host}") do |key|
94
+ if key =~ /\.pem\z/ or key =~ /\.pub\z/
95
+ key_count += 1
96
+
97
+ if `openssl verify -CApath /etc/puppet/keydist/cacerts /etc/puppet/keydist/#{host}/#{key}`.strip =~ /\s+OK\z/
98
+ valid_key_count += 1
99
+ report.push "Key /etc/puppet/keydist/#{host}/#{key} validated\n"
100
+ else
101
+ report.push "ERROR: Key /etc/puppet/keydist/#{host}/#{key} did not validate\n"
102
+ end
103
+ end
104
+ end
105
+ end
106
+ end
107
+
108
+ if key_count == 0
109
+ report.push "ERROR: No keys found (recursively) in /etc/puppet/keydist\n"
110
+ else
111
+ report.push "#{valid_key_count}/#{key_count} keys validated\n"
112
+ end
113
+ end
114
+
115
+ if @check_ldap
116
+ report.push "\n***Starting Ldap Check***\n"
117
+
118
+ binddn = ""
119
+ bindpw = ""
120
+ host = ""
121
+ base = ""
122
+
123
+ ldap_conf = '/etc/ldap.conf'
124
+ ldap_conf = '/etc/pam_ldap.conf' unless File.file?(ldap_conf)
125
+
126
+ File.open(ldap_conf).each_line do |line|
127
+ if (line =~ /\Abinddn\s+/) != nil
128
+ binddn = line.gsub(/\Abinddn\s+/, "").chomp
129
+ elsif (line =~ /\Abindpw\s+/) != nil
130
+ bindpw = line.gsub(/\Abindpw\s+/, "").chomp
131
+ elsif (line =~ /\Auri\s+/) != nil
132
+ host = line.gsub(/\Auri\s+/, "").chomp
133
+ elsif (line =~ /\Anss_base_passwd\s+/) != nil
134
+ base = line.gsub(/\Anss_base_passwd\s+/, "").chomp.gsub(/\?.*/, "")
135
+ end
136
+ end
137
+
138
+ exit_code = `ldapsearch -Z -LLLL -D "#{binddn}" -x -w "#{bindpw}" -H "#{host}" -b "#{base}" -s one uid sshPublidKey`.to_i
139
+
140
+ if exit_code == 0
141
+ report.push "Ldap appears to be working\n"
142
+ else
143
+ report.push "ERROR: Ldap does not appear to be working; ldapsearch exited with code #{exit_code}\n"
144
+ end
145
+ end
146
+
147
+ report = report.select { |line| line =~ /\A(\*\*\*|WARNING|ERROR)/ } unless @verbose
148
+
149
+ report = report.join("\n")
150
+
151
+ unless @report_file.nil?
152
+ begin
153
+ f = File.open(File.expand_path(@report_file), 'w')
154
+ f.puts report
155
+ f.close
156
+ rescue
157
+ raise "An error occurred while writing the report:#{$!}"
158
+ end
159
+ end
160
+
161
+ puts report
162
+ end
163
+ end
@@ -0,0 +1,114 @@
1
+ module Simp::Cli::Commands; end
2
+
3
+ class Simp::Cli::Commands::Cleancerts < Simp::Cli
4
+ @conf_dif = File.expand_path('~/.simp')
5
+ @host_file = "#{@conf_dir}/hosts"
6
+ @gen_host_list = true
7
+ @host_list = Array.new
8
+ @host_errors = Array.new
9
+
10
+ @opt_parser = OptionParser.new do |opts|
11
+ opts.banner = "\n === The SIMP CleanCerts Tool ==="
12
+ opts.separator ""
13
+ opts.separator "The SIMP CleanCerts Tool revokes and removes the Puppet certificates from a"
14
+ opts.separator "list of hosts."
15
+ opts.separator ""
16
+ opts.separator "Some requirements to use this tool:"
17
+ opts.separator " * the user must have SSH access to all of the list hosts"
18
+ opts.separator " * the user cannot be root"
19
+ opts.separator " * each target host must be able to run, with sudo, the following commands:"
20
+ opts.separator " - /usr/sbin/puppetd"
21
+ opts.separator " - /usr/sbin/puppetca"
22
+ opts.separator " - /bin/rm -rf /var/lib/puppet/ssl"
23
+ opts.separator ""
24
+ opts.separator "This tool will not clean the certificates for the hostname of the current box"
25
+ opts.separator "or the Puppet server listed in puppet.conf."
26
+ opts.separator ""
27
+ opts.separator "OPTIONS:\n"
28
+
29
+ opts.on("-H", "--hosts FILE", "FILE containing a list of hosts to clean.") do |file|
30
+ @host_file = file
31
+ @gen_host_list = false
32
+ end
33
+
34
+ opts.on("-h", "--help", "Print this message") do
35
+ puts opts
36
+ exit 0
37
+ end
38
+ end
39
+
40
+
41
+ def self.clean_certs
42
+
43
+ success
44
+ end
45
+
46
+ def self.run(args = Array.new)
47
+ File.exists?('/usr/sbin/puppetd') && File.exists?('/usr/sbin/puppetca')
48
+
49
+ raise "SIMP CleanCerts cannot be run as 'root'." if Process.uid == 0
50
+
51
+ @host_list = Array.new
52
+ if @gen_host_list
53
+ @host_list = %x{cd /;sudo /usr/sbin/puppetca --list --all}.split("\n").map { |host| host.split(/\(.*\)/).first.split(/\s+/).last }
54
+ else
55
+ File.open(@host_file).each_line do |line|
56
+ @host_list << line.chomp
57
+ end
58
+ end
59
+ @host_list.compact!
60
+
61
+ if @host_list.size == 0
62
+ puts "No known hosts to clean!"
63
+ exit 0
64
+ end
65
+
66
+ system("echo 'Please review the list of hosts to clean certificates on:\n - #{@host_list.join("\n - ")}' | less -f")
67
+
68
+ if Utils.yes_or_no("Clean certificates for all listed hosts?", false)
69
+ if @gen_host_list
70
+ file = File.open(@host_file, 'w')
71
+ @host_list.each do |host|
72
+ file.puts host
73
+ end
74
+ file.close
75
+ end
76
+
77
+ @host_list.each do |host|
78
+ %{sudo /usr/sbin/puppetca --revoke #{host}}
79
+ %{sudo /usr/sbin/puppetca --clean #{host}}
80
+ end
81
+
82
+ result = %x{pssh -f -h #{@host_file} -OStrictHostKeyChecking=no "sudo /bin/rm -rf /var/lib/puppet/ssl"}
83
+ result.each_line do |line|
84
+ if line =~ /.*\[FAILURE\]\s([A-Za-z0-9\-\.]+).*/
85
+ success = false
86
+ @host_errors << $1
87
+ end
88
+ end
89
+
90
+ if @host_errors.empty?
91
+ puts "Successfully cleaned certificates for the #{@host_list.size} hosts listed in #{@host_file.path}."
92
+ else
93
+ filename = "#{@conf_dir}/pssh_error#{Time.now.strftime("%Y%m%d%H%M")}"
94
+ File.open(filename, 'w') do
95
+ @host_errors.each { |err| file.puts err }
96
+ end
97
+ raise "Errors occured while cleaning certificates, outputting list of hosts with errors to #{filename}"
98
+ end
99
+ else
100
+ if @gen_host_list
101
+ puts "If you do not want to clean all certificates, you can place"
102
+ puts "all hosts you want to clean in a newline-delimited file and"
103
+ puts "use the '--hosts <hosts_file>' command line option."
104
+ end
105
+
106
+ puts "If you want to manually clean certificates on all boxes,"
107
+ puts "follow the steps to clean certificates from the "
108
+ puts "'\033[1mChanging Puppet Masters\033[21m' users guide."
109
+ puts "Also look through the '\033[1mPerforming One Shot Operations\033[21m'"
110
+ puts "users guide for guidance on doing this with PSSH.\n"
111
+ puts "Users guides can be found using '\033[1msimp doc\033[21m'."
112
+ end
113
+ end
114
+ end
@@ -0,0 +1,235 @@
1
+ require 'highline/import'
2
+ require 'yaml'
3
+ require 'fileutils'
4
+ require 'find'
5
+
6
+ require File.expand_path( '../../cli', File.dirname(__FILE__) )
7
+ require File.expand_path( '../config/item', File.dirname(__FILE__) )
8
+ require File.expand_path( '../config/questionnaire', File.dirname(__FILE__) )
9
+ require File.expand_path( '../config/item_list_factory', File.dirname(__FILE__) )
10
+
11
+ module Simp::Cli::Commands; end
12
+
13
+ # Handle CLI interactions for "simp config"
14
+ class Simp::Cli::Commands::Config < Simp::Cli
15
+ default_outfile = '~/.simp/simp_conf.yaml'
16
+
17
+ @version = Simp::Cli::VERSION
18
+ @advanced_config = false
19
+ @options = {
20
+ :verbose => 0,
21
+ :noninteractive => 0,
22
+ :dry_run => false, # TODO: between these two, we should choose better names
23
+
24
+ :input_file => nil,
25
+ :output_file => File.expand_path( default_outfile ),
26
+ :puppet_system_file => '/etc/puppet/environments/simp/hieradata/simp_def.yaml',
27
+
28
+ :use_safety_save => true,
29
+ :autoaccept_safety_save => false,
30
+ :fail_on_missing_answers => false,
31
+ }
32
+
33
+ @opt_parser = OptionParser.new do |opts|
34
+ opts_separator = ' '*4 + '-'*76
35
+ opts.banner = "\n=== The SIMP Configuration Tool === "
36
+ opts.separator ""
37
+ opts.separator "The SIMP Configuration Tool is designed to assist the configuration of a SIMP"
38
+ opts.separator "machine. It offers two main features:"
39
+ opts.separator ""
40
+ opts.separator " (1) create/edit system configurations, and"
41
+ opts.separator " (2) apply system configurations."
42
+ opts.separator ""
43
+ opts.separator "The features that will be used is dependent upon the options specified."
44
+ opts.separator ""
45
+ opts.separator "USAGE:"
46
+ opts.separator " #{File.basename($0)} config [KEY=VALUE] [KEY=VALUE1,,VALUE2,,VALUE3] [...]"
47
+ opts.separator ""
48
+ opts.separator "OPTIONS:\n"
49
+ opts.separator opts_separator
50
+
51
+ opts.on("-o", "--output FILE", "The answers FILE where the created/edited ",
52
+ "system configuration will be written. ",
53
+ " (defaults to '#{default_outfile}')") do |file|
54
+ @options[:output_file] = file
55
+ end
56
+
57
+ opts.on("-i", "-a", "-e", "--apply FILE", "Apply answers FILE (fails on missing items)"
58
+ ) do |file|
59
+ @options[:input_file] = file
60
+ @options[:fail_on_missing_answers] = true
61
+ end
62
+
63
+ opts.on("-I", "-A", "-E", "--apply-with-questions FILE",
64
+ "Apply answers FILE (asks on missing items) ",
65
+ " Note that the edited configuration",
66
+ " will be written to the file specified in ",
67
+ " --output.") do |file|
68
+ @options[:input_file] = file
69
+ @options[:fail_on_missing_answers] = false
70
+ end
71
+
72
+ opts.separator opts_separator
73
+
74
+ # TODO: improve nomenclature
75
+ opts.on("-v", "--verbose", "Verbose output (stacks)") do
76
+ @options[:verbose] += 1
77
+ end
78
+
79
+ opts.on("-q", "--quiet", "Quiet output (clears any verbosity)") do
80
+ @options[:verbose] = -1
81
+ end
82
+
83
+ opts.on("-n", "--dry-run", "Do not apply system changes",
84
+ " (e.g., NICs, puppet.conf, etc)" ) do
85
+ @options[:dry_run] = true
86
+ end
87
+
88
+ opts.on("-f", "--non-interactive", "Force default answers (prompt if unknown)"
89
+ #" (-ff fails instead of prompting)"
90
+ ) do |file|
91
+ @options[:noninteractive] += 1
92
+ end
93
+
94
+ opts.on("-s", "--skip-safety-save", "Ignore any safety-save files") do
95
+ @options[:use_safety_save] = false
96
+ end
97
+
98
+ opts.on("-S", "--accept-safety-save", "Automatically apply any safety-save files") do
99
+ @options[:autoaccept_safety_save] = true
100
+ end
101
+
102
+ opts.separator opts_separator
103
+
104
+ opts.on("-h", "--help", "Print this message") do
105
+ puts opts
106
+ exit 0
107
+ end
108
+ end
109
+
110
+
111
+ def self.saved_session
112
+ result = {}
113
+ if @options.fetch( :use_safety_save, false ) && file = @options.fetch( :output_file )
114
+ _file = File.join( File.dirname( file ), ".#{File.basename( file )}" )
115
+ if File.file?( _file )
116
+ lines = File.open( _file, 'r' ).readlines
117
+ saved_hash = read_answers_file _file
118
+ last_item = nil
119
+ if saved_hash.keys.size > 0
120
+ last_item = {saved_hash.keys.last =>
121
+ saved_hash[ saved_hash.keys.last ]}.to_yaml.gsub( /^---/, '' ).strip
122
+ end
123
+
124
+ message = %Q{WARNING: interrupted session detected!}
125
+ say "<%= color(%q{*** #{message} ***}, YELLOW, BOLD) %> \n\n"
126
+ say "<%= color(%q{An automatic safety-save file from a previous session has been found at:}, YELLOW) %> \n\n"
127
+ say " <%= color( %q{#{_file}}, BOLD ) %>\n\n"
128
+ if last_item
129
+ say "<%= color(%q{The most recent answer from this session was:}, YELLOW) %> \n\n"
130
+ say "<%= color( %q{#{last_item.gsub( /^/, " \0" )}} ) %>\n\n"
131
+ end
132
+
133
+ if @options.fetch( :autoaccept_safety_save, false )
134
+ color = 'YELLOW'
135
+ say "<%= color(%q{Automatically resuming these answers because }, #{color}) %>" +
136
+ "<%= color(%q{--accept-safety-save}, BOLD, #{color}) %>" +
137
+ "<%= color(%q{ is active.}, #{color}) %>\n\n"
138
+ result = saved_hash
139
+ else
140
+ say "<%= color(%q{You can resume these answers or delete the file.}, YELLOW) %>\n\n"
141
+
142
+ if agree( "resume the session? (no = deletes file)" ){ |q| q.default = 'yes' }
143
+ say "\n<%= color( %q{applying answers from '#{_file}'}, GREEN )%>\n"
144
+ result = saved_hash
145
+ else
146
+ say "\n<%= color( %q{removing file '#{_file}'}, RED )%>\n"
147
+ FileUtils.rm_f _file, :verbose => true
148
+ end
149
+ end
150
+ sleep 1
151
+ end
152
+ end
153
+ result
154
+ end
155
+
156
+
157
+ def self.remove_saved_session
158
+ if file = @options.fetch( :output_file )
159
+ _file = File.join( File.dirname( file ), ".#{File.basename( file )}" )
160
+ FileUtils.rm_f( _file, :verbose => false ) if File.file?( _file )
161
+ end
162
+ end
163
+
164
+
165
+ def self.read_answers_file file
166
+ answers_hash = {} # Read the input file
167
+
168
+ if file
169
+ unless File.exist?(file)
170
+ raise "Could not access the file '#{file}'!"
171
+ end
172
+ else
173
+ file = @options[:system_file]
174
+ end
175
+
176
+ begin
177
+ answers_hash = YAML.load(File.read(file))
178
+ answers_hash.empty?
179
+ rescue Errno::EACCES
180
+ error = "WARNING: Could not access the answers file '#{file}'!"
181
+ say "<%= color(%q{#{error}}, YELLOW) %>\n"
182
+ rescue
183
+ # If the file existed, but ingest failed, then there's a problem
184
+ raise "System Configuration File: '#{file}' is corrupted.\nReview the file and either fix or remove it before trying again."
185
+ end
186
+
187
+ answers_hash
188
+ end
189
+
190
+ def self.run(args = [])
191
+ begin
192
+ super # parse @options
193
+ rescue OptionParser::InvalidOption=> e
194
+ error = "ERROR: #{e.message}"
195
+ say "\n<%= color(%q{#{error}}, RED) %>\n"
196
+ puts @opt_parser
197
+ exit 1
198
+ end
199
+
200
+ # Ensure that custom facts are available before the first pluginsync
201
+ %x{puppet config print modulepath}.strip.split(':').each do |dir|
202
+ next unless File.directory?(dir)
203
+ Find.find(dir) do |mod_path|
204
+ fact_path = File.expand_path('lib/facter', mod_path)
205
+ Facter.search(fact_path) if File.directory?(fact_path)
206
+ Find.prune unless mod_path == dir
207
+ end
208
+ end
209
+
210
+ # read in answers file
211
+ answers_hash = {}
212
+ if file = @options.fetch( :input_file )
213
+ answers_hash = read_answers_file( file )
214
+ end
215
+
216
+ # NOTE: answers from an interrupted session take precedence over input file
217
+ answers_hash = saved_session.merge( answers_hash )
218
+
219
+ # NOTE: answers provided from the cli take precedence over everything else
220
+ cli_answers = Hash[ ARGV[1..-1].map{ |x| x.split '=' } ]
221
+ answers_hash = answers_hash.merge( cli_answers )
222
+
223
+ # get the list of items
224
+ # - applies any known answers at this point
225
+ item_list = Simp::Cli::Config::ItemListFactory.new( @options ).process( nil, answers_hash )
226
+
227
+ # process items:
228
+ # - get any remaining answers
229
+ # - apply changes as needed
230
+ questionnaire = Simp::Cli::Config::Questionnaire.new( @options )
231
+ answers = questionnaire.process( item_list, {} )
232
+
233
+ remove_saved_session
234
+ end
235
+ end