foreman_maintain 0.0.2 → 0.0.3
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.
- checksums.yaml +4 -4
- data/README.md +107 -25
- data/bin/foreman-maintain +1 -0
- data/definitions/checks/disk_speed_minimal.rb +31 -19
- data/definitions/checks/foreman_tasks/invalid/check_old.rb +20 -0
- data/definitions/checks/foreman_tasks/invalid/check_pending_state.rb +20 -0
- data/definitions/checks/foreman_tasks/invalid/check_planning_state.rb +20 -0
- data/definitions/checks/foreman_tasks/not_paused.rb +29 -0
- data/definitions/checks/foreman_tasks/not_running.rb +14 -0
- data/definitions/checks/sync_plans/with_disabled_status.rb +18 -0
- data/definitions/checks/sync_plans/with_enabled_status.rb +18 -0
- data/definitions/checks/system_registration.rb +31 -0
- data/definitions/features/downstream.rb +5 -3
- data/definitions/features/foreman_1_11_x.rb +4 -2
- data/definitions/features/foreman_1_7_x.rb +5 -3
- data/definitions/features/foreman_database.rb +11 -5
- data/definitions/features/foreman_tasks.rb +118 -9
- data/definitions/features/sync_plans.rb +75 -0
- data/definitions/features/upstream.rb +5 -3
- data/definitions/procedures/foreman_tasks/delete.rb +33 -0
- data/definitions/procedures/foreman_tasks/resume.rb +14 -0
- data/definitions/procedures/foreman_tasks/ui_investigate.rb +19 -0
- data/definitions/procedures/hammer_setup.rb +50 -0
- data/definitions/procedures/install_package.rb +17 -0
- data/definitions/procedures/sync_plans/disable.rb +22 -0
- data/definitions/procedures/sync_plans/enable.rb +21 -0
- data/definitions/scenarios/pre_upgrade_check_foreman_1_14.rb +7 -6
- data/definitions/scenarios/pre_upgrade_check_satellite_6_0_z.rb +8 -6
- data/definitions/scenarios/pre_upgrade_check_satellite_6_1.rb +8 -6
- data/definitions/scenarios/pre_upgrade_check_satellite_6_1_z.rb +8 -6
- data/definitions/scenarios/pre_upgrade_check_satellite_6_2.rb +8 -6
- data/definitions/scenarios/pre_upgrade_check_satellite_6_2_z.rb +8 -6
- data/definitions/scenarios/pre_upgrade_check_satellite_6_3.rb +8 -6
- data/lib/foreman_maintain.rb +52 -5
- data/lib/foreman_maintain/check.rb +18 -12
- data/lib/foreman_maintain/cli/base.rb +9 -2
- data/lib/foreman_maintain/cli/health_command.rb +2 -1
- data/lib/foreman_maintain/cli/upgrade_command.rb +2 -0
- data/lib/foreman_maintain/concerns/hammer.rb +20 -0
- data/lib/foreman_maintain/concerns/logger.rb +1 -5
- data/lib/foreman_maintain/concerns/metadata.rb +138 -31
- data/lib/foreman_maintain/concerns/system_helpers.rb +36 -32
- data/lib/foreman_maintain/config.rb +40 -5
- data/lib/foreman_maintain/core_ext.rb +24 -0
- data/lib/foreman_maintain/detector.rb +12 -13
- data/lib/foreman_maintain/error.rb +28 -0
- data/lib/foreman_maintain/executable.rb +86 -11
- data/lib/foreman_maintain/feature.rb +1 -0
- data/lib/foreman_maintain/param.rb +47 -0
- data/lib/foreman_maintain/reporter.rb +20 -3
- data/lib/foreman_maintain/reporter/cli_reporter.rb +166 -66
- data/lib/foreman_maintain/runner.rb +56 -13
- data/lib/foreman_maintain/runner/execution.rb +8 -0
- data/lib/foreman_maintain/scenario.rb +46 -2
- data/lib/foreman_maintain/top_level_modules.rb +3 -0
- data/lib/foreman_maintain/utils.rb +2 -0
- data/lib/foreman_maintain/utils/command_runner.rb +101 -0
- data/lib/foreman_maintain/utils/disk/device.rb +5 -9
- data/lib/foreman_maintain/utils/hammer.rb +78 -0
- data/lib/foreman_maintain/version.rb +1 -1
- data/lib/foreman_maintain/yaml_storage.rb +48 -0
- metadata +27 -9
- data/definitions/checks/foreman_tasks_not_paused.rb +0 -14
- data/definitions/checks/foreman_tasks_not_running.rb +0 -10
- data/definitions/procedures/foreman_tasks_resume.rb +0 -13
- data/lib/foreman_maintain/logger.rb +0 -11
@@ -1,5 +1,10 @@
|
|
1
1
|
module ForemanMaintain
|
2
2
|
class Reporter
|
3
|
+
class DummySpinner
|
4
|
+
def update(_message)
|
5
|
+
# do nothing
|
6
|
+
end
|
7
|
+
end
|
3
8
|
require 'foreman_maintain/reporter/cli_reporter'
|
4
9
|
|
5
10
|
# Each public method is a hook called by executor at the specific point
|
@@ -7,12 +12,24 @@ module ForemanMaintain
|
|
7
12
|
|
8
13
|
def before_execution_starts(_execution); end
|
9
14
|
|
10
|
-
def on_execution_update(_execution, _update); end
|
11
|
-
|
12
15
|
def after_execution_finishes(_execution); end
|
13
16
|
|
14
17
|
def after_scenario_finishes(_scenario); end
|
15
18
|
|
16
|
-
def
|
19
|
+
def on_next_steps(_steps); end
|
20
|
+
|
21
|
+
def with_spinner(_message, &_block)
|
22
|
+
yield DummySpinner.new
|
23
|
+
end
|
24
|
+
|
25
|
+
def print(_message); end
|
26
|
+
|
27
|
+
def puts(_message); end
|
28
|
+
|
29
|
+
def ask(_message); end
|
30
|
+
|
31
|
+
def assumeyes?
|
32
|
+
false
|
33
|
+
end
|
17
34
|
end
|
18
35
|
end
|
@@ -4,6 +4,12 @@ require 'highline'
|
|
4
4
|
module ForemanMaintain
|
5
5
|
class Reporter
|
6
6
|
class CLIReporter < Reporter
|
7
|
+
DECISION_MAPPER = {
|
8
|
+
%w[y yes] => :yes,
|
9
|
+
%w[n next no] => :no,
|
10
|
+
%w[q quit] => :quit
|
11
|
+
}.freeze
|
12
|
+
|
7
13
|
# Simple spinner able to keep updating current line
|
8
14
|
class Spinner
|
9
15
|
def initialize(reporter, interval = 0.1)
|
@@ -12,14 +18,21 @@ module ForemanMaintain
|
|
12
18
|
@active = false
|
13
19
|
@interval = interval
|
14
20
|
@spinner_index = 0
|
15
|
-
@spinner_chars = %w
|
21
|
+
@spinner_chars = %w[| / - \\]
|
16
22
|
@current_line = ''
|
17
23
|
@puts_needed = false
|
18
24
|
start_spinner
|
19
25
|
end
|
20
26
|
|
21
27
|
def update(line)
|
22
|
-
@mutex.synchronize
|
28
|
+
@mutex.synchronize do
|
29
|
+
@current_line = line
|
30
|
+
print_current_line
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def active?
|
35
|
+
@mutex.synchronize { @active }
|
23
36
|
end
|
24
37
|
|
25
38
|
def activate
|
@@ -28,9 +41,9 @@ module ForemanMaintain
|
|
28
41
|
end
|
29
42
|
|
30
43
|
def deactivate
|
44
|
+
return unless active?
|
31
45
|
@mutex.synchronize do
|
32
46
|
@active = false
|
33
|
-
@reporter.print "\r"
|
34
47
|
end
|
35
48
|
end
|
36
49
|
|
@@ -48,60 +61,127 @@ module ForemanMaintain
|
|
48
61
|
def spin
|
49
62
|
@mutex.synchronize do
|
50
63
|
return unless @active
|
51
|
-
|
52
|
-
@reporter.print "\r"
|
53
|
-
line = "#{@spinner_chars[@spinner_index]} #{@current_line}"
|
54
|
-
@reporter.print(line)
|
64
|
+
print_current_line
|
55
65
|
@spinner_index = (@spinner_index + 1) % @spinner_chars.size
|
56
66
|
end
|
57
67
|
end
|
68
|
+
|
69
|
+
def print_current_line
|
70
|
+
@reporter.clear_line
|
71
|
+
line = "#{@spinner_chars[@spinner_index]} #{@current_line}"
|
72
|
+
@reporter.print(line)
|
73
|
+
end
|
58
74
|
end
|
59
75
|
|
60
|
-
def initialize(stdout = STDOUT, stdin = STDIN)
|
76
|
+
def initialize(stdout = STDOUT, stdin = STDIN, options = {})
|
61
77
|
@stdout = stdout
|
62
78
|
@stdin = stdin
|
63
|
-
|
79
|
+
options.validate_options!(:assumeyes)
|
80
|
+
@assumeyes = options.fetch(:assumeyes, false)
|
81
|
+
@hl = HighLine.new(@stdin, @stdout)
|
64
82
|
@max_length = 80
|
65
83
|
@line_char = '-'
|
66
84
|
@cell_char = '|'
|
67
85
|
@spinner = Spinner.new(self)
|
86
|
+
@last_line = ''
|
68
87
|
end
|
69
88
|
|
70
89
|
def before_scenario_starts(scenario)
|
71
|
-
puts "
|
72
|
-
hline
|
90
|
+
puts "\nRunning #{scenario.description || scenario.class}"
|
91
|
+
hline('=')
|
73
92
|
end
|
74
93
|
|
75
94
|
def before_execution_starts(execution)
|
76
|
-
|
77
|
-
@spinner.activate
|
95
|
+
puts(execution_info(execution, ''))
|
78
96
|
end
|
79
97
|
|
80
|
-
def
|
81
|
-
|
98
|
+
def print(string)
|
99
|
+
new_line_if_needed
|
100
|
+
@stdout.print(string)
|
101
|
+
@stdout.flush
|
102
|
+
record_last_line(string)
|
82
103
|
end
|
83
104
|
|
84
|
-
def
|
105
|
+
def puts(string)
|
106
|
+
# we don't print the new line right away, as we want to be able to put
|
107
|
+
# the status label at the end of the last line, if possible.
|
108
|
+
# Therefore, we just mark that we need to print the new line next time
|
109
|
+
# we are printing something.
|
110
|
+
new_line_if_needed
|
111
|
+
@stdout.print(string)
|
112
|
+
@stdout.flush
|
113
|
+
@new_line_next_time = true
|
114
|
+
record_last_line(string)
|
115
|
+
end
|
116
|
+
|
117
|
+
def ask(message, options = {})
|
118
|
+
new_line_if_needed
|
119
|
+
options.validate_options!(:password)
|
120
|
+
# the answer is confirmed by ENTER which will emit a new line
|
121
|
+
@new_line_next_time = false
|
122
|
+
@last_line = ''
|
123
|
+
# add space at the end as otherwise highline would add new line there :/
|
124
|
+
message = "#{message} " unless message =~ /\s\Z/
|
125
|
+
answer = @hl.ask(message) { |q| q.echo = false if options[:password] }
|
126
|
+
answer.to_s.chomp.downcase if answer
|
127
|
+
end
|
128
|
+
|
129
|
+
def new_line_if_needed
|
130
|
+
if @new_line_next_time
|
131
|
+
@stdout.print("\n")
|
132
|
+
@stdout.flush
|
133
|
+
@new_line_next_time = false
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
def with_spinner(message)
|
138
|
+
new_line_if_needed
|
139
|
+
@spinner.activate
|
140
|
+
@spinner.update(message)
|
141
|
+
yield @spinner
|
142
|
+
ensure
|
85
143
|
@spinner.deactivate
|
86
|
-
|
87
|
-
|
144
|
+
@new_line_next_time = true
|
145
|
+
end
|
146
|
+
|
147
|
+
def after_execution_finishes(execution)
|
148
|
+
puts_status(execution.status)
|
149
|
+
puts(execution.output) unless execution.output.empty?
|
88
150
|
hline
|
151
|
+
new_line_if_needed
|
89
152
|
end
|
90
153
|
|
91
154
|
def after_scenario_finishes(_scenario); end
|
92
155
|
|
93
|
-
def on_next_steps(
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
156
|
+
def on_next_steps(steps)
|
157
|
+
return if steps.empty?
|
158
|
+
if steps.size > 1
|
159
|
+
multiple_steps_decision(steps)
|
160
|
+
else
|
161
|
+
single_step_decision(steps.first)
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
165
|
+
def clear_line
|
166
|
+
print "\r" + ' ' * @max_length + "\r"
|
167
|
+
end
|
168
|
+
|
169
|
+
private
|
170
|
+
|
171
|
+
def assumeyes?
|
172
|
+
@assumeyes
|
102
173
|
end
|
103
174
|
|
104
|
-
def
|
175
|
+
def single_step_decision(step)
|
176
|
+
answer = ask_decision("Continue with step [#{step.description}]?")
|
177
|
+
if answer == :yes
|
178
|
+
step
|
179
|
+
else
|
180
|
+
answer
|
181
|
+
end
|
182
|
+
end
|
183
|
+
|
184
|
+
def multiple_steps_decision(steps)
|
105
185
|
puts 'There are multiple steps to proceed:'
|
106
186
|
steps.each_with_index do |step, index|
|
107
187
|
puts "#{index + 1}) #{step.description}"
|
@@ -109,68 +189,88 @@ module ForemanMaintain
|
|
109
189
|
ask_to_select('Select step to continue', steps, &:description)
|
110
190
|
end
|
111
191
|
|
112
|
-
def
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
false
|
192
|
+
def ask_decision(message)
|
193
|
+
if assumeyes?
|
194
|
+
print("#{message} (assuming yes)")
|
195
|
+
return :yes
|
196
|
+
end
|
197
|
+
until_valid_decision do
|
198
|
+
filter_decision(ask("#{message}, [y(yes), n(no), q(quit)]"))
|
120
199
|
end
|
121
200
|
ensure
|
122
201
|
clear_line
|
123
202
|
end
|
124
203
|
|
204
|
+
def filter_decision(answer)
|
205
|
+
decision = nil
|
206
|
+
DECISION_MAPPER.each do |options, decision_label|
|
207
|
+
decision = decision_label if options.include?(answer)
|
208
|
+
end
|
209
|
+
decision
|
210
|
+
end
|
211
|
+
|
212
|
+
# rubocop:disable Metrics/MethodLength,Metrics/AbcSize
|
125
213
|
def ask_to_select(message, steps)
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
214
|
+
if assumeyes?
|
215
|
+
puts('(assuming first option)')
|
216
|
+
return steps.first
|
217
|
+
end
|
218
|
+
until_valid_decision do
|
219
|
+
answer = ask("#{message}, [n(next), q(quit)]")
|
220
|
+
if answer =~ /^\d+$/ && (answer.to_i - 1) < steps.size
|
221
|
+
steps[answer.to_i - 1]
|
222
|
+
else
|
223
|
+
decision = filter_decision(answer)
|
224
|
+
if decision == :yes
|
225
|
+
steps.first
|
226
|
+
else
|
227
|
+
decision
|
228
|
+
end
|
229
|
+
end
|
133
230
|
end
|
134
231
|
ensure
|
135
232
|
clear_line
|
136
233
|
end
|
137
234
|
|
138
|
-
|
139
|
-
|
235
|
+
# loop over the block until it returns some non-false value
|
236
|
+
def until_valid_decision
|
237
|
+
decision = nil
|
238
|
+
decision = yield until decision
|
239
|
+
decision
|
140
240
|
end
|
141
241
|
|
142
|
-
def execution_info(execution, text
|
242
|
+
def execution_info(execution, text)
|
143
243
|
prefix = "#{execution.name}:"
|
144
|
-
prefix = prefix.ljust(ljust) if ljust
|
145
244
|
"#{prefix} #{text}"
|
146
245
|
end
|
147
246
|
|
148
|
-
def
|
247
|
+
def puts_status(status)
|
248
|
+
label_offset = 10
|
249
|
+
padding = @max_length - @last_line.to_s.size - label_offset
|
250
|
+
if padding < 0
|
251
|
+
new_line_if_needed
|
252
|
+
padding = @max_length - label_offset
|
253
|
+
end
|
254
|
+
@stdout.print(' ' * padding + status_label(status))
|
255
|
+
@new_line_next_time = true
|
256
|
+
end
|
257
|
+
|
258
|
+
def status_label(status)
|
149
259
|
mapping = { :success => { :label => '[OK]', :color => :green },
|
150
260
|
:fail => { :label => '[FAIL]', :color => :red },
|
151
261
|
:running => { :label => '[RUNNING]', :color => :blue },
|
152
|
-
:skipped => { :label => '[SKIPPED]', :color => :yellow }
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
def hline
|
158
|
-
puts @line_char * @max_length
|
262
|
+
:skipped => { :label => '[SKIPPED]', :color => :yellow },
|
263
|
+
:warning => { :label => '[WARNING]', :color => :yellow } }
|
264
|
+
properties = mapping[status]
|
265
|
+
@hl.color(properties[:label], properties[:color], :bold)
|
159
266
|
end
|
160
267
|
|
161
|
-
def
|
162
|
-
|
163
|
-
puts @cell_char
|
268
|
+
def hline(line_char = @line_char)
|
269
|
+
puts line_char * @max_length
|
164
270
|
end
|
165
271
|
|
166
|
-
def
|
167
|
-
@
|
168
|
-
@stdout.flush
|
169
|
-
end
|
170
|
-
|
171
|
-
def puts(string)
|
172
|
-
@stdout.puts(string)
|
173
|
-
@stdout.flush
|
272
|
+
def record_last_line(string)
|
273
|
+
@last_line = string.lines.to_a.last
|
174
274
|
end
|
175
275
|
end
|
176
276
|
end
|
@@ -2,38 +2,81 @@ module ForemanMaintain
|
|
2
2
|
# Class responsible for running the scenario
|
3
3
|
class Runner
|
4
4
|
require 'foreman_maintain/runner/execution'
|
5
|
-
def initialize(reporter,
|
5
|
+
def initialize(reporter, scenarios, options = {})
|
6
|
+
options.validate_options!(:assumeyes)
|
7
|
+
@assumeyes = options.fetch(:assumeyes, false)
|
6
8
|
@reporter = reporter
|
7
|
-
@
|
8
|
-
@
|
9
|
-
@steps_to_run = @scenario.steps.dup
|
9
|
+
@scenarios = Array(scenarios)
|
10
|
+
@scenarios_with_dependencies = scenarios_with_dependencies
|
10
11
|
@quit = false
|
11
12
|
end
|
12
13
|
|
14
|
+
def assumeyes?
|
15
|
+
@assumeyes
|
16
|
+
end
|
17
|
+
|
18
|
+
def scenarios_with_dependencies
|
19
|
+
@scenarios.map do |scenario|
|
20
|
+
scenario.before_scenarios + [scenario]
|
21
|
+
end.flatten
|
22
|
+
end
|
23
|
+
|
13
24
|
def run
|
14
|
-
|
25
|
+
scenarios_with_dependencies.each do |scenario|
|
26
|
+
run_scenario(scenario)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def run_scenario(scenario)
|
31
|
+
@steps_to_run = scenario.steps.dup
|
32
|
+
@reporter.before_scenario_starts(scenario)
|
15
33
|
while !@quit && !@steps_to_run.empty?
|
16
34
|
step = @steps_to_run.shift
|
35
|
+
@reporter.puts('Rerunning the check after fix procedure') if rerun_check?(step)
|
17
36
|
execution = Execution.new(step, @reporter)
|
18
37
|
execution.run
|
19
|
-
|
20
|
-
ask_about_offered_steps(step.next_steps)
|
38
|
+
ask_about_offered_steps(step)
|
21
39
|
end
|
22
|
-
@reporter.after_scenario_finishes(
|
40
|
+
@reporter.after_scenario_finishes(scenario)
|
23
41
|
end
|
24
42
|
|
25
|
-
def ask_to_quit
|
43
|
+
def ask_to_quit(_step = nil)
|
26
44
|
@quit = true
|
27
45
|
end
|
28
46
|
|
29
|
-
def
|
30
|
-
|
47
|
+
def add_steps(*steps)
|
48
|
+
# we we add the steps at the beginning, but still keeping the
|
49
|
+
# order of steps passed in the arguments
|
50
|
+
steps.reverse.each do |step|
|
51
|
+
@steps_to_run.unshift(step)
|
52
|
+
end
|
31
53
|
end
|
32
54
|
|
33
55
|
private
|
34
56
|
|
35
|
-
|
36
|
-
|
57
|
+
# rubocop:disable Metrics/MethodLength,Metrics/AbcSize,Metrics/CyclomaticComplexity
|
58
|
+
def ask_about_offered_steps(step)
|
59
|
+
if assumeyes? && rerun_check?(step)
|
60
|
+
@reporter.puts 'Check still failing after attempt to fix. Skipping'
|
61
|
+
return :no
|
62
|
+
end
|
63
|
+
if step.next_steps && !step.next_steps.empty?
|
64
|
+
@last_decision_step = step
|
65
|
+
steps = step.next_steps.map(&:ensure_instance)
|
66
|
+
decision = @reporter.on_next_steps(steps)
|
67
|
+
case decision
|
68
|
+
when :quit
|
69
|
+
ask_to_quit
|
70
|
+
when Executable
|
71
|
+
chosen_steps = [decision]
|
72
|
+
chosen_steps << step if step.is_a?(Check)
|
73
|
+
add_steps(*chosen_steps)
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
def rerun_check?(step)
|
79
|
+
@last_decision_step == step
|
37
80
|
end
|
38
81
|
end
|
39
82
|
end
|