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.
Files changed (66) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +107 -25
  3. data/bin/foreman-maintain +1 -0
  4. data/definitions/checks/disk_speed_minimal.rb +31 -19
  5. data/definitions/checks/foreman_tasks/invalid/check_old.rb +20 -0
  6. data/definitions/checks/foreman_tasks/invalid/check_pending_state.rb +20 -0
  7. data/definitions/checks/foreman_tasks/invalid/check_planning_state.rb +20 -0
  8. data/definitions/checks/foreman_tasks/not_paused.rb +29 -0
  9. data/definitions/checks/foreman_tasks/not_running.rb +14 -0
  10. data/definitions/checks/sync_plans/with_disabled_status.rb +18 -0
  11. data/definitions/checks/sync_plans/with_enabled_status.rb +18 -0
  12. data/definitions/checks/system_registration.rb +31 -0
  13. data/definitions/features/downstream.rb +5 -3
  14. data/definitions/features/foreman_1_11_x.rb +4 -2
  15. data/definitions/features/foreman_1_7_x.rb +5 -3
  16. data/definitions/features/foreman_database.rb +11 -5
  17. data/definitions/features/foreman_tasks.rb +118 -9
  18. data/definitions/features/sync_plans.rb +75 -0
  19. data/definitions/features/upstream.rb +5 -3
  20. data/definitions/procedures/foreman_tasks/delete.rb +33 -0
  21. data/definitions/procedures/foreman_tasks/resume.rb +14 -0
  22. data/definitions/procedures/foreman_tasks/ui_investigate.rb +19 -0
  23. data/definitions/procedures/hammer_setup.rb +50 -0
  24. data/definitions/procedures/install_package.rb +17 -0
  25. data/definitions/procedures/sync_plans/disable.rb +22 -0
  26. data/definitions/procedures/sync_plans/enable.rb +21 -0
  27. data/definitions/scenarios/pre_upgrade_check_foreman_1_14.rb +7 -6
  28. data/definitions/scenarios/pre_upgrade_check_satellite_6_0_z.rb +8 -6
  29. data/definitions/scenarios/pre_upgrade_check_satellite_6_1.rb +8 -6
  30. data/definitions/scenarios/pre_upgrade_check_satellite_6_1_z.rb +8 -6
  31. data/definitions/scenarios/pre_upgrade_check_satellite_6_2.rb +8 -6
  32. data/definitions/scenarios/pre_upgrade_check_satellite_6_2_z.rb +8 -6
  33. data/definitions/scenarios/pre_upgrade_check_satellite_6_3.rb +8 -6
  34. data/lib/foreman_maintain.rb +52 -5
  35. data/lib/foreman_maintain/check.rb +18 -12
  36. data/lib/foreman_maintain/cli/base.rb +9 -2
  37. data/lib/foreman_maintain/cli/health_command.rb +2 -1
  38. data/lib/foreman_maintain/cli/upgrade_command.rb +2 -0
  39. data/lib/foreman_maintain/concerns/hammer.rb +20 -0
  40. data/lib/foreman_maintain/concerns/logger.rb +1 -5
  41. data/lib/foreman_maintain/concerns/metadata.rb +138 -31
  42. data/lib/foreman_maintain/concerns/system_helpers.rb +36 -32
  43. data/lib/foreman_maintain/config.rb +40 -5
  44. data/lib/foreman_maintain/core_ext.rb +24 -0
  45. data/lib/foreman_maintain/detector.rb +12 -13
  46. data/lib/foreman_maintain/error.rb +28 -0
  47. data/lib/foreman_maintain/executable.rb +86 -11
  48. data/lib/foreman_maintain/feature.rb +1 -0
  49. data/lib/foreman_maintain/param.rb +47 -0
  50. data/lib/foreman_maintain/reporter.rb +20 -3
  51. data/lib/foreman_maintain/reporter/cli_reporter.rb +166 -66
  52. data/lib/foreman_maintain/runner.rb +56 -13
  53. data/lib/foreman_maintain/runner/execution.rb +8 -0
  54. data/lib/foreman_maintain/scenario.rb +46 -2
  55. data/lib/foreman_maintain/top_level_modules.rb +3 -0
  56. data/lib/foreman_maintain/utils.rb +2 -0
  57. data/lib/foreman_maintain/utils/command_runner.rb +101 -0
  58. data/lib/foreman_maintain/utils/disk/device.rb +5 -9
  59. data/lib/foreman_maintain/utils/hammer.rb +78 -0
  60. data/lib/foreman_maintain/version.rb +1 -1
  61. data/lib/foreman_maintain/yaml_storage.rb +48 -0
  62. metadata +27 -9
  63. data/definitions/checks/foreman_tasks_not_paused.rb +0 -14
  64. data/definitions/checks/foreman_tasks_not_running.rb +0 -10
  65. data/definitions/procedures/foreman_tasks_resume.rb +0 -13
  66. data/lib/foreman_maintain/logger.rb +0 -11
@@ -1,8 +1,10 @@
1
1
  class Features::Downstream < ForemanMaintain::Feature
2
- label :downstream
2
+ metadata do
3
+ label :downstream
3
4
 
4
- confine do
5
- downstream_installation?
5
+ confine do
6
+ downstream_installation?
7
+ end
6
8
  end
7
9
 
8
10
  def current_version
@@ -1,7 +1,9 @@
1
1
  require 'features/foreman_1_7_x'
2
2
 
3
3
  class Features::Foreman_1_11_x < Features::Foreman_1_7_x
4
- confine do
5
- check_min_version('foreman', '1.11')
4
+ metadata do
5
+ confine do
6
+ check_min_version('foreman', '1.11')
7
+ end
6
8
  end
7
9
  end
@@ -1,7 +1,9 @@
1
1
  class Features::Foreman_1_7_x < ForemanMaintain::Feature
2
- label :foreman
2
+ metadata do
3
+ label :foreman
3
4
 
4
- confine do
5
- check_min_version('foreman', '1.7')
5
+ confine do
6
+ check_min_version('foreman', '1.7')
7
+ end
6
8
  end
7
9
  end
@@ -1,15 +1,21 @@
1
1
  class Features::ForemanDatabase < ForemanMaintain::Feature
2
- label :foreman_database
2
+ metadata do
3
+ label :foreman_database
3
4
 
4
- confine do
5
- File.exist?('/etc/foreman/database.yml')
5
+ confine do
6
+ File.exist?('/etc/foreman/database.yml')
7
+ end
6
8
  end
7
9
 
8
10
  def query(sql)
9
- parse_csv(psql(%{COPY (#{sql}) TO STDOUT WITH CSV HEADER}))
11
+ parse_csv(query_csv(sql))
12
+ end
13
+
14
+ def query_csv(sql)
15
+ psql(%{COPY (#{sql}) TO STDOUT WITH CSV HEADER})
10
16
  end
11
17
 
12
18
  def psql(query)
13
- execute("su - postgres -c 'psql -d foreman'", query)
19
+ execute("su - postgres -c 'psql -d foreman'", :stdin => query)
14
20
  end
15
21
  end
@@ -1,9 +1,35 @@
1
1
  class Features::ForemanTasks < ForemanMaintain::Feature
2
- label :foreman_tasks
2
+ MIN_AGE = 30
3
3
 
4
- confine do
5
- check_min_version('ruby193-rubygem-foreman-tasks', '0.6') ||
6
- check_min_version('tfm-rubygem-foreman-tasks', '0.7')
4
+ SAFE_TO_DELETE = %w[
5
+ Actions::Katello::Host::GenerateApplicability
6
+ Actions::Katello::RepositorySet::ScanCdn
7
+ Actions::Katello::Host::Hypervisors
8
+ Actions::Katello::Host::HypervisorsUpdate
9
+ Actions::Foreman::Host::ImportFacts
10
+ Actions::Candlepin::ListenOnCandlepinEvents
11
+ Actions::Katello::EventQueue::Monitor
12
+ ].freeze
13
+
14
+ metadata do
15
+ label :foreman_tasks
16
+
17
+ confine do
18
+ check_min_version('ruby193-rubygem-foreman-tasks', '0.6') ||
19
+ check_min_version('tfm-rubygem-foreman-tasks', '0.7')
20
+ end
21
+ end
22
+
23
+ def backup_tasks(state)
24
+ backup_table('dynflow_execution_plans', state, 'uuid') { |status| yield(status) }
25
+ backup_table('dynflow_steps', state) { |status| yield(status) }
26
+ backup_table('dynflow_actions', state) { |status| yield(status) }
27
+
28
+ yield('Backup Tasks [running]')
29
+ export_csv("SELECT * FROM foreman_tasks_tasks WHERE #{condition(state)}",
30
+ 'foreman_tasks_tasks.csv', state)
31
+ yield('Backup Tasks [DONE]')
32
+ @backup_dir = nil
7
33
  end
8
34
 
9
35
  def running_tasks_count
@@ -13,10 +39,93 @@ class Features::ForemanTasks < ForemanMaintain::Feature
13
39
  0
14
40
  end
15
41
 
16
- def paused_tasks_count
17
- # feature(:foreman_database).query(<<-SQL).first['count'].to_i
18
- # SELECT count(*) AS count FROM foreman_tasks_tasks WHERE state in ('running', 'paused')
19
- # SQL
20
- 5
42
+ def paused_tasks_count(ignored_tasks = [])
43
+ sql = <<-SQL
44
+ SELECT count(*) AS count
45
+ FROM foreman_tasks_tasks
46
+ WHERE state IN ('paused')
47
+ SQL
48
+ unless ignored_tasks.empty?
49
+ sql << "AND label NOT IN (#{quotize(ignored_tasks)})"
50
+ end
51
+ feature(:foreman_database).query(sql).first['count'].to_i
52
+ end
53
+
54
+ def count(state)
55
+ feature(:foreman_database).query(<<-SQL).first['count'].to_i
56
+ SELECT count(*) AS count FROM foreman_tasks_tasks WHERE #{condition(state)}
57
+ SQL
58
+ end
59
+
60
+ def delete(state)
61
+ tasks_condition = condition(state)
62
+
63
+ feature(:foreman_database).psql(<<-SQL)
64
+ BEGIN;
65
+ DELETE FROM dynflow_steps USING foreman_tasks_tasks WHERE (foreman_tasks_tasks.external_id = dynflow_steps.execution_plan_uuid) AND #{tasks_condition};
66
+ DELETE FROM dynflow_actions USING foreman_tasks_tasks WHERE (foreman_tasks_tasks.external_id = dynflow_actions.execution_plan_uuid) AND #{tasks_condition};
67
+ DELETE FROM dynflow_execution_plans USING foreman_tasks_tasks WHERE (foreman_tasks_tasks.external_id = dynflow_execution_plans.uuid) AND #{tasks_condition};
68
+ DELETE FROM foreman_tasks_tasks WHERE #{tasks_condition};
69
+ COMMIT;
70
+ SQL
71
+
72
+ count(state)
73
+ end
74
+
75
+ def condition(state)
76
+ raise 'Invalid State' unless valid(state)
77
+
78
+ if state == :old
79
+ old_tasks_condition
80
+ else
81
+ tasks_condition(state)
82
+ end
83
+ end
84
+
85
+ private
86
+
87
+ def backup_dir(state)
88
+ @backup_dir ||=
89
+ "/var/lib/foreman/backup-tasks/#{state}/#{Time.now.strftime('%Y-%m-%d_%H-%M-%S')}"
90
+ end
91
+
92
+ def backup_table(table, state, fkey = 'execution_plan_uuid')
93
+ yield("Backup #{table} [running]")
94
+ sql = "SELECT #{table}.* FROM foreman_tasks_tasks JOIN #{table} ON\
95
+ (foreman_tasks_tasks.external_id = #{table}.#{fkey})"
96
+ export_csv(sql, "#{table}.csv", state)
97
+ yield("Backup #{table} [DONE]")
98
+ end
99
+
100
+ def export_csv(sql, file_name, state)
101
+ dir = prepare_for_backup(state)
102
+ filepath = "#{dir}/#{file_name}"
103
+ execute("echo \"COPY (#{sql}) TO STDOUT WITH CSV;\" \
104
+ | su - postgres -c '/usr/bin/psql -d foreman' | bzip2 -9 > #{filepath}.bz2")
105
+ end
106
+
107
+ def old_tasks_condition(state = "'stopped', 'paused'")
108
+ "foreman_tasks_tasks.state IN (#{state}) AND " \
109
+ "foreman_tasks_tasks.started_at < CURRENT_DATE - INTERVAL '#{MIN_AGE} days'"
110
+ end
111
+
112
+ def prepare_for_backup(state)
113
+ dir = backup_dir(state)
114
+ execute("mkdir -p #{dir}")
115
+ dir
116
+ end
117
+
118
+ def quotize(array)
119
+ array.map { |el| "'#{el}'" }.join(',')
120
+ end
121
+
122
+ def tasks_condition(state)
123
+ safe_to_delete_tasks = quotize(SAFE_TO_DELETE)
124
+ "foreman_tasks_tasks.state = '#{state}' AND " \
125
+ "foreman_tasks_tasks.label IN (#{safe_to_delete_tasks})"
126
+ end
127
+
128
+ def valid(state)
129
+ [:old, :planning, :pending].include?(state)
21
130
  end
22
131
  end
@@ -0,0 +1,75 @@
1
+ class Features::SyncPlans < ForemanMaintain::Feature
2
+ metadata do
3
+ label :sync_plans
4
+ end
5
+
6
+ def active_sync_plans_count
7
+ feature(:foreman_database).query(
8
+ <<-SQL
9
+ SELECT count(*) AS count FROM katello_sync_plans WHERE enabled ='t'
10
+ SQL
11
+ ).first['count'].to_i
12
+ end
13
+
14
+ def ids_by_status(enabled = true)
15
+ enabled = enabled ? 't' : 'f'
16
+ feature(:foreman_database).query(
17
+ <<-SQL
18
+ SELECT id FROM katello_sync_plans WHERE enabled ='#{enabled}'
19
+ SQL
20
+ ).map { |r| r['id'].to_i }
21
+ end
22
+
23
+ def disabled_plans_count
24
+ data[:disabled].length
25
+ end
26
+
27
+ def make_disable(ids)
28
+ update_records(ids, false)
29
+ end
30
+
31
+ def make_enable
32
+ update_records(data[:disabled], true)
33
+ end
34
+
35
+ private
36
+
37
+ def update_records(ids, enabled)
38
+ updated_record_ids = []
39
+ ids.each do |sp_id|
40
+ result = hammer("sync-plan update --id #{sp_id} --enabled #{enabled}")
41
+ if result.include?('Sync plan updated')
42
+ updated_record_ids << sp_id
43
+ else
44
+ raise result
45
+ end
46
+ end
47
+ updated_record_ids
48
+ ensure
49
+ new_data = sync_plan_data(enabled, updated_record_ids)
50
+ save_state(new_data)
51
+ end
52
+
53
+ def data
54
+ upgrade_storage = ForemanMaintain.storage(:upgrade)
55
+ @data ||= upgrade_storage.data.fetch(:sync_plans, :enabled => [], :disabled => [])
56
+ @data
57
+ end
58
+
59
+ def sync_plan_data(enabled, new_ids)
60
+ sync_plan_hash = data
61
+ if enabled
62
+ sync_plan_hash[:disabled] -= new_ids
63
+ sync_plan_hash[:enabled] = new_ids
64
+ else
65
+ sync_plan_hash[:disabled].concat(new_ids)
66
+ end
67
+ sync_plan_hash
68
+ end
69
+
70
+ def save_state(sync_plan_hash = {})
71
+ storage = ForemanMaintain.storage(:upgrade)
72
+ storage[:sync_plans] = sync_plan_hash
73
+ storage.save
74
+ end
75
+ end
@@ -1,7 +1,9 @@
1
1
  class Features::Upstream < ForemanMaintain::Feature
2
- label :upstream
2
+ metadata do
3
+ label :upstream
3
4
 
4
- confine do
5
- !downstream_installation?
5
+ confine do
6
+ !downstream_installation?
7
+ end
6
8
  end
7
9
  end
@@ -0,0 +1,33 @@
1
+ module Procedures::ForemanTasks
2
+ class Delete < ForemanMaintain::Procedure
3
+ metadata do
4
+ param :state, 'In what state should the task be deleted'
5
+ end
6
+
7
+ def run
8
+ with_spinner("Deleting #{@state} task") do |spinner|
9
+ count_tasks_before = feature(:foreman_tasks).count(@state)
10
+
11
+ if count_tasks_before > 0
12
+ spinner.update "Backup #{@state} tasks"
13
+
14
+ feature(:foreman_tasks).backup_tasks(@state) do |backup_progress|
15
+ spinner.update backup_progress
16
+ end
17
+
18
+ spinner.update "Deleting #{@state} tasks [running]"
19
+ count_tasks_later = feature(:foreman_tasks).delete(@state)
20
+ spinner.update "Deleting #{@state} tasks [DONE]"
21
+ end
22
+
23
+ spinner.update(
24
+ "Deleted #{@state} stopped and paused tasks: #{count_tasks_before - count_tasks_later}"
25
+ )
26
+ end
27
+ end
28
+
29
+ def description
30
+ "Delete #{@state} tasks"
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,14 @@
1
+ module Procedures::ForemanTasks
2
+ class Resume < ForemanMaintain::Procedure
3
+ include ForemanMaintain::Concerns::Hammer
4
+
5
+ metadata do
6
+ for_feature :foreman_tasks
7
+ description 'resume paused tasks'
8
+ end
9
+
10
+ def run
11
+ output << hammer('task resume')
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,19 @@
1
+ require 'cgi'
2
+ module Procedures::ForemanTasks
3
+ class UiInvestigate < ForemanMaintain::Procedure
4
+ metadata do
5
+ for_feature :foreman_tasks
6
+ description 'investigate the tasks via UI'
7
+ param :search_query
8
+ end
9
+
10
+ attr_reader :search_query
11
+
12
+ def run
13
+ ask(<<-MESSAGE.strip_heredoc)
14
+ Go to https://#{hostname}/foreman_tasks/tasks?search=#{CGI.escape(@search_query.to_s)}
15
+ press ENTER after the paused tasks are resolved.
16
+ MESSAGE
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,50 @@
1
+ class Procedures::HammerSetup < ForemanMaintain::Procedure
2
+ def run
3
+ setup_from_default || setup_from_answers
4
+ puts "New settings saved into #{hammer.config_file}"
5
+ hammer.run_command('architecture list') # if not setup properly, an error will be risen
6
+ end
7
+
8
+ def necessary?
9
+ !hammer.ready?
10
+ end
11
+
12
+ def description
13
+ 'Setup hammer'
14
+ end
15
+
16
+ private
17
+
18
+ def setup_from_default
19
+ used_default_file = hammer.setup_from_default
20
+ if used_default_file
21
+ puts "Using defaults from #{used_default_file}"
22
+ true
23
+ end
24
+ end
25
+
26
+ def setup_from_answers
27
+ loop do
28
+ username, password = ask_for_credentials
29
+ break if username.nil?
30
+ if hammer.setup_from_answers(username, password)
31
+ return true
32
+ else
33
+ puts 'Invalid credentials'
34
+ end
35
+ end
36
+ end
37
+
38
+ def ask_for_credentials
39
+ username = ask('Hammer username [admin]:')
40
+ return if username.nil?
41
+ username = 'admin' if username.empty?
42
+ password = ask('Hammer password:', :password => true)
43
+ return if password.nil?
44
+ [username.strip, password.strip]
45
+ end
46
+
47
+ def hammer
48
+ ForemanMaintain::Utils::Hammer.instance
49
+ end
50
+ end
@@ -0,0 +1,17 @@
1
+ class Procedures::InstallPackage < ForemanMaintain::Procedure
2
+ metadata do
3
+ param :packages, 'List of packages to install', :array => true
4
+ end
5
+
6
+ def run
7
+ install_packages(@packages, :assumeyes => assumeyes?)
8
+ end
9
+
10
+ def necessary?
11
+ @packages.any? { |package| package_version(package).nil? }
12
+ end
13
+
14
+ def description
15
+ "Install package(s) #{@packages.join(', ')}"
16
+ end
17
+ end
@@ -0,0 +1,22 @@
1
+ module Procedures::SyncPlans
2
+ class Disable < ForemanMaintain::Procedure
3
+ metadata do
4
+ for_feature :sync_plans
5
+ description 'disable active sync plans'
6
+ end
7
+
8
+ def run
9
+ disable_all_enabled_sync_plans
10
+ end
11
+
12
+ private
13
+
14
+ def disable_all_enabled_sync_plans
15
+ with_spinner('disabling sync plans') do |spinner|
16
+ ids = feature(:sync_plans).ids_by_status(true)
17
+ feature(:sync_plans).make_disable(ids)
18
+ spinner.update "Total #{ids.length} sync plans are now disabled."
19
+ end
20
+ end
21
+ end
22
+ end