foreman_maintain 0.2.1 → 0.2.2

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 (41) hide show
  1. checksums.yaml +4 -4
  2. data/bin/foreman-maintain +2 -1
  3. data/definitions/checks/backup/directory_ready.rb +1 -1
  4. data/definitions/checks/restore/validate_backup.rb +77 -0
  5. data/definitions/checks/restore/validate_hostname.rb +20 -0
  6. data/definitions/features/foreman_proxy.rb +6 -1
  7. data/definitions/features/foreman_tasks.rb +5 -1
  8. data/definitions/features/instance.rb +8 -0
  9. data/definitions/features/katello.rb +0 -1
  10. data/definitions/features/mongo.rb +51 -12
  11. data/definitions/features/pulp.rb +2 -1
  12. data/definitions/features/service.rb +36 -0
  13. data/definitions/features/tar.rb +16 -4
  14. data/definitions/procedures/backup/online/pg_global_objects.rb +3 -1
  15. data/definitions/procedures/backup/prepare_directory.rb +1 -1
  16. data/definitions/procedures/backup/pulp.rb +2 -1
  17. data/definitions/procedures/pulp/migrate.rb +21 -0
  18. data/definitions/procedures/restore/candlepin_dump.rb +30 -0
  19. data/definitions/procedures/restore/configs.rb +40 -0
  20. data/definitions/procedures/restore/confirmation.rb +17 -0
  21. data/definitions/procedures/restore/drop_databases.rb +39 -0
  22. data/definitions/procedures/restore/extract_files.rb +70 -0
  23. data/definitions/procedures/restore/foreman_dump.rb +30 -0
  24. data/definitions/procedures/restore/installer_reset.rb +39 -0
  25. data/definitions/procedures/restore/mongo_dump.rb +41 -0
  26. data/definitions/procedures/restore/pg_global_objects.rb +36 -0
  27. data/definitions/procedures/restore/postgres_owner.rb +18 -0
  28. data/definitions/procedures/selinux/set_file_security.rb +22 -0
  29. data/definitions/procedures/service/daemon_reload.rb +18 -0
  30. data/definitions/procedures/service/restart.rb +2 -2
  31. data/definitions/scenarios/restore.rb +91 -0
  32. data/lib/foreman_maintain/cli.rb +3 -1
  33. data/lib/foreman_maintain/cli/restore_command.rb +26 -0
  34. data/lib/foreman_maintain/concerns/base_database.rb +35 -1
  35. data/lib/foreman_maintain/concerns/system_helpers.rb +1 -5
  36. data/lib/foreman_maintain/reporter/cli_reporter.rb +2 -1
  37. data/lib/foreman_maintain/utils/backup.rb +225 -0
  38. data/lib/foreman_maintain/utils/command_runner.rb +4 -2
  39. data/lib/foreman_maintain/utils/mongo_core.rb +49 -0
  40. data/lib/foreman_maintain/version.rb +1 -1
  41. metadata +20 -2
@@ -0,0 +1,30 @@
1
+ module Procedures::Restore
2
+ class CandlepinDump < ForemanMaintain::Procedure
3
+ metadata do
4
+ description 'Restore candlepin postgresql dump from backup'
5
+ param :backup_dir,
6
+ 'Path to backup directory',
7
+ :required => true
8
+ preparation_steps { Checks::Candlepin::DBUp.new }
9
+ confine do
10
+ feature(:candlepin_database)
11
+ end
12
+ end
13
+
14
+ def run
15
+ backup = ForemanMaintain::Utils::Backup.new(@backup_dir)
16
+
17
+ with_spinner('Restoring candlepin postgresql dump') do |spinner|
18
+ restore_candlepin_dump(backup, spinner)
19
+ end
20
+ end
21
+
22
+ def restore_candlepin_dump(backup, spinner)
23
+ if backup.file_map[:candlepin_dump][:present]
24
+ spinner.update('Restoring candlepin dump')
25
+ local = feature(:candlepin_database).local?
26
+ feature(:candlepin_database).restore_dump(backup.file_map[:candlepin_dump][:path], local)
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,40 @@
1
+ module Procedures::Restore
2
+ class Configs < ForemanMaintain::Procedure
3
+ metadata do
4
+ description 'Restore configs from backup'
5
+
6
+ param :backup_dir,
7
+ 'Path to backup directory',
8
+ :required => true
9
+ end
10
+
11
+ def run
12
+ backup = ForemanMaintain::Utils::Backup.new(@backup_dir)
13
+ with_spinner('Resetting') do |spinner|
14
+ spinner.update('Restoring configs')
15
+ clean_conflicting_data
16
+ restore_configs(backup)
17
+ end
18
+ end
19
+
20
+ def restore_configs(backup)
21
+ tar_options = {
22
+ :overwrite => true,
23
+ :listed_incremental => '/dev/null',
24
+ :command => 'extract',
25
+ :directory => '/',
26
+ :archive => backup.file_map[:config_files][:path],
27
+ :gzip => true
28
+ }
29
+
30
+ feature(:tar).run(tar_options)
31
+ end
32
+
33
+ private
34
+
35
+ def clean_conflicting_data
36
+ # tar is unable to --overwrite dir with symlink
37
+ execute('rm -rf /usr/share/foreman-proxy/.ssh')
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,17 @@
1
+ module Procedures::Restore
2
+ class Confirmation < ForemanMaintain::Procedure
3
+ metadata do
4
+ description 'Confirm dropping databases and running restore'
5
+ tags :restore
6
+ end
7
+
8
+ def run
9
+ warning = "\nWARNING: This script will drop and restore your database.\n" \
10
+ "Your existing installation will be replaced with the backup database.\n" \
11
+ "Once this operation is complete there is no going back.\n" \
12
+ 'Do you want to proceed?'
13
+ answer = ask_decision(warning, 'y(yes), q(quit)')
14
+ abort! unless answer == :yes
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,39 @@
1
+ module Procedures::Restore
2
+ class DropDatabases < ForemanMaintain::Procedure
3
+ metadata do
4
+ description 'Drop postgresql databases'
5
+
6
+ param :backup_dir,
7
+ 'Path to backup directory',
8
+ :required => true
9
+
10
+ confine do
11
+ feature(:foreman_database) || feature(:candlepin_database)
12
+ end
13
+ end
14
+
15
+ def run
16
+ backup = ForemanMaintain::Utils::Backup.new(@backup_dir)
17
+
18
+ with_spinner('Dropping databases') do |spinner|
19
+ feature(:service).handle_services(spinner, 'start', :only => ['postgresql'])
20
+ drop_foreman(backup, spinner)
21
+ drop_candlepin(backup, spinner)
22
+ end
23
+ end
24
+
25
+ def drop_foreman(backup, spinner)
26
+ if backup.file_map[:foreman_dump][:present]
27
+ spinner.update('Dropping foreman database')
28
+ feature(:foreman_database).dropdb
29
+ end
30
+ end
31
+
32
+ def drop_candlepin(backup, spinner)
33
+ if backup.file_map[:candlepin_dump][:present]
34
+ spinner.update('Dropping candlepin database')
35
+ feature(:candlepin_database).dropdb
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,70 @@
1
+ module Procedures::Restore
2
+ class ExtractFiles < ForemanMaintain::Procedure
3
+ metadata do
4
+ description 'Extract any existing tar files in backup'
5
+
6
+ param :backup_dir,
7
+ 'Path to backup directory',
8
+ :required => true
9
+ end
10
+
11
+ def run
12
+ backup = ForemanMaintain::Utils::Backup.new(@backup_dir)
13
+ with_spinner('Extracting Files') do |spinner|
14
+ if backup.file_map[:pulp_data][:present]
15
+ spinner.update('Extracting pulp data')
16
+ extract_pulp_data(backup)
17
+ end
18
+ if backup.file_map[:mongo_data][:present]
19
+ spinner.update('Extracting mongo data')
20
+ extract_mongo_data(backup)
21
+ end
22
+ if backup.file_map[:pgsql_data][:present]
23
+ spinner.update('Extracting pgsql data')
24
+ extract_pgsql_data(backup)
25
+ end
26
+ end
27
+ end
28
+
29
+ def base_tar
30
+ {
31
+ :overwrite => true,
32
+ :listed_incremental => '/dev/null',
33
+ :command => 'extract',
34
+ :directory => '/'
35
+ }
36
+ end
37
+
38
+ def extract_pulp_data(backup)
39
+ pulp_data_tar = if backup.pulp_tar_split?
40
+ base_tar.merge(
41
+ :multi_volume => true,
42
+ :split_data => true,
43
+ :archive => backup.file_map[:pulp_data][:path]
44
+ )
45
+ else
46
+ base_tar.merge(
47
+ :archive => backup.file_map[:pulp_data][:path]
48
+ )
49
+ end
50
+
51
+ feature(:tar).run(pulp_data_tar)
52
+ end
53
+
54
+ def extract_mongo_data(backup)
55
+ mongo_data_tar = base_tar.merge(
56
+ :archive => backup.file_map[:mongo_data][:path],
57
+ :gzip => true
58
+ )
59
+ feature(:tar).run(mongo_data_tar)
60
+ end
61
+
62
+ def extract_pgsql_data(backup)
63
+ pgsql_data_tar = base_tar.merge(
64
+ :archive => backup.file_map[:pgsql_data][:path],
65
+ :gzip => true
66
+ )
67
+ feature(:tar).run(pgsql_data_tar)
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,30 @@
1
+ module Procedures::Restore
2
+ class ForemanDump < ForemanMaintain::Procedure
3
+ metadata do
4
+ description 'Restore foreman postgresql dump from backup'
5
+ param :backup_dir,
6
+ 'Path to backup directory',
7
+ :required => true
8
+ preparation_steps { Checks::Foreman::DBUp.new }
9
+ confine do
10
+ feature(:foreman_database)
11
+ end
12
+ end
13
+
14
+ def run
15
+ backup = ForemanMaintain::Utils::Backup.new(@backup_dir)
16
+
17
+ with_spinner('Restoring foreman postgresql dump') do |spinner|
18
+ restore_foreman_dump(backup, spinner)
19
+ end
20
+ end
21
+
22
+ def restore_foreman_dump(backup, spinner)
23
+ if backup.file_map[:foreman_dump][:present]
24
+ spinner.update('Restoring foreman dump')
25
+ local = feature(:foreman_database).local?
26
+ feature(:foreman_database).restore_dump(backup.file_map[:foreman_dump][:path], local)
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,39 @@
1
+ module Procedures::Restore
2
+ class InstallerReset < ForemanMaintain::Procedure
3
+ metadata do
4
+ description 'Run installer reset'
5
+
6
+ param :incremental_backup,
7
+ 'Is the backup incremental?'
8
+ end
9
+
10
+ def run
11
+ with_spinner('Resetting') do |spinner|
12
+ if @incremental_backup
13
+ spinner.update('Skipping installer reset for incremental update')
14
+ else
15
+ spinner.update('Installer reset')
16
+ execute!(installer_cmd)
17
+ end
18
+ end
19
+ end
20
+
21
+ def installer_cmd
22
+ installer = "yes | #{feature(:installer).installer_command} "
23
+ installer << '-v --reset '
24
+ if feature(:instance).foreman_proxy_with_content?
25
+ installer << '--foreman-proxy-register-in-foreman false '
26
+ end
27
+
28
+ # We always disable system checks to avoid unnecessary errors. The installer should have
29
+ # already ran since this is to be run on an existing system, which means installer checks
30
+ # has already been skipped
31
+ if feature(:foreman_proxy) &&
32
+ feature(:foreman_proxy).with_content? &&
33
+ check_min_version('katello-installer-base', '3.2.0')
34
+ installer << '--disable-system-checks '
35
+ end
36
+ installer
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,41 @@
1
+ module Procedures::Restore
2
+ class MongoDump < ForemanMaintain::Procedure
3
+ metadata do
4
+ description 'Restore mongo dump'
5
+ for_feature :pulp
6
+ param :backup_dir,
7
+ 'Path to backup directory',
8
+ :required => true
9
+ preparation_steps do
10
+ [Checks::Mongo::DBUp.new, Checks::Mongo::ToolsInstalled.new]
11
+ end
12
+ confine do
13
+ feature(:mongo) && feature(:pulp)
14
+ end
15
+ end
16
+
17
+ def run
18
+ backup = ForemanMaintain::Utils::Backup.new(@backup_dir)
19
+ with_spinner('Restoring mongo dump') do |spinner|
20
+ handle_mongo_service('start', spinner)
21
+ drop_and_restore_mongo(backup, spinner)
22
+ handle_mongo_service('stop', spinner)
23
+ end
24
+ end
25
+
26
+ def handle_mongo_service(action, spinner)
27
+ if feature(:instance).database_local?(:mongo)
28
+ feature(:service).handle_services(spinner, action,
29
+ :only => feature(:mongo).services.keys)
30
+ end
31
+ end
32
+
33
+ def drop_and_restore_mongo(backup, spinner)
34
+ spinner.update('Dropping pulp_database')
35
+ feature(:mongo).dropdb
36
+
37
+ spinner.update('Restoring mongo dump')
38
+ feature(:mongo).restore(backup.file_map[:mongo_dump][:path])
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,36 @@
1
+ module Procedures::Restore
2
+ class PgGlobalObjects < ForemanMaintain::Procedure
3
+ include ForemanMaintain::Concerns::SystemHelpers
4
+
5
+ metadata do
6
+ description 'Restore any existing postgresql global objects from backup'
7
+
8
+ param :backup_dir,
9
+ 'Path to backup directory',
10
+ :required => true
11
+
12
+ confine do
13
+ feature(:foreman_database) || feature(:candlepin_database)
14
+ end
15
+ end
16
+
17
+ def run
18
+ backup = ForemanMaintain::Utils::Backup.new(@backup_dir)
19
+ restore_global_objects(backup.file_map[:pg_globals][:path])
20
+ end
21
+
22
+ def restore_global_objects(pg_global_file)
23
+ if feature(:instance).postgresql_local?
24
+ with_spinner('') do |spinner|
25
+ feature(:service).handle_services(spinner, 'start', :only => ['postgresql'])
26
+
27
+ spinner.update('Restoring postgresql global objects')
28
+ local_db = feature(:foreman_database).local? ? :foreman_database : :candlepin_database
29
+ feature(local_db).restore_pg_globals(pg_global_file)
30
+ end
31
+ else
32
+ skip 'Restore of global objects is not supported for remote databases.'
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,18 @@
1
+ module Procedures::Restore
2
+ class PostgresOwner < ForemanMaintain::Procedure
3
+ include ForemanMaintain::Concerns::SystemHelpers
4
+ metadata do
5
+ description 'Make postgres owner of backup directory'
6
+
7
+ param :backup_dir,
8
+ 'Path to backup directory',
9
+ :required => true
10
+ end
11
+
12
+ def run
13
+ if feature(:instance).foreman_proxy_with_content?
14
+ FileUtils.chown(nil, 'postgres', @backup_dir)
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,22 @@
1
+ module Procedures::Selinux
2
+ class SetFileSecurity < ForemanMaintain::Procedure
3
+ metadata do
4
+ description 'Setting file security'
5
+
6
+ param :incremental_backup,
7
+ 'Is the backup incremental?',
8
+ :required => true
9
+ manual_detection
10
+ end
11
+
12
+ def run
13
+ with_spinner('Restoring SELinux context') do |spinner|
14
+ if @incremental_backup
15
+ spinner.update('Skipping for incremental update')
16
+ else
17
+ execute!('restorecon -Rn /')
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,18 @@
1
+ module Procedures::Service
2
+ class DaemonReload < ForemanMaintain::Procedure
3
+ include ForemanMaintain::Concerns::SystemHelpers
4
+ metadata do
5
+ description 'Run daemon reload'
6
+
7
+ confine do
8
+ systemd_installed?
9
+ end
10
+ end
11
+
12
+ def run
13
+ unless feature(:instance).foreman_proxy_with_content?
14
+ execute!('systemctl daemon-reload')
15
+ end
16
+ end
17
+ end
18
+ end
@@ -28,11 +28,11 @@ module Procedures::Service
28
28
  break
29
29
  elsif retry_count < (RETRIES_FOR_SERVICES_RESTART - 1)
30
30
  apply_sleep_before_retry(spinner, result)
31
+ else
32
+ raise 'Hammer ping failed!'
31
33
  end
32
34
  end
33
35
  end
34
- rescue StandardError => e
35
- logger.error e.message
36
36
  end
37
37
 
38
38
  def retry_message(retry_count)
@@ -0,0 +1,91 @@
1
+ require 'foreman_maintain/utils/backup'
2
+
3
+ module ForemanMaintain::Scenarios
4
+ class Restore < ForemanMaintain::Scenario
5
+ metadata do
6
+ description 'Restore backup'
7
+ param :backup_dir, 'Path to backup directory'
8
+ param :incremental_backup, 'Is the backup incremental?'
9
+ manual_detection
10
+ end
11
+
12
+ # rubocop:disable Metrics/MethodLength,Metrics/AbcSize
13
+ def compose
14
+ backup = ForemanMaintain::Utils::Backup.new(context.get(:backup_dir))
15
+
16
+ add_steps(find_checks(:root_user))
17
+ supported_version_check
18
+ add_steps_with_context(Checks::Restore::ValidateBackup,
19
+ Procedures::Restore::Confirmation,
20
+ Checks::Restore::ValidateHostname,
21
+ Procedures::Selinux::SetFileSecurity,
22
+ Procedures::Restore::Configs,
23
+ Procedures::Restore::InstallerReset,
24
+ Procedures::Service::Stop)
25
+ add_steps_with_context(Procedures::Restore::ExtractFiles) if backup.tar_backups_exist?
26
+ drop_dbs(backup)
27
+ if backup.sql_dump_files_exist? && feature(:instance).postgresql_local?
28
+ add_step(Procedures::Service::Start.new(:only => ['postgresql']))
29
+ end
30
+ restore_sql_dumps(backup)
31
+ if backup.sql_dump_files_exist? && feature(:instance).postgresql_local?
32
+ add_step(Procedures::Service::Stop.new(:only => ['postgresql']))
33
+ end
34
+ restore_mongo_dump(backup)
35
+ add_steps_with_context(Procedures::Pulp::Migrate,
36
+ Procedures::Service::Start,
37
+ Procedures::Service::DaemonReload)
38
+ end
39
+ # rubocop:enable Metrics/MethodLength,Metrics/AbcSize
40
+
41
+ def drop_dbs(backup)
42
+ if backup.file_map[:candlepin_dump][:present] ||
43
+ backup.file_map[:foreman_dump][:present]
44
+ add_steps_with_context(Procedures::Restore::DropDatabases)
45
+ end
46
+ end
47
+
48
+ def restore_sql_dumps(backup)
49
+ if backup.file_map[:pg_globals][:present]
50
+ add_steps_with_context(Procedures::Restore::PgGlobalObjects)
51
+ end
52
+ if backup.file_map[:candlepin_dump][:present]
53
+ add_steps_with_context(Procedures::Restore::CandlepinDump)
54
+ end
55
+ if backup.file_map[:foreman_dump][:present]
56
+ add_steps_with_context(Procedures::Restore::ForemanDump)
57
+ end
58
+ end
59
+
60
+ def restore_mongo_dump(backup)
61
+ if backup.file_map[:mongo_dump][:present]
62
+ add_steps_with_context(Procedures::Restore::MongoDump)
63
+ end
64
+ end
65
+
66
+ def supported_version_check
67
+ if feature(:downstream) && feature(:downstream).less_than_version?('6.3')
68
+ msg = 'ERROR: Restore subcommand is supported by Satellite 6.3+. ' \
69
+ 'Please use katello-restore instead.'
70
+ abort(msg)
71
+ end
72
+ end
73
+
74
+ def set_context_mapping
75
+ context.map(:backup_dir,
76
+ Checks::Restore::ValidateBackup => :backup_dir,
77
+ Checks::Restore::ValidateHostname => :backup_dir,
78
+ Procedures::Restore::Configs => :backup_dir,
79
+ Procedures::Restore::DropDatabases => :backup_dir,
80
+ Procedures::Restore::PgGlobalObjects => :backup_dir,
81
+ Procedures::Restore::CandlepinDump => :backup_dir,
82
+ Procedures::Restore::ForemanDump => :backup_dir,
83
+ Procedures::Restore::ExtractFiles => :backup_dir,
84
+ Procedures::Restore::MongoDump => :backup_dir)
85
+
86
+ context.map(:incremental_backup,
87
+ Procedures::Selinux::SetFileSecurity => :incremental_backup,
88
+ Procedures::Restore::InstallerReset => :incremental_backup)
89
+ end
90
+ end
91
+ end