foreman_maintain 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (44) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +674 -0
  3. data/README.md +193 -0
  4. data/bin/foreman-maintain +9 -0
  5. data/definitions/checks/foreman_tasks_not_paused.rb +14 -0
  6. data/definitions/checks/foreman_tasks_not_running.rb +10 -0
  7. data/definitions/features/downstream.rb +17 -0
  8. data/definitions/features/foreman_1_11_x.rb +7 -0
  9. data/definitions/features/foreman_1_7_x.rb +7 -0
  10. data/definitions/features/foreman_database.rb +15 -0
  11. data/definitions/features/foreman_tasks.rb +22 -0
  12. data/definitions/features/upstream.rb +7 -0
  13. data/definitions/procedures/foreman_tasks_resume.rb +13 -0
  14. data/definitions/scenarios/pre_upgrade_check_foreman_1_14.rb +12 -0
  15. data/definitions/scenarios/pre_upgrade_check_satellite_6_0_z.rb +12 -0
  16. data/definitions/scenarios/pre_upgrade_check_satellite_6_1.rb +12 -0
  17. data/definitions/scenarios/pre_upgrade_check_satellite_6_1_z.rb +12 -0
  18. data/definitions/scenarios/pre_upgrade_check_satellite_6_2.rb +12 -0
  19. data/definitions/scenarios/pre_upgrade_check_satellite_6_2_z.rb +12 -0
  20. data/definitions/scenarios/pre_upgrade_check_satellite_6_3.rb +12 -0
  21. data/lib/foreman_maintain.rb +54 -0
  22. data/lib/foreman_maintain/check.rb +34 -0
  23. data/lib/foreman_maintain/cli.rb +14 -0
  24. data/lib/foreman_maintain/cli/base.rb +47 -0
  25. data/lib/foreman_maintain/cli/health_command.rb +39 -0
  26. data/lib/foreman_maintain/cli/upgrade_command.rb +57 -0
  27. data/lib/foreman_maintain/concerns/finders.rb +69 -0
  28. data/lib/foreman_maintain/concerns/logger.rb +13 -0
  29. data/lib/foreman_maintain/concerns/metadata.rb +124 -0
  30. data/lib/foreman_maintain/concerns/system_helpers.rb +93 -0
  31. data/lib/foreman_maintain/config.rb +17 -0
  32. data/lib/foreman_maintain/detector.rb +146 -0
  33. data/lib/foreman_maintain/executable.rb +44 -0
  34. data/lib/foreman_maintain/feature.rb +22 -0
  35. data/lib/foreman_maintain/logger.rb +11 -0
  36. data/lib/foreman_maintain/procedure.rb +8 -0
  37. data/lib/foreman_maintain/reporter.rb +18 -0
  38. data/lib/foreman_maintain/reporter/cli_reporter.rb +177 -0
  39. data/lib/foreman_maintain/runner.rb +39 -0
  40. data/lib/foreman_maintain/runner/execution.rb +74 -0
  41. data/lib/foreman_maintain/scenario.rb +46 -0
  42. data/lib/foreman_maintain/top_level_modules.rb +13 -0
  43. data/lib/foreman_maintain/version.rb +3 -0
  44. 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,13 @@
1
+ require 'logger'
2
+
3
+ module ForemanMaintain
4
+ module Concerns
5
+ module Logger
6
+ def logger
7
+ @logger ||= ::Logger.new($stderr).tap do |logger|
8
+ logger.level = ForemanMaintain.config.log_level
9
+ end
10
+ end
11
+ end
12
+ end
13
+ 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