foreman_maintain 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/LICENSE +674 -0
- data/README.md +193 -0
- data/bin/foreman-maintain +9 -0
- data/definitions/checks/foreman_tasks_not_paused.rb +14 -0
- data/definitions/checks/foreman_tasks_not_running.rb +10 -0
- data/definitions/features/downstream.rb +17 -0
- data/definitions/features/foreman_1_11_x.rb +7 -0
- data/definitions/features/foreman_1_7_x.rb +7 -0
- data/definitions/features/foreman_database.rb +15 -0
- data/definitions/features/foreman_tasks.rb +22 -0
- data/definitions/features/upstream.rb +7 -0
- data/definitions/procedures/foreman_tasks_resume.rb +13 -0
- data/definitions/scenarios/pre_upgrade_check_foreman_1_14.rb +12 -0
- data/definitions/scenarios/pre_upgrade_check_satellite_6_0_z.rb +12 -0
- data/definitions/scenarios/pre_upgrade_check_satellite_6_1.rb +12 -0
- data/definitions/scenarios/pre_upgrade_check_satellite_6_1_z.rb +12 -0
- data/definitions/scenarios/pre_upgrade_check_satellite_6_2.rb +12 -0
- data/definitions/scenarios/pre_upgrade_check_satellite_6_2_z.rb +12 -0
- data/definitions/scenarios/pre_upgrade_check_satellite_6_3.rb +12 -0
- data/lib/foreman_maintain.rb +54 -0
- data/lib/foreman_maintain/check.rb +34 -0
- data/lib/foreman_maintain/cli.rb +14 -0
- data/lib/foreman_maintain/cli/base.rb +47 -0
- data/lib/foreman_maintain/cli/health_command.rb +39 -0
- data/lib/foreman_maintain/cli/upgrade_command.rb +57 -0
- data/lib/foreman_maintain/concerns/finders.rb +69 -0
- data/lib/foreman_maintain/concerns/logger.rb +13 -0
- data/lib/foreman_maintain/concerns/metadata.rb +124 -0
- data/lib/foreman_maintain/concerns/system_helpers.rb +93 -0
- data/lib/foreman_maintain/config.rb +17 -0
- data/lib/foreman_maintain/detector.rb +146 -0
- data/lib/foreman_maintain/executable.rb +44 -0
- data/lib/foreman_maintain/feature.rb +22 -0
- data/lib/foreman_maintain/logger.rb +11 -0
- data/lib/foreman_maintain/procedure.rb +8 -0
- data/lib/foreman_maintain/reporter.rb +18 -0
- data/lib/foreman_maintain/reporter/cli_reporter.rb +177 -0
- data/lib/foreman_maintain/runner.rb +39 -0
- data/lib/foreman_maintain/runner/execution.rb +74 -0
- data/lib/foreman_maintain/scenario.rb +46 -0
- data/lib/foreman_maintain/top_level_modules.rb +13 -0
- data/lib/foreman_maintain/version.rb +3 -0
- metadata +173 -0
@@ -0,0 +1,34 @@
|
|
1
|
+
module ForemanMaintain
|
2
|
+
class Check < Executable
|
3
|
+
include Concerns::Logger
|
4
|
+
include Concerns::SystemHelpers
|
5
|
+
include Concerns::Metadata
|
6
|
+
include Concerns::Finders
|
7
|
+
|
8
|
+
attr_accessor :associated_feature
|
9
|
+
|
10
|
+
class Fail < StandardError
|
11
|
+
end
|
12
|
+
|
13
|
+
def initialize(associated_feature)
|
14
|
+
@associated_feature = associated_feature
|
15
|
+
end
|
16
|
+
|
17
|
+
def assert(condition, error_message)
|
18
|
+
raise Fail, error_message unless condition
|
19
|
+
end
|
20
|
+
|
21
|
+
# public method to be overriden
|
22
|
+
def run
|
23
|
+
raise NotImplementedError
|
24
|
+
end
|
25
|
+
|
26
|
+
# internal method called by executor
|
27
|
+
def __run__(execution)
|
28
|
+
super
|
29
|
+
rescue Fail => e
|
30
|
+
execution.status = :fail
|
31
|
+
execution.output << e.message
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
require 'clamp'
|
2
|
+
require 'highline'
|
3
|
+
require 'foreman_maintain/cli/base'
|
4
|
+
require 'foreman_maintain/cli/health_command'
|
5
|
+
require 'foreman_maintain/cli/upgrade_command'
|
6
|
+
|
7
|
+
module ForemanMaintain
|
8
|
+
module Cli
|
9
|
+
class MainCommand < Base
|
10
|
+
subcommand 'health', 'Health related commands', HealthCommand
|
11
|
+
subcommand 'upgrade', 'Upgrade related commands', UpgradeCommand
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
module ForemanMaintain
|
2
|
+
module Cli
|
3
|
+
class Base < Clamp::Command
|
4
|
+
include Concerns::Finders
|
5
|
+
|
6
|
+
def dashize(string)
|
7
|
+
string.to_s.tr('_', '-')
|
8
|
+
end
|
9
|
+
|
10
|
+
def underscorize(string)
|
11
|
+
string.to_s.tr('-', '_')
|
12
|
+
end
|
13
|
+
|
14
|
+
def label_string(string)
|
15
|
+
HighLine.color("[#{dashize(string)}]", :yellow)
|
16
|
+
end
|
17
|
+
|
18
|
+
def tag_string(string)
|
19
|
+
HighLine.color("[#{dashize(string)}]", :cyan)
|
20
|
+
end
|
21
|
+
|
22
|
+
def reporter
|
23
|
+
@reporter ||= ForemanMaintain::Reporter::CLIReporter.new
|
24
|
+
end
|
25
|
+
|
26
|
+
def run_scenario(scenario)
|
27
|
+
ForemanMaintain::Runner.new(reporter, scenario).run
|
28
|
+
end
|
29
|
+
|
30
|
+
def available_checks
|
31
|
+
filter = {}
|
32
|
+
filter[:tags] = tags if respond_to?(:tags)
|
33
|
+
ForemanMaintain.available_checks(filter)
|
34
|
+
end
|
35
|
+
|
36
|
+
def available_tags(collection)
|
37
|
+
collection.inject([]) { |array, check| array.concat(check.tags).uniq }.sort_by(&:to_s)
|
38
|
+
end
|
39
|
+
|
40
|
+
def self.tags_option
|
41
|
+
option '--tags', 'tags', 'Limit only for specific set of tags' do |tags|
|
42
|
+
tags.split(',').map(&:strip).map { |tag| underscorize(tag).to_sym }
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
module ForemanMaintain
|
2
|
+
module Cli
|
3
|
+
class HealthCommand < Base
|
4
|
+
subcommand 'list-checks', 'List the checks based on criteria' do
|
5
|
+
tags_option
|
6
|
+
|
7
|
+
def execute
|
8
|
+
available_checks.each { |check| print_check_info(check) }
|
9
|
+
end
|
10
|
+
|
11
|
+
def print_check_info(check)
|
12
|
+
desc = "#{label_string(check.label)} #{check.description}".ljust(80)
|
13
|
+
tags = check.tags.map { |t| tag_string(t) }.join(' ').to_s
|
14
|
+
puts "#{desc} #{tags}"
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
subcommand 'list-tags', 'List the tags to use for filtering checks' do
|
19
|
+
def execute
|
20
|
+
available_tags(available_checks).each { |tag| puts tag_string(tag) }
|
21
|
+
end
|
22
|
+
|
23
|
+
def print_check_info(check)
|
24
|
+
desc = "#{label_string(check.label)} #{check.description}".ljust(80)
|
25
|
+
tags = check.tags.map { |t| tag_string(t) }.join(' ').to_s
|
26
|
+
puts "#{desc} #{tags}"
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
subcommand 'check', 'Run the health checks against the system' do
|
31
|
+
tags_option
|
32
|
+
|
33
|
+
def execute
|
34
|
+
run_scenario(Scenario::ChecksScenario.new(tags || [:basic]))
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
module ForemanMaintain
|
2
|
+
module Cli
|
3
|
+
class UpgradeCommand < Base
|
4
|
+
def tags_to_versions
|
5
|
+
{ :satellite_6_0_z => '6.0.z',
|
6
|
+
:satellite_6_1 => '6.1',
|
7
|
+
:satellite_6_1_z => '6.1.z',
|
8
|
+
:satellite_6_2 => '6.2',
|
9
|
+
:satellite_6_2_z => '6.2.z',
|
10
|
+
:satellite_6_3 => '6.3' }
|
11
|
+
end
|
12
|
+
|
13
|
+
# We search for scenarios available for the system and determine
|
14
|
+
# user-friendly version numbers for it.
|
15
|
+
# This method returns a hash of mapping the versions to scenarios to run
|
16
|
+
# The tag is determining which kind of scenario we're searching for
|
17
|
+
# (such as pre_upgrade_check)
|
18
|
+
def available_target_versions(tag)
|
19
|
+
conditions = { :tags => [tag] }
|
20
|
+
find_scenarios(conditions).inject({}) do |hash, scenario|
|
21
|
+
# find tag that represent the version upgrade
|
22
|
+
version_tag = scenario.tags.find { |t| tags_to_versions.key?(t) }
|
23
|
+
if version_tag
|
24
|
+
hash.update(tags_to_versions[version_tag] => scenario)
|
25
|
+
else
|
26
|
+
hash
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def print_versions(target_versions)
|
32
|
+
target_versions.keys.sort.each { |version| puts version }
|
33
|
+
end
|
34
|
+
|
35
|
+
subcommand 'list-versions', 'List versions this system is upgradable to' do
|
36
|
+
def execute
|
37
|
+
print_versions(available_target_versions(:pre_upgrade_check))
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
subcommand 'check', 'Run pre-upgrade checks for upgradeing to specified version' do
|
42
|
+
parameter 'TARGET_VERSION', 'Target version of the upgrade', :required => false
|
43
|
+
def execute
|
44
|
+
versions_to_scenarios = available_target_versions(:pre_upgrade_check)
|
45
|
+
scenario = versions_to_scenarios[target_version]
|
46
|
+
if scenario
|
47
|
+
run_scenario(scenario)
|
48
|
+
else
|
49
|
+
puts "The specified version #{target_version} is unavailable"
|
50
|
+
puts 'Possible target versions are:'
|
51
|
+
print_versions(versions_to_scenarios)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
module ForemanMaintain
|
2
|
+
module Concerns
|
3
|
+
module Finders
|
4
|
+
def detector
|
5
|
+
@detector ||= ForemanMaintain.detector
|
6
|
+
end
|
7
|
+
|
8
|
+
def feature(label)
|
9
|
+
detector.feature(label)
|
10
|
+
end
|
11
|
+
|
12
|
+
def check(label)
|
13
|
+
ensure_one_object(:check, label)
|
14
|
+
end
|
15
|
+
|
16
|
+
def find_checks(conditions)
|
17
|
+
detector.available_checks(conditions)
|
18
|
+
end
|
19
|
+
|
20
|
+
def procedure(label)
|
21
|
+
ensure_one_object(:procedure, label)
|
22
|
+
end
|
23
|
+
|
24
|
+
def find_procedures(conditions)
|
25
|
+
detector.available_procedures(conditions)
|
26
|
+
end
|
27
|
+
|
28
|
+
def find_scenarios(conditions)
|
29
|
+
detector.available_scenarios(conditions)
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
|
34
|
+
def ensure_one_object(object_type, label_or_class)
|
35
|
+
objects = find_objects(object_type, label_or_class)
|
36
|
+
if objects.first.nil?
|
37
|
+
raise "#{object_type} #{label_or_class} not present"
|
38
|
+
elsif objects.size > 1
|
39
|
+
raise "Multiple objects of #{object_type} found for #{label_or_class}"
|
40
|
+
else
|
41
|
+
objects.first
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def label_or_class_condition(label_or_class)
|
46
|
+
case label_or_class
|
47
|
+
when Symbol
|
48
|
+
{ :label => label_or_class }
|
49
|
+
when Class
|
50
|
+
{ :class => label_or_class }
|
51
|
+
else
|
52
|
+
raise 'Expecting symbol or class'
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def find_objects(object_type, label_or_class)
|
57
|
+
conditions = label_or_class_condition(label_or_class)
|
58
|
+
case object_type
|
59
|
+
when :procedure
|
60
|
+
detector.available_procedures(conditions)
|
61
|
+
when :check
|
62
|
+
detector.available_checks(conditions)
|
63
|
+
else
|
64
|
+
raise "Unexpected object type #{object_type}"
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
@@ -0,0 +1,124 @@
|
|
1
|
+
module ForemanMaintain
|
2
|
+
module Concerns
|
3
|
+
module Metadata
|
4
|
+
module DSL
|
5
|
+
def label(label)
|
6
|
+
metadata[:label] = label
|
7
|
+
end
|
8
|
+
|
9
|
+
def tags(*tags)
|
10
|
+
metadata[:tags].concat(tags)
|
11
|
+
end
|
12
|
+
|
13
|
+
def description(description)
|
14
|
+
metadata[:description] = description
|
15
|
+
end
|
16
|
+
|
17
|
+
def confine(&block)
|
18
|
+
metadata[:confine_blocks] << block
|
19
|
+
end
|
20
|
+
|
21
|
+
# Mark the class as manual: this means the instance
|
22
|
+
# of class will not be initialized by detector to check the confine block
|
23
|
+
# to determine if it's valid on the system or not.
|
24
|
+
# The classes marked for manual detect need to be initialized
|
25
|
+
# in from other places (such as `additional_features` in Feature)
|
26
|
+
def manual_detection
|
27
|
+
@autodetect = false
|
28
|
+
end
|
29
|
+
|
30
|
+
def autodetect?
|
31
|
+
defined?(@autodetect) ? @autodetect : true
|
32
|
+
end
|
33
|
+
|
34
|
+
# Specify what feature the definition related to.
|
35
|
+
def for_feature(feature_label)
|
36
|
+
metadata[:for_feature] = feature_label
|
37
|
+
confine do
|
38
|
+
feature(feature_label)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
module ClassMethods
|
44
|
+
def inherited(klass)
|
45
|
+
sub_classes << klass
|
46
|
+
end
|
47
|
+
|
48
|
+
# Override if the class should be used as parent class only.
|
49
|
+
# By default, we assume the class that does't inherit from class with
|
50
|
+
# Metadata is abstract = the base class of particular concept
|
51
|
+
def abstract_class
|
52
|
+
!(superclass < Metadata)
|
53
|
+
end
|
54
|
+
|
55
|
+
def sub_classes
|
56
|
+
@sub_classes ||= []
|
57
|
+
end
|
58
|
+
|
59
|
+
def all_sub_classes(ignore_abstract_classes = true)
|
60
|
+
ret = []
|
61
|
+
ret << self if !ignore_abstract_classes || !abstract_class
|
62
|
+
sub_classes.each do |sub_class|
|
63
|
+
ret.concat(sub_class.all_sub_classes(ignore_abstract_classes))
|
64
|
+
end
|
65
|
+
ret
|
66
|
+
end
|
67
|
+
|
68
|
+
def metadata
|
69
|
+
@metadata ||= initialize_metadata
|
70
|
+
end
|
71
|
+
|
72
|
+
def initialize_metadata
|
73
|
+
{ :tags => [],
|
74
|
+
:confine_blocks => [] }.tap do |metadata|
|
75
|
+
if superclass.respond_to?(:metadata)
|
76
|
+
metadata[:label] = superclass.metadata[:label]
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
def self.included(klass)
|
83
|
+
klass.extend(DSL)
|
84
|
+
klass.extend(ClassMethods)
|
85
|
+
end
|
86
|
+
|
87
|
+
def metadata
|
88
|
+
self.class.metadata
|
89
|
+
end
|
90
|
+
|
91
|
+
def label
|
92
|
+
metadata[:label] || generate_label
|
93
|
+
end
|
94
|
+
|
95
|
+
def description
|
96
|
+
metadata[:description] || to_s
|
97
|
+
end
|
98
|
+
|
99
|
+
def tags
|
100
|
+
metadata[:tags]
|
101
|
+
end
|
102
|
+
|
103
|
+
def present?
|
104
|
+
@_present ||= evaluate_confines
|
105
|
+
end
|
106
|
+
|
107
|
+
private
|
108
|
+
|
109
|
+
def evaluate_confines
|
110
|
+
raise 'Recursive dependency in confine blocks detected' if @confines_evaluation_in_progress
|
111
|
+
@confines_evaluation_in_progress = true
|
112
|
+
metadata[:confine_blocks].all? do |block|
|
113
|
+
instance_exec(&block)
|
114
|
+
end
|
115
|
+
ensure
|
116
|
+
@confines_evaluation_in_progress = false
|
117
|
+
end
|
118
|
+
|
119
|
+
def generate_label
|
120
|
+
self.class.name.split('::').last.split(/(?=[A-Z])/).map(&:downcase).join('_').to_sym
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
@@ -0,0 +1,93 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'csv'
|
3
|
+
require 'English'
|
4
|
+
|
5
|
+
module ForemanMaintain
|
6
|
+
module Concerns
|
7
|
+
module SystemHelpers
|
8
|
+
include Logger
|
9
|
+
|
10
|
+
def self.included(klass)
|
11
|
+
klass.extend(self)
|
12
|
+
end
|
13
|
+
|
14
|
+
# class we use for comparing the versions
|
15
|
+
class Version < Gem::Version
|
16
|
+
end
|
17
|
+
|
18
|
+
class ExecutionError < StandardError
|
19
|
+
def initialize(command, input, output)
|
20
|
+
@command = command
|
21
|
+
@input = input
|
22
|
+
@output = output
|
23
|
+
super(generate_message)
|
24
|
+
end
|
25
|
+
|
26
|
+
def generate_message
|
27
|
+
ret = "Could not execute #{command}"
|
28
|
+
ret << "with input '#{input}'" if input
|
29
|
+
ret << ":\n #{output}" if output && !output.empty?
|
30
|
+
ret
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def version(value)
|
35
|
+
Version.new(value)
|
36
|
+
end
|
37
|
+
|
38
|
+
def check_min_version(name, minimal_version)
|
39
|
+
current_version = rpm_version(name)
|
40
|
+
if current_version
|
41
|
+
return current_version >= version(minimal_version)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def downstream_installation?
|
46
|
+
execute?('rpm -q satellite') ||
|
47
|
+
(execute('rpm -q foreman') =~ /6sat.noarch/)
|
48
|
+
end
|
49
|
+
|
50
|
+
def rpm_version(name)
|
51
|
+
rpm_version = execute(%(rpm -q '#{name}' --queryformat="%{VERSION}"))
|
52
|
+
if $CHILD_STATUS.success?
|
53
|
+
version(rpm_version)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def parse_csv(data)
|
58
|
+
parsed_data = CSV.parse(data)
|
59
|
+
header = parsed_data.first
|
60
|
+
parsed_data[1..-1].map do |row|
|
61
|
+
Hash[*header.zip(row).flatten(1)]
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def execute?(command, input = nil)
|
66
|
+
execute(command, input)
|
67
|
+
$CHILD_STATUS.success?
|
68
|
+
end
|
69
|
+
|
70
|
+
def execute!(command, input = nil)
|
71
|
+
output = execute(command, input)
|
72
|
+
if $CHILD_STATUS.success?
|
73
|
+
output
|
74
|
+
else
|
75
|
+
raise ExecutionError.new(command, input, output)
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
def execute(command, stdin = nil)
|
80
|
+
logger.debug("Running command #{command.inspect} with stdin #{stdin.inspect}")
|
81
|
+
IO.popen("#{command} 2>&1", 'r+') do |f|
|
82
|
+
if stdin
|
83
|
+
f.puts(stdin)
|
84
|
+
f.close_write
|
85
|
+
end
|
86
|
+
output = f.read
|
87
|
+
logger.debug("output of the command:\n #{output}")
|
88
|
+
output
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|