foreman_maintain 0.0.6 → 0.0.7
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 +75 -3
- data/config/foreman_maintain.yml.example +6 -0
- data/config/foreman_maintain.yml.packaging +6 -0
- data/definitions/checks/disk_speed_minimal.rb +1 -1
- data/definitions/checks/foreman_proxy/verify_dhcp_config_syntax.rb +17 -0
- data/definitions/checks/system_registration.rb +1 -1
- data/definitions/features/downstream.rb +31 -0
- data/definitions/features/foreman_1_7_x.rb +33 -0
- data/definitions/features/foreman_proxy.rb +72 -0
- data/definitions/features/sync_plans.rb +14 -20
- data/definitions/features/upstream.rb +4 -0
- data/definitions/procedures/foreman_tasks/delete.rb +2 -1
- data/definitions/procedures/hammer_setup.rb +4 -4
- data/definitions/procedures/installer/upgrade.rb +19 -0
- data/definitions/procedures/maintenance_mode/disable.rb +13 -0
- data/definitions/procedures/maintenance_mode/enable.rb +13 -0
- data/definitions/procedures/packages/install.rb +20 -0
- data/definitions/procedures/packages/update.rb +20 -0
- data/definitions/procedures/repositories/setup.rb +18 -0
- data/definitions/procedures/sync_plans/disable.rb +4 -0
- data/definitions/procedures/sync_plans/enable.rb +5 -0
- data/definitions/scenarios/upgrade_to_satellite_6_2.rb +77 -0
- data/lib/foreman_maintain.rb +5 -2
- data/lib/foreman_maintain/check.rb +11 -4
- data/lib/foreman_maintain/cli.rb +23 -0
- data/lib/foreman_maintain/cli/advanced/procedure/abstract_by_tag_command.rb +38 -0
- data/lib/foreman_maintain/cli/advanced/procedure/abstract_procedure_command.rb +17 -0
- data/lib/foreman_maintain/cli/advanced/procedure/by_tag_command.rb +32 -0
- data/lib/foreman_maintain/cli/advanced/procedure/run_command.rb +17 -0
- data/lib/foreman_maintain/cli/advanced/procedure_command.rb +11 -0
- data/lib/foreman_maintain/cli/advanced_command.rb +9 -0
- data/lib/foreman_maintain/cli/base.rb +52 -7
- data/lib/foreman_maintain/cli/health_command.rb +0 -12
- data/lib/foreman_maintain/cli/transform_clamp_options.rb +66 -0
- data/lib/foreman_maintain/cli/upgrade_command.rb +45 -33
- data/lib/foreman_maintain/concerns/metadata.rb +28 -2
- data/lib/foreman_maintain/concerns/scenario_metadata.rb +44 -0
- data/lib/foreman_maintain/concerns/system_helpers.rb +27 -5
- data/lib/foreman_maintain/config.rb +10 -5
- data/lib/foreman_maintain/core_ext.rb +5 -1
- data/lib/foreman_maintain/csv_parser.rb +81 -0
- data/lib/foreman_maintain/dependency_graph.rb +10 -48
- data/lib/foreman_maintain/error.rb +4 -0
- data/lib/foreman_maintain/executable.rb +64 -13
- data/lib/foreman_maintain/param.rb +1 -0
- data/lib/foreman_maintain/reporter.rb +84 -3
- data/lib/foreman_maintain/reporter/cli_reporter.rb +57 -21
- data/lib/foreman_maintain/runner.rb +80 -21
- data/lib/foreman_maintain/runner/execution.rb +29 -4
- data/lib/foreman_maintain/runner/stored_execution.rb +23 -0
- data/lib/foreman_maintain/scenario.rb +90 -7
- data/lib/foreman_maintain/upgrade_runner.rb +194 -0
- data/lib/foreman_maintain/utils.rb +1 -0
- data/lib/foreman_maintain/utils/curl_response.rb +21 -0
- data/lib/foreman_maintain/version.rb +1 -1
- metadata +24 -14
- data/definitions/checks/sync_plans/with_disabled_status.rb +0 -18
- data/definitions/checks/sync_plans/with_enabled_status.rb +0 -19
- data/definitions/procedures/install_package.rb +0 -17
- data/definitions/scenarios/pre_upgrade_check_foreman_1_14.rb +0 -13
- data/definitions/scenarios/pre_upgrade_check_satellite_6_0_z.rb +0 -14
- data/definitions/scenarios/pre_upgrade_check_satellite_6_1.rb +0 -14
- data/definitions/scenarios/pre_upgrade_check_satellite_6_1_z.rb +0 -14
- data/definitions/scenarios/pre_upgrade_check_satellite_6_2.rb +0 -14
- data/definitions/scenarios/pre_upgrade_check_satellite_6_2_z.rb +0 -14
- data/definitions/scenarios/pre_upgrade_check_satellite_6_3.rb +0 -14
- data/lib/foreman_maintain/object_cache.rb +0 -34
@@ -2,8 +2,8 @@ require 'fileutils'
|
|
2
2
|
module ForemanMaintain
|
3
3
|
class Config
|
4
4
|
attr_accessor :pre_setup_log_messages,
|
5
|
-
:config_file, :definitions_dirs, :log_level, :log_dir, :
|
6
|
-
:backup_dir
|
5
|
+
:config_file, :definitions_dirs, :log_level, :log_dir, :log_file_size,
|
6
|
+
:storage_file, :backup_dir, :foreman_proxy_cert_path
|
7
7
|
|
8
8
|
def initialize(options)
|
9
9
|
@pre_setup_log_messages = []
|
@@ -11,17 +11,22 @@ module ForemanMaintain
|
|
11
11
|
@options = load_config
|
12
12
|
@definitions_dirs = @options.fetch(:definitions_dirs,
|
13
13
|
[File.join(source_path, 'definitions')])
|
14
|
-
|
15
|
-
@log_level = @options.fetch(:log_level, ::Logger::DEBUG)
|
16
|
-
@log_dir = find_dir_path(@options.fetch(:log_dir, 'log'))
|
14
|
+
load_log_configs
|
17
15
|
@storage_file = @options.fetch(:storage_file, 'data.yml')
|
18
16
|
@backup_dir = find_dir_path(
|
19
17
|
@options.fetch(:backup_dir, '/var/lib/foreman-maintain')
|
20
18
|
)
|
19
|
+
@foreman_proxy_cert_path = @options.fetch(:foreman_proxy_cert_path, '/etc/foreman')
|
21
20
|
end
|
22
21
|
|
23
22
|
private
|
24
23
|
|
24
|
+
def load_log_configs
|
25
|
+
@log_level = @options.fetch(:log_level, ::Logger::DEBUG)
|
26
|
+
@log_dir = find_dir_path(@options.fetch(:log_dir, 'log'))
|
27
|
+
@log_file_size = @options.fetch(:log_file_size, 10_000)
|
28
|
+
end
|
29
|
+
|
25
30
|
def load_config
|
26
31
|
if File.exist?(config_file)
|
27
32
|
YAML.load(File.open(config_file)) || {}
|
@@ -2,7 +2,11 @@ module ForemanMaintain
|
|
2
2
|
module CoreExt
|
3
3
|
module StripHeredoc
|
4
4
|
def strip_heredoc
|
5
|
-
indent =
|
5
|
+
indent = 0
|
6
|
+
indented_lines = scan(/^[ \t]+(?=\S)/)
|
7
|
+
unless indented_lines.empty?
|
8
|
+
indent = indented_lines.min.size
|
9
|
+
end
|
6
10
|
gsub(/^[ \t]{#{indent}}/, '')
|
7
11
|
end
|
8
12
|
end
|
@@ -0,0 +1,81 @@
|
|
1
|
+
module ForemanMaintain
|
2
|
+
class CSVParser
|
3
|
+
def initialize
|
4
|
+
reset_parser
|
5
|
+
end
|
6
|
+
|
7
|
+
def parse(data)
|
8
|
+
return [] if data.nil?
|
9
|
+
reset_parser
|
10
|
+
data.each_char do |char|
|
11
|
+
handle_escape(char) || handle_quoting(char) || handle_comma(char) || add_to_buffer(char)
|
12
|
+
end
|
13
|
+
unless @last_quote.nil?
|
14
|
+
raise(ArgumentError, format('Illegal quoting in %s', @raw_buffer))
|
15
|
+
end
|
16
|
+
clean_buffer
|
17
|
+
@value
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
def handle_comma(char)
|
23
|
+
if char == ','
|
24
|
+
clean_buffer
|
25
|
+
true
|
26
|
+
else
|
27
|
+
false
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def handle_quoting(char)
|
32
|
+
if @last_quote.nil? && ["'", '"'].include?(char)
|
33
|
+
@last_quote = char
|
34
|
+
@raw_buffer += char
|
35
|
+
true
|
36
|
+
elsif @last_quote == char
|
37
|
+
@last_quote = nil
|
38
|
+
@raw_buffer += char
|
39
|
+
true
|
40
|
+
elsif @last_quote
|
41
|
+
add_to_buffer(char)
|
42
|
+
true
|
43
|
+
else
|
44
|
+
false
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def handle_escape(char)
|
49
|
+
if @escape
|
50
|
+
add_to_buffer(char)
|
51
|
+
@escape = false
|
52
|
+
true
|
53
|
+
elsif char == '\\'
|
54
|
+
@escape = true
|
55
|
+
@raw_buffer += char
|
56
|
+
true
|
57
|
+
else
|
58
|
+
false
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def add_to_buffer(char)
|
63
|
+
@buffer += char
|
64
|
+
@raw_buffer += char
|
65
|
+
end
|
66
|
+
|
67
|
+
def reset_parser
|
68
|
+
@value = []
|
69
|
+
@buffer = ''
|
70
|
+
@raw_buffer = ''
|
71
|
+
@escape = false
|
72
|
+
@last_quote = nil
|
73
|
+
end
|
74
|
+
|
75
|
+
def clean_buffer
|
76
|
+
@value << @buffer
|
77
|
+
@raw_buffer = ''
|
78
|
+
@buffer = ''
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
@@ -13,24 +13,18 @@ module ForemanMaintain
|
|
13
13
|
def initialize(collection)
|
14
14
|
@graph = Hash.new([])
|
15
15
|
@collection = collection
|
16
|
-
@
|
16
|
+
@steps_by_labels = @collection.group_by(&:label)
|
17
17
|
generate_label_graph
|
18
|
-
convert_label_graph_to_object_graph
|
19
18
|
end
|
20
19
|
|
21
20
|
def add_to_graph(key, dependencies = [])
|
22
21
|
return unless key
|
23
22
|
|
24
|
-
dependencies = dependencies.map do |dep|
|
25
|
-
next unless labels.include?(dep)
|
26
|
-
dep
|
27
|
-
end.compact
|
28
|
-
|
29
23
|
graph[key] = dependencies
|
30
24
|
end
|
31
25
|
|
32
26
|
def tsort_each_node(&block)
|
33
|
-
|
27
|
+
@collection.each(&block)
|
34
28
|
end
|
35
29
|
|
36
30
|
def tsort_each_child(node, &block)
|
@@ -39,51 +33,19 @@ module ForemanMaintain
|
|
39
33
|
|
40
34
|
private
|
41
35
|
|
42
|
-
def cache
|
43
|
-
ForemanMaintain.cache
|
44
|
-
end
|
45
|
-
|
46
|
-
def convert_label_graph_to_object_graph
|
47
|
-
graph.keys.each do |key|
|
48
|
-
graph[cache.fetch(key)] = graph[key].map { |dep| cache.fetch(dep) }
|
49
|
-
graph.delete(key)
|
50
|
-
end
|
51
|
-
|
52
|
-
graph
|
53
|
-
end
|
54
|
-
|
55
|
-
def extract_labels
|
56
|
-
collection.map(&:label)
|
57
|
-
end
|
58
|
-
|
59
|
-
def find_class(dep)
|
60
|
-
case dep
|
61
|
-
when Class
|
62
|
-
dep
|
63
|
-
when String
|
64
|
-
if dep.include?('::')
|
65
|
-
dep.split('::').reduce(Object) { |o, e| o.const_get(e) }
|
66
|
-
else
|
67
|
-
cache.fetch(dep)
|
68
|
-
end
|
69
|
-
else
|
70
|
-
cache.fetch(dep)
|
71
|
-
end
|
72
|
-
end
|
73
|
-
|
74
36
|
def generate_label_graph
|
75
37
|
collection.each do |object|
|
76
|
-
klass = object.class
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
add_to_graph(
|
81
|
-
map_label_to_object(object)
|
38
|
+
klass = object.is_a?(Class) ? object : object.class
|
39
|
+
klass.before.each do |label|
|
40
|
+
add_to_graph(labels_to_objects(label).first, [object])
|
41
|
+
end
|
42
|
+
add_to_graph(object, labels_to_objects(klass.after))
|
82
43
|
end
|
83
44
|
end
|
84
45
|
|
85
|
-
def
|
86
|
-
|
46
|
+
def labels_to_objects(labels)
|
47
|
+
labels = Array(labels)
|
48
|
+
labels.map { |label| @steps_by_labels[label] }.compact.flatten
|
87
49
|
end
|
88
50
|
end
|
89
51
|
end
|
@@ -1,9 +1,12 @@
|
|
1
1
|
module ForemanMaintain
|
2
2
|
class Executable
|
3
3
|
extend Forwardable
|
4
|
+
extend Concerns::Finders
|
4
5
|
attr_reader :options
|
5
|
-
def_delegators :execution,
|
6
|
-
|
6
|
+
def_delegators :execution,
|
7
|
+
:success?, :skipped?, :fail?, :warning?, :output,
|
8
|
+
:assumeyes?, :whitelisted?,
|
9
|
+
:execution, :puts, :print, :with_spinner, :ask, :storage
|
7
10
|
|
8
11
|
attr_accessor :associated_feature
|
9
12
|
|
@@ -56,11 +59,41 @@ module ForemanMaintain
|
|
56
59
|
@next_steps ||= []
|
57
60
|
end
|
58
61
|
|
62
|
+
# make the step to fail: the failure is considered significant and
|
63
|
+
# the next steps should not continue. The specific behaviour depends
|
64
|
+
# on the scenario it's being used on. In check-sets scenario, the next
|
65
|
+
# steps of the same scenario might continue, while the following scenarios
|
66
|
+
# would be aborted.
|
67
|
+
def fail!(message)
|
68
|
+
raise Error::Fail, message
|
69
|
+
end
|
70
|
+
|
71
|
+
# make the step a warning: this doesn't indicate the whole scenario should
|
72
|
+
# not continue, but the user will be warned before proceeding
|
73
|
+
def warn!(message)
|
74
|
+
raise Error::Warn, message
|
75
|
+
end
|
76
|
+
|
77
|
+
# rubocop:disable Style/AccessorMethodName
|
78
|
+
def set_fail(message)
|
79
|
+
execution.status = :fail
|
80
|
+
execution.output << message
|
81
|
+
end
|
82
|
+
|
83
|
+
def set_warn(message)
|
84
|
+
execution.status = :warning
|
85
|
+
execution.output << message
|
86
|
+
end
|
87
|
+
|
59
88
|
# public method to be overriden
|
60
89
|
def run
|
61
90
|
raise NotImplementedError
|
62
91
|
end
|
63
92
|
|
93
|
+
def executed?
|
94
|
+
@_execution ? true : false
|
95
|
+
end
|
96
|
+
|
64
97
|
def execution
|
65
98
|
if @_execution
|
66
99
|
@_execution
|
@@ -70,22 +103,12 @@ module ForemanMaintain
|
|
70
103
|
end
|
71
104
|
|
72
105
|
# public method to be overriden: it can perform additional checks
|
73
|
-
# to say, if the step is actually necessary to run. For example an `
|
106
|
+
# to say, if the step is actually necessary to run. For example an `Packages::Install`
|
74
107
|
# procedure would not be necessary when the package is already installed.
|
75
108
|
def necessary?
|
76
109
|
true
|
77
110
|
end
|
78
111
|
|
79
|
-
def fail!(message)
|
80
|
-
execution.status = :fail
|
81
|
-
execution.output << message
|
82
|
-
end
|
83
|
-
|
84
|
-
def warn!(message)
|
85
|
-
execution.status = :warning
|
86
|
-
execution.output << message
|
87
|
-
end
|
88
|
-
|
89
112
|
# update reporter about the current message
|
90
113
|
def say(message)
|
91
114
|
execution.update(message)
|
@@ -110,6 +133,34 @@ module ForemanMaintain
|
|
110
133
|
@next_steps = []
|
111
134
|
end
|
112
135
|
|
136
|
+
# serialization methods
|
137
|
+
def to_hash
|
138
|
+
ret = { :label => label, :param_values => @param_values }
|
139
|
+
if @_execution
|
140
|
+
ret[:status] = @_execution.status
|
141
|
+
ret[:output] = @_execution.output
|
142
|
+
end
|
143
|
+
ret
|
144
|
+
end
|
145
|
+
|
146
|
+
def matches_hash?(hash)
|
147
|
+
label == hash[:label] && @param_values == hash[:param_values]
|
148
|
+
end
|
149
|
+
|
150
|
+
def update_from_hash(hash)
|
151
|
+
raise "The step is not matching the hash #{hash.inspect}" unless matches_hash?(hash)
|
152
|
+
raise "Can't update step that was already executed" if @_execution
|
153
|
+
@_execution = Runner::StoredExecution.new(self, :status => hash[:status],
|
154
|
+
:output => hash[:output])
|
155
|
+
end
|
156
|
+
|
157
|
+
def inspect
|
158
|
+
ret = "#{self.class.name} label:#{label}"
|
159
|
+
ret << " params: #{@param_values.inspect}" unless @param_values.empty?
|
160
|
+
ret << " status: #{execution.status}" if executed?
|
161
|
+
ret
|
162
|
+
end
|
163
|
+
|
113
164
|
class << self
|
114
165
|
def ensure_instance
|
115
166
|
new
|
@@ -7,8 +7,14 @@ module ForemanMaintain
|
|
7
7
|
end
|
8
8
|
require 'foreman_maintain/reporter/cli_reporter'
|
9
9
|
|
10
|
+
DECISION_MAPPER = {
|
11
|
+
%w[y yes] => :yes,
|
12
|
+
%w[n next no] => :no,
|
13
|
+
%w[q quit] => :quit
|
14
|
+
}.freeze
|
15
|
+
|
10
16
|
# Each public method is a hook called by executor at the specific point
|
11
|
-
def before_scenario_starts(_scenario); end
|
17
|
+
def before_scenario_starts(_scenario, _last_scenario = nil); end
|
12
18
|
|
13
19
|
def before_execution_starts(_execution); end
|
14
20
|
|
@@ -16,7 +22,14 @@ module ForemanMaintain
|
|
16
22
|
|
17
23
|
def after_scenario_finishes(_scenario); end
|
18
24
|
|
19
|
-
def on_next_steps(
|
25
|
+
def on_next_steps(steps)
|
26
|
+
return if steps.empty?
|
27
|
+
if steps.size > 1
|
28
|
+
multiple_steps_decision(steps)
|
29
|
+
else
|
30
|
+
single_step_decision(steps.first)
|
31
|
+
end
|
32
|
+
end
|
20
33
|
|
21
34
|
def with_spinner(_message, &_block)
|
22
35
|
yield DummySpinner.new
|
@@ -29,7 +42,75 @@ module ForemanMaintain
|
|
29
42
|
def ask(_message); end
|
30
43
|
|
31
44
|
def assumeyes?
|
32
|
-
|
45
|
+
@assumeyes
|
46
|
+
end
|
47
|
+
|
48
|
+
# simple yes/no question, returns :yes, :no or :quit
|
49
|
+
def ask_decision(message, options = {})
|
50
|
+
options.validate_options!(:assumeyes)
|
51
|
+
assumeyes = options.fetch(:assumeyes, assumeyes?)
|
52
|
+
if assumeyes
|
53
|
+
print("#{message} (assuming yes)")
|
54
|
+
return :yes
|
55
|
+
end
|
56
|
+
until_valid_decision do
|
57
|
+
filter_decision(ask("#{message}, [y(yes), n(no), q(quit)]"))
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
private
|
62
|
+
|
63
|
+
def single_step_decision(step)
|
64
|
+
answer = ask_decision("Continue with step [#{step.description}]?")
|
65
|
+
if answer == :yes
|
66
|
+
step
|
67
|
+
else
|
68
|
+
answer
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
def multiple_steps_decision(steps)
|
73
|
+
puts 'There are multiple steps to proceed:'
|
74
|
+
steps.each_with_index do |step, index|
|
75
|
+
puts "#{index + 1}) #{step.description}"
|
76
|
+
end
|
77
|
+
ask_to_select('Select step to continue', steps, &:description)
|
78
|
+
end
|
79
|
+
|
80
|
+
def filter_decision(answer)
|
81
|
+
decision = nil
|
82
|
+
DECISION_MAPPER.each do |options, decision_label|
|
83
|
+
decision = decision_label if options.include?(answer)
|
84
|
+
end
|
85
|
+
decision
|
86
|
+
end
|
87
|
+
|
88
|
+
# rubocop:disable Metrics/MethodLength,Metrics/AbcSize
|
89
|
+
def ask_to_select(message, steps)
|
90
|
+
if assumeyes?
|
91
|
+
puts('(assuming first option)')
|
92
|
+
return steps.first
|
93
|
+
end
|
94
|
+
until_valid_decision do
|
95
|
+
answer = ask("#{message}, [n(next), q(quit)]")
|
96
|
+
if answer =~ /^\d+$/ && (answer.to_i - 1) < steps.size
|
97
|
+
steps[answer.to_i - 1]
|
98
|
+
else
|
99
|
+
decision = filter_decision(answer)
|
100
|
+
if decision == :yes
|
101
|
+
steps.first
|
102
|
+
else
|
103
|
+
decision
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
# loop over the block until it returns some non-false value
|
110
|
+
def until_valid_decision
|
111
|
+
decision = nil
|
112
|
+
decision = yield until decision
|
113
|
+
decision
|
33
114
|
end
|
34
115
|
end
|
35
116
|
end
|