foreman_maintain 0.1.3 → 0.1.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (37) hide show
  1. checksums.yaml +4 -4
  2. data/bin/passenger-recycler +23 -15
  3. data/config/foreman_maintain.yml.example +4 -0
  4. data/config/foreman_maintain.yml.packaging +2 -0
  5. data/definitions/checks/candlepin/validate_db.rb +9 -1
  6. data/definitions/checks/remote_execution/verify_settings_file_already_exists.rb +42 -0
  7. data/definitions/checks/repositories/validate.rb +33 -0
  8. data/definitions/features/candlepin_database.rb +19 -11
  9. data/definitions/features/downstream.rb +13 -0
  10. data/definitions/features/foreman_1_7_x.rb +3 -1
  11. data/definitions/features/foreman_proxy.rb +6 -5
  12. data/definitions/procedures/candlepin/delete_orphaned_records_from_env_content.rb +34 -0
  13. data/definitions/procedures/knowledge_base_article.rb +29 -0
  14. data/definitions/procedures/remote_execution/remove_existing_settingsd.rb +18 -0
  15. data/definitions/procedures/sync_plans/disable.rb +4 -2
  16. data/definitions/procedures/sync_plans/enable.rb +4 -2
  17. data/definitions/scenarios/upgrade_to_satellite_6_2.rb +1 -0
  18. data/definitions/scenarios/upgrade_to_satellite_6_2_z.rb +1 -0
  19. data/definitions/scenarios/upgrade_to_satellite_6_3.rb +2 -0
  20. data/definitions/scenarios/upgrade_to_satellite_6_3_z.rb +1 -0
  21. data/lib/foreman_maintain/cli/advanced/procedure/abstract_by_tag_command.rb +4 -2
  22. data/lib/foreman_maintain/cli/advanced/procedure/by_tag_command.rb +4 -2
  23. data/lib/foreman_maintain/cli/advanced/procedure/run_command.rb +1 -1
  24. data/lib/foreman_maintain/cli/base.rb +2 -2
  25. data/lib/foreman_maintain/concerns/base_database.rb +43 -0
  26. data/lib/foreman_maintain/concerns/metadata.rb +14 -1
  27. data/lib/foreman_maintain/concerns/system_helpers.rb +11 -0
  28. data/lib/foreman_maintain/config.rb +13 -5
  29. data/lib/foreman_maintain/context.rb +34 -0
  30. data/lib/foreman_maintain/detector.rb +1 -1
  31. data/lib/foreman_maintain/executable.rb +3 -1
  32. data/lib/foreman_maintain/runner/execution.rb +5 -1
  33. data/lib/foreman_maintain/scenario.rb +16 -3
  34. data/lib/foreman_maintain/upgrade_runner.rb +1 -1
  35. data/lib/foreman_maintain/version.rb +1 -1
  36. data/lib/foreman_maintain.rb +6 -0
  37. metadata +8 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 8e9f70b0ee10a6f3d86da947c29b44f28425fe3d
4
- data.tar.gz: 25c1ffb8b8b12ff26a2ee7cad07f407b7586f276
3
+ metadata.gz: 65b1daa7e3ed6b36c77a34061c0cde3badf9cc0d
4
+ data.tar.gz: '08aedcc2e9c4242dc17b8d50b5bf356bd3349d28'
5
5
  SHA512:
6
- metadata.gz: ab4457e9ea8319b82ac952b5d389bd2156d384076d2d36db0150b51a04127b62393617327118d715ebb468bcf104ceaefda96cb86d641a0da8724d4ae29cb632
7
- data.tar.gz: 4d59d274a7f28c85f4c315766e276890fb9c5285225fd02dcf5aef62e495b08343e8becfcdd7cbb39ddf916b6be38180692dba4ffc83ff9f83fddc9570c12e80
6
+ metadata.gz: 8a7548a663107ef22475acefbe0b0a5dc5aeff92e816c6eaf0096b2ce344c43536af033d9d67fbbc5c36f976c4d6f7e0dbb6e6d8f0343e69ad678feaa907cdc6
7
+ data.tar.gz: 6e0a423955cd009992767a126168609ac9ec0d1687465089bfa1861798402157f8225f335ad452fa64ba04a751468d5804cac000faafe9a450d07d75109db6bf
@@ -41,31 +41,39 @@ def process_status?(pid)
41
41
  end
42
42
 
43
43
  require 'phusion_passenger'
44
+ PhusionPassenger.locate_directories
44
45
  require 'phusion_passenger/platform_info'
45
46
  require 'phusion_passenger/platform_info/ruby'
46
47
  require 'phusion_passenger/admin_tools/memory_stats'
47
- PhusionPassenger.locate_directories
48
48
  stats = PhusionPassenger::AdminTools::MemoryStats.new
49
- unless stats.platform_provides_private_dirty_rss_information?
50
- puts 'Please run as root or platform unsupported'
51
- exit 1
52
- end
53
49
  killed = 0
50
+
51
+ def get_process_start(pid)
52
+ `ps -p#{pid} -o start=`.strip
53
+ rescue StandardError => e
54
+ verbose "Error: #{e.message} \nReturning '?'"
55
+ '?'
56
+ end
57
+
58
+ def get_rss_info(process)
59
+ get_pid_mem_kb = process.private_dirty_rss || process.rss
60
+ get_pid_mem_mb = format('%.0f', get_pid_mem_kb / 1024)
61
+ [get_pid_mem_kb, get_pid_mem_mb]
62
+ end
63
+
54
64
  stats.passenger_processes.each do |p|
55
65
  pid = p.pid.to_i
56
- debug "Checking #{pid} with RSS of #{p.private_dirty_rss}"
57
- next unless p.private_dirty_rss > CONFIG[:MAX_PRIV_RSS_MEMORY]
58
- started = begin
59
- `ps -p#{pid} -o start=`.strip
60
- rescue StandardError => e
61
- verbose "Error: #{e.message} \nReturning '?'"
62
- '?'
63
- end
66
+ started = get_process_start(pid)
67
+ get_pid_mem_kb, get_pid_mem_mb = get_rss_info(p)
68
+ debug_message = "Checking #{pid} with RSS of #{get_pid_mem_kb}"
69
+ verbose_message = "Terminating #{pid} (started #{started}) with private" \
70
+ " RSS size of #{get_pid_mem_mb} MB"
71
+ debug debug_message
72
+ next unless get_pid_mem_kb > CONFIG[:MAX_PRIV_RSS_MEMORY]
64
73
  status_ps = `ps -p#{pid} -u`
65
74
  status_all = `passenger-status 2> /dev/null`
66
75
  status_backtraces = `passenger-status --show=backtraces 2>/dev/null`
67
- verbose "Terminating #{pid} (started #{started}) with private dirty RSS" \
68
- " size of #{p.private_dirty_rss} MB"
76
+ verbose verbose_message
69
77
  Process.kill 'SIGUSR1', pid
70
78
  sleep CONFIG[:GRACEFUL_SHUTDOWN_SLEEP]
71
79
  kill(pid)
@@ -19,3 +19,7 @@
19
19
 
20
20
  # Mention path where foreman-proxy certificates stored on filesystem
21
21
  # :foreman_proxy_cert_path: '/etc/foreman'
22
+
23
+ # Mention directory path to store candlepin db backups
24
+ # :db_backup_dir: '/var/lib/foreman-maintain/db-backups'
25
+
@@ -20,3 +20,5 @@
20
20
  # Mention path where foreman-proxy certificates stored on filesystem
21
21
  # :foreman_proxy_cert_path: '/etc/foreman'
22
22
 
23
+ # Mention directory path to store candlepin db backups
24
+ # :db_backup_dir: '/var/lib/foreman-maintain/db-backups'
@@ -10,7 +10,15 @@ module Checks::Candlepin
10
10
  end
11
11
 
12
12
  def run
13
- feature(:candlepin_database).execute_cpdb_validate_cmd
13
+ result, result_msg = feature(:candlepin_database).execute_cpdb_validate_cmd
14
+ next_steps = []
15
+ if feature(:downstream) && feature(:downstream).current_minor_version == '6.2'
16
+ next_steps.concat(
17
+ [Procedures::Candlepin::DeleteOrphanedRecordsFromEnvContent.new,
18
+ Procedures::KnowledgeBaseArticle.new(:doc => 'fix_cpdb_validate_failure')]
19
+ )
20
+ end
21
+ assert(result == 0, result_msg, :next_steps => next_steps)
14
22
  end
15
23
  end
16
24
  end
@@ -0,0 +1,42 @@
1
+ module Checks::RemoteExecution
2
+ class VerifySettingsFileAlreadyExists < ForemanMaintain::Check
3
+ metadata do
4
+ description 'Check to verify remote_execution_ssh settings already exist'
5
+
6
+ confine do
7
+ feature(:downstream) &&
8
+ feature(:downstream).current_minor_version == '6.2' &&
9
+ find_package('tfm-rubygem-smart_proxy_dynflow_core') &&
10
+ file_exists?('/etc/smart_proxy_dynflow_core')
11
+ end
12
+ end
13
+
14
+ def run
15
+ if file_exists?(settingd_dir_path)
16
+ symlinks = find_symlinks(settingd_dir_path)
17
+ assert(
18
+ symlinks.empty?,
19
+ failure_msg(settingd_dir_path, symlinks),
20
+ :next_steps => [
21
+ Procedures::RemoteExecution::RemoveExistingSettingsd.new(
22
+ :dirpath => settingd_dir_path
23
+ )
24
+ ]
25
+ )
26
+ end
27
+ end
28
+
29
+ private
30
+
31
+ def failure_msg(dir_path, symlinks)
32
+ 'Settings related to remote_execution_ssh are already present'\
33
+ " under #{dir_path} and " \
34
+ "\nit would conflict with the installer from the next version." \
35
+ "\nsymlinks available - #{symlinks.join(', ')}"
36
+ end
37
+
38
+ def settingd_dir_path
39
+ '/etc/smart_proxy_dynflow_core/settings.d'
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,33 @@
1
+ module Checks::Repositories
2
+ class Validate < ForemanMaintain::Check
3
+ metadata do
4
+ description 'Validate availability of repositories'
5
+
6
+ confine do
7
+ feature(:downstream)
8
+ end
9
+
10
+ param :version,
11
+ 'Version for which repositories needs to be validated',
12
+ :required => true
13
+
14
+ manual_detection
15
+ end
16
+
17
+ def run
18
+ with_spinner("Validating availability of repositories for #{@version}") do |spinner|
19
+ absent_repos = feature(:downstream).absent_repos(@version)
20
+ unless absent_repos.empty?
21
+ spinner.update('Some repositories missing, calling `subscription-manager refresh`')
22
+ feature(:downstream).rhsm_refresh
23
+ absent_repos = feature(:downstream).absent_repos(@version)
24
+ end
25
+ unless absent_repos.empty?
26
+ fail!(
27
+ "Following repositories are not available on your system: #{absent_repos.join(', ')}"
28
+ )
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
@@ -27,12 +27,20 @@ class Features::CandlepinDatabase < ForemanMaintain::Feature
27
27
 
28
28
  def execute_cpdb_validate_cmd
29
29
  main_cmd = cpdb_validate_cmd
30
- unless main_cmd.empty?
31
- main_cmd += format_shell_args(
32
- '-u' => configuration['username'], '-p' => configuration[%(password)]
33
- )
34
- execute!(main_cmd, :hidden_patterns => [configuration['password']])
35
- end
30
+ return [true, nil] if main_cmd.empty?
31
+ main_cmd += format_shell_args(
32
+ '-u' => configuration['username'], '-p' => configuration[%(password)]
33
+ )
34
+ execute_with_status(main_cmd, :hidden_patterns => [configuration['password']])
35
+ end
36
+
37
+ def env_content_ids_with_null_content
38
+ sql = <<-SQL
39
+ SELECT ec.id
40
+ FROM cp_env_content ec
41
+ LEFT JOIN cp_content c ON ec.contentid = c.id WHERE c.id IS NULL
42
+ SQL
43
+ query(sql).map { |r| r['id'] }
36
44
  end
37
45
 
38
46
  private
@@ -40,15 +48,15 @@ class Features::CandlepinDatabase < ForemanMaintain::Feature
40
48
  def load_configuration
41
49
  raw_config = File.read(CANDLEPIN_DB_CONFIG)
42
50
  full_config = Hash[raw_config.scan(/(^[^#\n][^=]*)=(.*)/)]
43
- uri = %r{://(([^/:]*):?([^/]*))/(.*)}.match(full_config['org.quartz.dataSource.myDS.URL'])
51
+ uri = %r{://(([^/:]*):?([^/]*))/(.*)}.match(full_config['jpa.config.hibernate.connection.url'])
44
52
  @configuration = {
45
- 'username' => full_config['org.quartz.dataSource.myDS.user'],
46
- 'password' => full_config['org.quartz.dataSource.myDS.password'],
53
+ 'username' => full_config['jpa.config.hibernate.connection.username'],
54
+ 'password' => full_config['jpa.config.hibernate.connection.password'],
47
55
  'database' => uri[4],
48
56
  'host' => uri[2],
49
57
  'port' => uri[3] || '5432',
50
- 'driver' => full_config['org.quartz.dataSource.myDS.driver'],
51
- 'url' => full_config['org.quartz.dataSource.myDS.URL']
58
+ 'driver_class' => full_config['jpa.config.hibernate.connection.driver_class'],
59
+ 'url' => full_config['jpa.config.hibernate.connection.url']
52
60
  }
53
61
  end
54
62
 
@@ -29,6 +29,19 @@ class Features::Downstream < ForemanMaintain::Feature
29
29
  end
30
30
  end
31
31
 
32
+ def absent_repos(version)
33
+ all_repo_lines = execute(%(LANG=en_US.utf-8 subscription-manager repos --list | ) +
34
+ %(grep '^Repo ID:')).split("\n")
35
+ all_repos = all_repo_lines.map { |line| line.split(/\s+/).last }
36
+ repos_required = rh_repos(version)
37
+ repos_found = repos_required & all_repos
38
+ repos_required - repos_found
39
+ end
40
+
41
+ def rhsm_refresh
42
+ execute!(%(subscription-manager refresh))
43
+ end
44
+
32
45
  private
33
46
 
34
47
  def rh_repos(sat_version)
@@ -35,7 +35,9 @@ class Features::Foreman_1_7_x < ForemanMaintain::Feature
35
35
 
36
36
  def del_custom_iptables_chain(name)
37
37
  return unless execute?("iptables -L #{name}") # the chain is already gone
38
- execute!("iptables -D INPUT -j #{name}")
38
+ if execute?("iptables -L INPUT | tail -n +3 | grep '^#{name} '")
39
+ execute!("iptables -D INPUT -j #{name}")
40
+ end
39
41
  execute!("iptables -F #{name}")
40
42
  execute!("iptables -X #{name}")
41
43
  end
@@ -34,16 +34,17 @@ class Features::ForemanProxy < ForemanMaintain::Feature
34
34
  next unless str.include?('HTTP')
35
35
  http_line = str
36
36
  end
37
+ return http_line if http_line.empty?
37
38
  http_line.split(curl_http_status.to_s).last.strip
38
39
  end
39
40
  end
40
41
 
41
42
  def run_dhcp_curl
42
43
  curl_resp = execute(dhcp_curl_cmd)
43
- array_output = curl_resp.split(/\r\n/)
44
- result_array = array_output.last.split(/\n/)
45
- curl_http_status = result_array.delete_at(result_array.length - 1).strip.to_i
46
- curl_http_resp = parse_json(result_array.join(''))
44
+ array_output = curl_resp.split(/\r?\n/)
45
+ status_str = array_output.last
46
+ curl_http_status = (status_str ? status_str.strip : status_str).to_i
47
+ curl_http_resp = parse_json(array_output[0])
47
48
  ForemanMaintain::Utils::CurlResponse.new(
48
49
  curl_http_resp,
49
50
  curl_http_status,
@@ -55,7 +56,7 @@ class Features::ForemanProxy < ForemanMaintain::Feature
55
56
  dhcp_curl_resp = run_dhcp_curl
56
57
  success = true
57
58
  if dhcp_curl_resp.http_code.eql?(200)
58
- if dhcp_curl_resp.result.empty?
59
+ if dhcp_curl_resp.result.to_s.empty?
59
60
  success = false
60
61
  puts "Verify DHCP Settings. Response: #{dhcp_curl_resp.result.inspect}"
61
62
  end
@@ -0,0 +1,34 @@
1
+ module Procedures::Candlepin
2
+ class DeleteOrphanedRecordsFromEnvContent < ForemanMaintain::Procedure
3
+ metadata do
4
+ description 'Delete orphaned record(s) from cp_env_content with unresolvable content'
5
+ label :candlepin_delete_orphaned_records_from_env_content
6
+
7
+ confine do
8
+ feature(:candlepin_database) &&
9
+ feature(:downstream) &&
10
+ feature(:downstream).current_minor_version == '6.2' &&
11
+ feature(:candlepin_database).table_exist?('cp_env_content')
12
+ end
13
+ end
14
+
15
+ def run
16
+ with_spinner('Deleting cp_env_content record(s) with unresolvable content') do |spinner|
17
+ spinner.update 'Finding cp_env_content records with unresolvable content'
18
+ env_content_ids = feature(:candlepin_database).env_content_ids_with_null_content
19
+ if env_content_ids.empty?
20
+ spinner.update 'No orphaned records found'
21
+ else
22
+ spinner.update 'Taking a backup of the candlepin database'
23
+ feature(:candlepin_database).perform_backup
24
+
25
+ puts "Total #{env_content_ids.length} records with unresolvable content"
26
+ spinner.update 'Deleting record(s) from cp_env_content table'
27
+ feature(:candlepin_database).delete_records_by_ids(
28
+ 'cp_env_content', env_content_ids
29
+ )
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,29 @@
1
+ class Procedures::KnowledgeBaseArticle < ForemanMaintain::Procedure
2
+ metadata do
3
+ description 'Show knowledge base article for troubleshooting'
4
+
5
+ confine do
6
+ feature(:downstream)
7
+ end
8
+ param :doc,
9
+ 'Document name required to select a correct article',
10
+ :required => true
11
+ advanced_run false
12
+ end
13
+
14
+ def run
15
+ ask(<<-MESSAGE.strip_heredoc)
16
+ Go to #{kcs_documents[@doc]}
17
+ please follow steps from above article to resolve this issue
18
+ press ENTER once done.
19
+ MESSAGE
20
+ end
21
+
22
+ private
23
+
24
+ def kcs_documents
25
+ {
26
+ 'fix_cpdb_validate_failure' => 'https://access.redhat.com/solutions/3362821'
27
+ }
28
+ end
29
+ end
@@ -0,0 +1,18 @@
1
+ module Procedures::RemoteExecution
2
+ class RemoveExistingSettingsd < ForemanMaintain::Procedure
3
+ metadata do
4
+ param :dirpath,
5
+ 'Directory path for settings.d folder',
6
+ :required => true
7
+ description 'Remove existing settings.d directory before installer run.' \
8
+ "\n The next run of the installer will re-create the directory properly."
9
+ advanced_run false
10
+ end
11
+
12
+ def run
13
+ with_spinner("Removing existing #{@dirpath} directory") do |_spinner|
14
+ execute!("rm -rf #{@dirpath}")
15
+ end
16
+ end
17
+ end
18
+ end
@@ -17,14 +17,16 @@ module Procedures::SyncPlans
17
17
  private
18
18
 
19
19
  def disable_all_enabled_sync_plans
20
- feature(:sync_plans).load_from_storage(storage)
20
+ default_storage = ForemanMaintain.storage(:default)
21
+ feature(:sync_plans).load_from_storage(default_storage)
21
22
  with_spinner('disabling sync plans') do |spinner|
22
23
  ids = feature(:sync_plans).ids_by_status(true)
23
24
  feature(:sync_plans).make_disable(ids)
24
25
  spinner.update "Total #{ids.length} sync plans are now disabled."
25
26
  end
26
27
  ensure
27
- feature(:sync_plans).save_to_storage(storage)
28
+ feature(:sync_plans).save_to_storage(default_storage)
29
+ default_storage.save
28
30
  end
29
31
  end
30
32
  end
@@ -18,13 +18,15 @@ module Procedures::SyncPlans
18
18
  private
19
19
 
20
20
  def enabled_sync_plans
21
- feature(:sync_plans).load_from_storage(storage)
21
+ default_storage = ForemanMaintain.storage(:default)
22
+ feature(:sync_plans).load_from_storage(default_storage)
22
23
  with_spinner('re-enabling sync plans') do |spinner|
23
24
  record_ids = feature(:sync_plans).make_enable
24
25
  spinner.update "Total #{record_ids.length} sync plans are now enabled."
25
26
  end
26
27
  ensure
27
- feature(:sync_plans).save_to_storage(storage)
28
+ feature(:sync_plans).save_to_storage(default_storage)
29
+ default_storage.save
28
30
  end
29
31
  end
30
32
  end
@@ -21,6 +21,7 @@ module Scenarios::Satellite_6_2
21
21
  def compose
22
22
  add_steps(find_checks(:default))
23
23
  add_steps(find_checks(:pre_upgrade))
24
+ add_step(Checks::Repositories::Validate.new(:version => '6.2'))
24
25
  end
25
26
  end
26
27
 
@@ -21,6 +21,7 @@ module Scenarios::Satellite_6_2_z
21
21
  def compose
22
22
  add_steps(find_checks(:default))
23
23
  add_steps(find_checks(:pre_upgrade))
24
+ add_step(Checks::Repositories::Validate.new(:version => '6.2'))
24
25
  end
25
26
  end
26
27
 
@@ -21,6 +21,8 @@ module Scenarios::Satellite_6_3
21
21
  def compose
22
22
  add_steps(find_checks(:default))
23
23
  add_steps(find_checks(:pre_upgrade))
24
+ add_step(Checks::RemoteExecution::VerifySettingsFileAlreadyExists.new)
25
+ add_step(Checks::Repositories::Validate.new(:version => '6.3'))
24
26
  end
25
27
  end
26
28
 
@@ -21,6 +21,7 @@ module Scenarios::Satellite_6_3_z
21
21
  def compose
22
22
  add_steps(find_checks(:default))
23
23
  add_steps(find_checks(:pre_upgrade))
24
+ add_step(Checks::Repositories::Validate.new(:version => '6.3'))
24
25
  end
25
26
  end
26
27
 
@@ -13,7 +13,7 @@ module ForemanMaintain
13
13
 
14
14
  def self.params_for_tag(tag)
15
15
  params = {}
16
- ForemanMaintain.available_procedures(:tags => tag).each do |procedure|
16
+ ForemanMaintain.allowed_available_procedures(:tags => tag).each do |procedure|
17
17
  procedure.params.each_value do |param|
18
18
  unless params.key?(param.name)
19
19
  params[param.name] = { :instance => param, :procedures => [] }
@@ -35,7 +35,9 @@ module ForemanMaintain
35
35
  tag = underscorize(invocation_path.split.last).to_sym
36
36
  scenario = ForemanMaintain::Scenario.new
37
37
 
38
- ForemanMaintain.available_procedures(:tags => tag).sort_by(&:label).each do |procedure|
38
+ ForemanMaintain.allowed_available_procedures(
39
+ :tags => tag
40
+ ).sort_by(&:label).each do |procedure|
39
41
  scenario.add_step(procedure.new(get_params_for(procedure)))
40
42
  end
41
43
 
@@ -4,12 +4,14 @@ module ForemanMaintain
4
4
  module Cli
5
5
  module Procedure
6
6
  class ByTagCommand < Base
7
- available_tags(ForemanMaintain.available_procedures(nil)).each do |tag|
7
+ available_tags(ForemanMaintain.allowed_available_procedures(nil)).each do |tag|
8
8
  klass = Class.new(AbstractByTagCommand) do
9
9
  tag_params_to_options(tag)
10
10
  interactive_option
11
11
  end
12
- procedures = ForemanMaintain.available_procedures(:tags => tag).map do |procedure|
12
+ procedures = ForemanMaintain.allowed_available_procedures(
13
+ :tags => tag
14
+ ).map do |procedure|
13
15
  procedure.label.to_s
14
16
  end
15
17
 
@@ -4,7 +4,7 @@ module ForemanMaintain
4
4
  module Cli
5
5
  module Procedure
6
6
  class RunCommand < Base
7
- ForemanMaintain.available_procedures(nil).each do |procedure|
7
+ ForemanMaintain.allowed_available_procedures(nil).each do |procedure|
8
8
  klass = Class.new(AbstractProcedureCommand) do
9
9
  params_to_options(procedure.params)
10
10
  interactive_option
@@ -103,8 +103,8 @@ module ForemanMaintain
103
103
  option ['-y', '--assumeyes'], :flag,
104
104
  'Automatically answer yes for all questions'
105
105
 
106
- option ['-w', '--whitelist'], 'whitelist',
107
- 'Comma-separated list of labels of steps to be ignored' do |whitelist|
106
+ option(['-w', '--whitelist'], 'whitelist',
107
+ 'Comma-separated list of labels of steps to be skipped') do |whitelist|
108
108
  raise ArgumentError, 'value not specified' if whitelist.nil? || whitelist.empty?
109
109
  whitelist.split(',').map(&:strip)
110
110
  end
@@ -22,6 +22,49 @@ module ForemanMaintain
22
22
  psql('SELECT 1 as ping', config)
23
23
  end
24
24
 
25
+ def backup_file_path(config = configuration)
26
+ dump_file_name = "#{config['database']}_#{Time.now.strftime('%Y-%m-%d_%H-%M-%S')}.dump"
27
+ "#{backup_dir}/#{dump_file_name}.bz2"
28
+ end
29
+
30
+ def backup_db_command(file_path, config = configuration)
31
+ pg_dump_cmd = "pg_dump -Fc #{config['database']}"
32
+ "runuser - postgres -c '#{pg_dump_cmd}' | bzip2 -9 > #{file_path}"
33
+ end
34
+
35
+ def backup_dir
36
+ @backup_dir ||= File.expand_path(ForemanMaintain.config.db_backup_dir)
37
+ end
38
+
39
+ def perform_backup(config = configuration)
40
+ file_path = backup_file_path(config)
41
+ backup_cmd = backup_db_command(file_path, config)
42
+ execute!(backup_cmd)
43
+ puts "\n Note: Database backup file path - #{file_path}"
44
+ puts "\n In case of any exception, use above dump file to restore DB."
45
+ end
46
+
47
+ def table_exist?(table_name)
48
+ sql = <<-SQL
49
+ SELECT EXISTS ( SELECT *
50
+ FROM information_schema.tables WHERE table_name = '#{table_name}' )
51
+ SQL
52
+ result = query(sql)
53
+ return false if result.nil? || (result && result.empty?)
54
+ result.first['exists'].eql?('t')
55
+ end
56
+
57
+ def delete_records_by_ids(tbl_name, rec_ids)
58
+ quotize_rec_ids = rec_ids.map { |el| "'#{el}'" }.join(',')
59
+ unless quotize_rec_ids.empty?
60
+ psql(<<-SQL)
61
+ BEGIN;
62
+ DELETE FROM #{tbl_name} WHERE id IN (#{quotize_rec_ids});
63
+ COMMIT;
64
+ SQL
65
+ end
66
+ end
67
+
25
68
  private
26
69
 
27
70
  def psql_db_connection_str(config)
@@ -96,6 +96,10 @@ module ForemanMaintain
96
96
  @data[:run_once] = true
97
97
  end
98
98
 
99
+ def advanced_run(advanced_run)
100
+ @data[:advanced_run] = advanced_run
101
+ end
102
+
99
103
  def self.eval_dsl(metadata, &block)
100
104
  new(metadata).tap do |dsl|
101
105
  dsl.instance_eval(&block)
@@ -174,13 +178,18 @@ module ForemanMaintain
174
178
  metadata[:run_once]
175
179
  end
176
180
 
181
+ def advanced_run?
182
+ metadata[:advanced_run]
183
+ end
184
+
177
185
  def initialize_metadata
178
186
  { :tags => [],
179
187
  :confine_blocks => [],
180
188
  :params => {},
181
189
  :preparation_steps_blocks => [],
182
190
  :before => [],
183
- :after => [] }.tap do |metadata|
191
+ :after => [],
192
+ :advanced_run => true }.tap do |metadata|
184
193
  if superclass.respond_to?(:metadata)
185
194
  metadata[:label] = superclass.metadata[:label]
186
195
  end
@@ -271,6 +280,10 @@ module ForemanMaintain
271
280
  def preparation_steps(*args)
272
281
  self.class.preparation_steps(*args)
273
282
  end
283
+
284
+ def advanced_run?
285
+ self.class.advanced_run?
286
+ end
274
287
  end
275
288
  end
276
289
  end
@@ -61,6 +61,11 @@ module ForemanMaintain
61
61
  command_runner.output
62
62
  end
63
63
 
64
+ def execute_with_status(command, options = {})
65
+ result_msg = execute(command, options)
66
+ [$CHILD_STATUS.to_i, result_msg]
67
+ end
68
+
64
69
  def file_exists?(filename)
65
70
  File.exist?(filename)
66
71
  end
@@ -142,6 +147,12 @@ module ForemanMaintain
142
147
  def format_shell_args(options = {})
143
148
  options.map { |shell_optn, val| " #{shell_optn} '#{shellescape(val)}'" }.join
144
149
  end
150
+
151
+ def find_symlinks(dir_path)
152
+ cmd = "find '#{dir_path}' -maxdepth 1 -type l"
153
+ result = execute(cmd).strip
154
+ result.split(/\n/)
155
+ end
145
156
  end
146
157
  end
147
158
  end
@@ -3,7 +3,8 @@ module ForemanMaintain
3
3
  class Config
4
4
  attr_accessor :pre_setup_log_messages,
5
5
  :config_file, :definitions_dirs, :log_level, :log_dir, :log_file_size,
6
- :storage_file, :backup_dir, :foreman_proxy_cert_path
6
+ :storage_file, :backup_dir, :foreman_proxy_cert_path,
7
+ :db_backup_dir
7
8
 
8
9
  def initialize(options)
9
10
  @pre_setup_log_messages = []
@@ -12,10 +13,7 @@ module ForemanMaintain
12
13
  @definitions_dirs = @options.fetch(:definitions_dirs,
13
14
  [File.join(source_path, 'definitions')])
14
15
  load_log_configs
15
- @storage_file = @options.fetch(:storage_file, 'data.yml')
16
- @backup_dir = find_dir_path(
17
- @options.fetch(:backup_dir, '/var/lib/foreman-maintain')
18
- )
16
+ load_backup_dir_paths
19
17
  @foreman_proxy_cert_path = @options.fetch(:foreman_proxy_cert_path, '/etc/foreman')
20
18
  end
21
19
 
@@ -27,6 +25,16 @@ module ForemanMaintain
27
25
  @log_file_size = @options.fetch(:log_file_size, 10_000)
28
26
  end
29
27
 
28
+ def load_backup_dir_paths
29
+ @storage_file = @options.fetch(:storage_file, 'data.yml')
30
+ @backup_dir = find_dir_path(
31
+ @options.fetch(:backup_dir, '/var/lib/foreman-maintain')
32
+ )
33
+ @db_backup_dir = find_dir_path(
34
+ @options.fetch(:db_backup_dir, '/var/lib/foreman-maintain/db-backups')
35
+ )
36
+ end
37
+
30
38
  def load_config
31
39
  if File.exist?(config_file)
32
40
  YAML.load(File.open(config_file)) || {}
@@ -0,0 +1,34 @@
1
+ module ForemanMaintain
2
+ class Context
3
+ def initialize(data = {})
4
+ @data = data
5
+ @mapping = {}
6
+ end
7
+
8
+ def set(key, value, mapping = {})
9
+ @data[key] = value
10
+ map(key, mapping)
11
+ end
12
+
13
+ def get(key, default = nil)
14
+ @data.fetch(key, default)
15
+ end
16
+
17
+ def to_hash
18
+ @data
19
+ end
20
+
21
+ def map(key, mapping = {})
22
+ @mapping[key] ||= {}
23
+ @mapping[key].merge!(mapping)
24
+ end
25
+
26
+ def params_for(definition)
27
+ @mapping.inject({}) do |params, (key, mapping)|
28
+ target = mapping[definition]
29
+ params[target] = @data[key] unless target.nil?
30
+ params
31
+ end
32
+ end
33
+ end
34
+ end
@@ -50,7 +50,7 @@ module ForemanMaintain
50
50
 
51
51
  def find_present_classes(object_base_class)
52
52
  object_base_class.all_sub_classes.reduce([]) do |array, object_class|
53
- array << object_class if object_class.present?
53
+ array << object_class if object_class.autodetect? && object_class.present?
54
54
  array
55
55
  end
56
56
  end
@@ -116,7 +116,9 @@ module ForemanMaintain
116
116
  # internal method called by executor
117
117
  def __run__(execution)
118
118
  setup_execution_state(execution)
119
- run
119
+ unless skipped?
120
+ run
121
+ end
120
122
  end
121
123
 
122
124
  # method defined both on object and class to ensure we work always with object
@@ -66,16 +66,20 @@ module ForemanMaintain
66
66
 
67
67
  def run
68
68
  @reporter.before_execution_starts(self)
69
+
69
70
  if skip?
70
71
  @status = :already_run
71
72
  return
72
73
  end
73
- @status = :running
74
+
75
+ @status = whitelisted? ? :skipped : :running
76
+
74
77
  with_metadata_calculation do
75
78
  capture_errors do
76
79
  step.__run__(self)
77
80
  end
78
81
  end
82
+
79
83
  # change the state only when not modified
80
84
  @status = :success if @status == :running
81
85
  ensure
@@ -6,7 +6,7 @@ module ForemanMaintain
6
6
  include Concerns::Finders
7
7
  extend Concerns::Finders
8
8
 
9
- attr_reader :steps
9
+ attr_reader :steps, :context
10
10
 
11
11
  class FilteredScenario < Scenario
12
12
  metadata do
@@ -79,14 +79,19 @@ module ForemanMaintain
79
79
  end
80
80
  end
81
81
 
82
- def initialize
82
+ def initialize(context_data = {})
83
83
  @steps = []
84
+ @context = Context.new(context_data)
85
+ set_context_mapping
84
86
  compose
85
87
  end
86
88
 
87
89
  # Override to compose steps for the scenario
88
90
  def compose; end
89
91
 
92
+ # Override to map context for the scenario
93
+ def set_context_mapping; end
94
+
90
95
  def preparation_steps
91
96
  # we first take the preparation steps defined for the scenario + collect
92
97
  # preparation steps for the steps inside the scenario
@@ -141,7 +146,15 @@ module ForemanMaintain
141
146
  end
142
147
 
143
148
  def add_step(step)
144
- add_steps([step])
149
+ add_steps([step]) unless step.nil?
150
+ end
151
+
152
+ def add_step_with_context(definition)
153
+ add_step(definition.send(:new, context.params_for(definition))) if definition.present?
154
+ end
155
+
156
+ def add_steps_with_context(*definitions)
157
+ definitions.flatten.each { |definition| add_step_with_context(definition) }
145
158
  end
146
159
 
147
160
  def self.inspect
@@ -175,7 +175,7 @@ module ForemanMaintain
175
175
  The upgrade failed and system was restored to pre-upgrade state.
176
176
  MESSAGE
177
177
  end
178
- # rubocop:enable Metrics/AbcSize, Lint/NonLocalExitFromIterator
178
+ # rubocop:enable Metrics/AbcSize
179
179
 
180
180
  def with_non_empty_scenario(phase)
181
181
  next_scenario = scenario(phase)
@@ -1,3 +1,3 @@
1
1
  module ForemanMaintain
2
- VERSION = '0.1.3'.freeze
2
+ VERSION = '0.1.4'.freeze
3
3
  end
@@ -20,6 +20,7 @@ module ForemanMaintain
20
20
  require 'foreman_maintain/top_level_modules'
21
21
  require 'foreman_maintain/yaml_storage'
22
22
  require 'foreman_maintain/config'
23
+ require 'foreman_maintain/context'
23
24
  require 'foreman_maintain/detector'
24
25
  require 'foreman_maintain/dependency_graph'
25
26
  require 'foreman_maintain/param'
@@ -91,6 +92,11 @@ module ForemanMaintain
91
92
  detector.available_procedures(*args)
92
93
  end
93
94
 
95
+ def allowed_available_procedures(*args)
96
+ procedures = detector.available_procedures(*args)
97
+ procedures.select(&:advanced_run?)
98
+ end
99
+
94
100
  def init_logger
95
101
  # Note - If timestamp added to filename then number of log files i.e second
96
102
  # argument to Logger.new will not work as expected
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: foreman_maintain
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.3
4
+ version: 0.1.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ivan Nečas
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2018-01-31 00:00:00.000000000 Z
11
+ date: 2018-03-20 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: clamp
@@ -120,6 +120,8 @@ files:
120
120
  - definitions/checks/foreman_tasks/not_paused.rb
121
121
  - definitions/checks/foreman_tasks/not_running.rb
122
122
  - definitions/checks/hammer_ping.rb
123
+ - definitions/checks/remote_execution/verify_settings_file_already_exists.rb
124
+ - definitions/checks/repositories/validate.rb
123
125
  - definitions/checks/system_registration.rb
124
126
  - definitions/features/candlepin_database.rb
125
127
  - definitions/features/downstream.rb
@@ -135,6 +137,7 @@ files:
135
137
  - definitions/features/pulp.rb
136
138
  - definitions/features/sync_plans.rb
137
139
  - definitions/features/upstream.rb
140
+ - definitions/procedures/candlepin/delete_orphaned_records_from_env_content.rb
138
141
  - definitions/procedures/foreman_tasks/delete.rb
139
142
  - definitions/procedures/foreman_tasks/fetch_tasks_status.rb
140
143
  - definitions/procedures/foreman_tasks/resume.rb
@@ -144,11 +147,13 @@ files:
144
147
  - definitions/procedures/katello_service/restart.rb
145
148
  - definitions/procedures/katello_service/start.rb
146
149
  - definitions/procedures/katello_service/stop.rb
150
+ - definitions/procedures/knowledge_base_article.rb
147
151
  - definitions/procedures/maintenance_mode/disable.rb
148
152
  - definitions/procedures/maintenance_mode/enable.rb
149
153
  - definitions/procedures/packages/install.rb
150
154
  - definitions/procedures/packages/update.rb
151
155
  - definitions/procedures/passenger_recycler.rb
156
+ - definitions/procedures/remote_execution/remove_existing_settingsd.rb
152
157
  - definitions/procedures/repositories/setup.rb
153
158
  - definitions/procedures/sync_plans/disable.rb
154
159
  - definitions/procedures/sync_plans/enable.rb
@@ -177,6 +182,7 @@ files:
177
182
  - lib/foreman_maintain/concerns/scenario_metadata.rb
178
183
  - lib/foreman_maintain/concerns/system_helpers.rb
179
184
  - lib/foreman_maintain/config.rb
185
+ - lib/foreman_maintain/context.rb
180
186
  - lib/foreman_maintain/core_ext.rb
181
187
  - lib/foreman_maintain/csv_parser.rb
182
188
  - lib/foreman_maintain/dependency_graph.rb