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
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: a85ae503f11debb15fd1a4321f46da9f2d6a89db
4
- data.tar.gz: 94b3ffb6d47033a5351798cde22c5c1924da69c2
3
+ metadata.gz: '078ab0c0f9b3bfe3b4a6a798aaa181bb0ecba87e'
4
+ data.tar.gz: 898a5faf68654553aafaa03c8afe026e33ddc8e2
5
5
  SHA512:
6
- metadata.gz: 4a0956cc569272ede19b66d720c4ced7861f9a0a32c06cb6203188a1d8471f42cd2198976f3438b5a2ede7be86e164300df8bc3477c299b193ff156c69e6c5aa
7
- data.tar.gz: f947a4053c037fe23563250e06928ab75e99d2a2b9fa78b6c2d03869bfa70419181d4960c476991242782a38d424d3c32fe9ff2366904311c4ad0f3da853a7ff
6
+ metadata.gz: c512de5ef767ec004443beb7f32386ea113899b626ebe195ac69aeba6d2ec21cb66fcb4d93b8423fbbb8e3ee56bf54f95d11d59bc6fc892fa412b28b532f9e3b
7
+ data.tar.gz: 3be8f1aad6584b3565841633d3b6f6ef99c2fbf7eb7b986e34e58b7dce60fd775bde2a3dbed6ce3a11cbddaff0490ae82d31f126ae92cfa36f40807b77836491
data/bin/foreman-maintain CHANGED
@@ -9,4 +9,5 @@ CONFIG_FILE = '/etc/foreman-maintain/foreman_maintain.yml'.freeze
9
9
  ForemanMaintain.setup
10
10
 
11
11
  require 'foreman_maintain/cli'
12
- ForemanMaintain::Cli::MainCommand.run
12
+ exit_code = ForemanMaintain::Cli::MainCommand.run
13
+ exit(exit_code || 0)
@@ -10,7 +10,7 @@ module Checks::Backup
10
10
 
11
11
  def run
12
12
  assert(File.directory?(@backup_dir), "Backup directory (#{@backup_dir}) does not exit.")
13
- if local_psql_database?
13
+ if feature(:instance).postgresql_local?
14
14
  result = system("runuser - postgres -c 'test -w #{@backup_dir}'")
15
15
  assert(result, "Postgres user needs write access to the backup directory \n" \
16
16
  "Please allow the postgres user write access to #{@backup_dir}" \
@@ -0,0 +1,77 @@
1
+ require 'foreman_maintain/utils/backup'
2
+
3
+ module Checks::Restore
4
+ class ValidateBackup < ForemanMaintain::Check
5
+ metadata do
6
+ description 'Validate backup has appropriate files'
7
+
8
+ param :backup_dir,
9
+ 'Path to backup directory',
10
+ :required => true
11
+ manual_detection
12
+ end
13
+
14
+ def run
15
+ backup = ForemanMaintain::Utils::Backup.new(@backup_dir)
16
+ assert(backup.valid_backup?, valid_backup_message(backup))
17
+ end
18
+
19
+ def valid_backup_message(backup)
20
+ message = "\n"
21
+ message += "The given directory does not contain the required files or has too many files\n\n"
22
+ message += "All backup directories contain: #{backup.standard_files.join(', ')}\n"
23
+ message += required_files(backup)
24
+ message += 'Including pulp_data.tar is optional and '
25
+ message += "will restore pulp data to the filesystem if included.\n\n"
26
+ message += "Only the following files were found: #{backup.present_files.join(', ')}\n"
27
+ message
28
+ end
29
+
30
+ def required_files(backup)
31
+ message = ''
32
+ message += if feature(:instance).foreman_proxy_with_content?
33
+ required_fpc_files(backup)
34
+ elsif feature(:katello)
35
+ required_katello_files(backup)
36
+ else
37
+ required_foreman_files(backup)
38
+ end
39
+ message
40
+ end
41
+
42
+ def required_katello_files(backup)
43
+ backup_files_message(
44
+ backup.katello_online_files.join(', '),
45
+ backup.katello_offline_files.join(', '),
46
+ [backup.katello_online_files + backup.katello_offline_files].join(', ')
47
+ )
48
+ end
49
+
50
+ def required_fpc_files(backup)
51
+ backup_files_message(
52
+ backup.fpc_online_files.join(', '),
53
+ backup.fpc_offline_files.join(', '),
54
+ [backup.fpc_online_files + backup.fpc_offline_files].join(', ')
55
+ )
56
+ end
57
+
58
+ def required_foreman_files(backup)
59
+ backup_files_message(
60
+ backup.foreman_online_files.join(', '),
61
+ backup.foreman_offline_files.join(', '),
62
+ [backup.foreman_online_files + backup.foreman_offline_files].join(', ')
63
+ )
64
+ end
65
+
66
+ def backup_files_message(online_files, offline_files, logical_files)
67
+ message = ''
68
+ message += 'An online or remote database backup directory contains: '
69
+ message += "#{online_files}\n"
70
+ message += 'An offline backup directory contains: '
71
+ message += "#{offline_files}\n"
72
+ message += 'A logical backup directory contains: '
73
+ message += "#{logical_files}\n"
74
+ message
75
+ end
76
+ end
77
+ end
@@ -0,0 +1,20 @@
1
+ require 'foreman_maintain/utils/backup'
2
+
3
+ module Checks::Restore
4
+ class ValidateHostname < ForemanMaintain::Check
5
+ metadata do
6
+ description 'Validate hostname is the same as backup'
7
+
8
+ param :backup_dir,
9
+ 'Path to backup directory',
10
+ :required => true
11
+ manual_detection
12
+ end
13
+
14
+ def run
15
+ msg = 'The hostname in the backup does not match the hostname of the system'
16
+ backup = ForemanMaintain::Utils::Backup.new(@backup_dir)
17
+ assert(backup.validate_hostname?, msg)
18
+ end
19
+ end
20
+ end
@@ -17,6 +17,10 @@ class Features::ForemanProxy < ForemanMaintain::Feature
17
17
  dhcp_req_pass? && !syntax_error_exists?
18
18
  end
19
19
 
20
+ def with_content?
21
+ !!feature(:pulp)
22
+ end
23
+
20
24
  def services
21
25
  {
22
26
  'foreman-proxy' => 20,
@@ -36,7 +40,8 @@ class Features::ForemanProxy < ForemanMaintain::Feature
36
40
  def config_files(for_features = ['all'])
37
41
  configs = [
38
42
  '/etc/foreman-proxy',
39
- '/usr/share/foreman-proxy/.ssh/',
43
+ '/usr/share/foreman-proxy/.ssh',
44
+ '/var/lib/foreman-proxy/ssh',
40
45
  '/etc/smart_proxy_dynflow_core/settings.yml',
41
46
  '/etc/sudoers.d/foreman-proxy'
42
47
  ]
@@ -108,7 +108,11 @@ class Features::ForemanTasks < ForemanMaintain::Feature
108
108
  end
109
109
 
110
110
  def services
111
- { 'foreman-tasks' => 30 }
111
+ if check_min_version('foreman', '1.17')
112
+ { 'dynflowd' => 30 }
113
+ else
114
+ { 'foreman-tasks' => 30 }
115
+ end
112
116
  end
113
117
 
114
118
  private
@@ -29,6 +29,10 @@ class Features::Instance < ForemanMaintain::Feature
29
29
  end
30
30
  end
31
31
 
32
+ def database_remote?(feature)
33
+ !!feature(feature) && !feature(feature).local?
34
+ end
35
+
32
36
  def database_local?(feature)
33
37
  !!feature(feature) && feature(feature).local?
34
38
  end
@@ -36,4 +40,8 @@ class Features::Instance < ForemanMaintain::Feature
36
40
  def postgresql_local?
37
41
  database_local?(:candlepin_database) || database_local?(:foreman_database)
38
42
  end
43
+
44
+ def foreman_proxy_with_content?
45
+ feature(:foreman_proxy) && feature(:foreman_proxy).with_content? && !feature(:katello)
46
+ end
39
47
  end
@@ -7,7 +7,6 @@ class Features::Katello < ForemanMaintain::Feature
7
7
  end
8
8
  end
9
9
 
10
- # TODO: refactor to new features?
11
10
  def data_dirs
12
11
  @dirs ||= ['/var/lib/pulp', '/var/lib/mongodb', '/var/lib/pgsql']
13
12
  end
@@ -31,16 +31,31 @@ class Features::Mongo < ForemanMaintain::Feature
31
31
  @configuration = load_db_config(config_file)
32
32
  end
33
33
 
34
+ # guess mongo version from installed packages
35
+ # safe method to run mongo commands prior we know
36
+ # what version uses the target DB
37
+ def available_core
38
+ return @core unless @core.nil? # return correct mongo ver if known
39
+ if @available_core.nil?
40
+ @available_core = ForemanMaintain::Utils::MongoCoreInstalled.new
41
+ end
42
+ @available_core
43
+ end
44
+
34
45
  def core
35
46
  if @core.nil?
36
- version = server_version
37
- @core = if version =~ /^3\.4/
38
- logger.debug("Mongo #{version} detected, using commands from rh-mongodb34 SCL")
39
- ForemanMaintain::Utils::MongoCore34.new
40
- else
41
- logger.debug("Mongo #{version} detected, using default commands")
42
- ForemanMaintain::Utils::MongoCore.new
43
- end
47
+ begin
48
+ version = server_version
49
+ @core = if version =~ /^3\.4/
50
+ load_mongo_core_34(version)
51
+ else
52
+ load_mongo_core_default(version)
53
+ end
54
+ rescue ForemanMaintain::Error::ExecutionError
55
+ # server version detection failed
56
+ logger.debug('Mongo version detection failed, choosing from installed versions')
57
+ @core = @available_core
58
+ end
44
59
  end
45
60
  @core
46
61
  end
@@ -72,20 +87,27 @@ class Features::Mongo < ForemanMaintain::Feature
72
87
  :hidden_patterns => [config['password']].compact)
73
88
  end
74
89
 
90
+ def restore(dir, config = configuration)
91
+ cmd = base_command(core.restore_command, config,
92
+ "-d #{config['name']} #{File.join(dir, config['name'])}")
93
+ execute!(cmd, :hidden_patterns => [config['password']].compact)
94
+ end
95
+
75
96
  def dropdb(config = configuration)
76
97
  execute!(mongo_command("--eval 'db.dropDatabase()'", config),
77
98
  :hidden_patterns => [config['password']].compact)
78
99
  end
79
100
 
80
101
  def ping(config = configuration)
81
- execute?(mongo_command("--eval 'ping:1'"),
102
+ execute?(mongo_command("--eval 'ping:1'", config),
82
103
  :hidden_patterns => [config['password']].compact)
83
104
  end
84
105
 
85
106
  def server_version(config = configuration)
86
107
  # do not use any core methods as we need this prior the core is created
87
- version = execute(base_command('mongo', config, "--eval 'db.version()' #{config['name']}"),
88
- :hidden_patterns => [config['password']].compact)
108
+ mongo_cmd = base_command(available_core.client_command, config,
109
+ "--eval 'db.version()' #{config['name']}")
110
+ version = execute!(mongo_cmd, :hidden_patterns => [config['password']].compact)
89
111
  version.split("\n").last
90
112
  end
91
113
 
@@ -98,7 +120,8 @@ class Features::Mongo < ForemanMaintain::Feature
98
120
  :archive => backup_file,
99
121
  :command => 'create',
100
122
  :exclude => ['mongod.lock'],
101
- :transform => 's,^,var/lib/mongodb/,S'
123
+ :transform => 's,^,var/lib/mongodb/,S',
124
+ :files => '*'
102
125
  }.merge(extra_tar_options)
103
126
  feature(:tar).run(tar_options)
104
127
  end
@@ -110,6 +133,22 @@ class Features::Mongo < ForemanMaintain::Feature
110
133
 
111
134
  private
112
135
 
136
+ def load_mongo_core_default(version)
137
+ unless find_package('mongodb')
138
+ raise ForemanMaintain::Error::Fail, 'Mongo client was not found'
139
+ end
140
+ logger.debug("Mongo #{version} detected, using default commands")
141
+ ForemanMaintain::Utils::MongoCore.new
142
+ end
143
+
144
+ def load_mongo_core_34(version)
145
+ unless find_package('rh-mongodb34-mongodb')
146
+ raise ForemanMaintain::Error::Fail, 'Mongo client ver. 3.4 was not found'
147
+ end
148
+ logger.debug("Mongo #{version} detected, using commands from rh-mongodb34 SCL")
149
+ ForemanMaintain::Utils::MongoCore34.new
150
+ end
151
+
113
152
  def norm_value(value)
114
153
  value = value.strip
115
154
  case value
@@ -29,7 +29,8 @@ class Features::Pulp < ForemanMaintain::Feature
29
29
  '/etc/qpid-dispatch',
30
30
  '/etc/crane.conf',
31
31
  '/etc/default/pulp_workers',
32
- '/var/lib/qpidd'
32
+ '/var/lib/qpidd',
33
+ '/etc/qpid-dispatch'
33
34
  ]
34
35
  end
35
36
 
@@ -66,6 +66,42 @@ class Features::Service < ForemanMaintain::Feature
66
66
  end
67
67
 
68
68
  def perform_action_on_service(action, service)
69
+ if service == 'postgresql'
70
+ if feature(:instance).postgresql_local?
71
+ perform_action_on_local_service(action, service)
72
+ end
73
+ if feature(:instance).database_remote?(:candlepin_database)
74
+ remote_db_message('Candlepin', :candlepin_database, action)
75
+ end
76
+ if feature(:instance).database_remote?(:foreman_database)
77
+ remote_db_message('Foreman', :foreman_database, action)
78
+ end
79
+ elsif service =~ /^.*mongod$/ && feature(:instance).database_remote?(:mongo)
80
+ remote_db_message('Pulp', :mongo, action)
81
+ else
82
+ perform_action_on_local_service(action, service)
83
+ end
84
+ end
85
+
86
+ def remote_db_message(app, db, action)
87
+ ping = !!feature(db).ping
88
+ message = if %w[enable disable].include?(action)
89
+ " - #{app} DB is remote. Can not #{action} the service."
90
+ else
91
+ 'the service is on remote host. The DB is ' + (ping ? 'UP.' : 'DOWN.')
92
+ end
93
+ if action == 'status'
94
+ puts "\nFor #{app} DB #{message}\n"
95
+ else
96
+ print " - #{message}"
97
+ end
98
+ logger.info(message)
99
+ if action == 'start' && !ping
100
+ raise ForemanMaintain::Error::Fail, "The remote #{app} databse is down."
101
+ end
102
+ end
103
+
104
+ def perform_action_on_local_service(action, service)
69
105
  command = service_command(action, service)
70
106
  if action == 'status'
71
107
  status = execute(command)
@@ -14,7 +14,11 @@ class Features::Tar < ForemanMaintain::Feature
14
14
  # @option options [String] :volume_size size of tar volume
15
15
  # (will try to split the archive when set)
16
16
  # @option options [String] :files (*) files to operate on
17
- # rubocop:disable Metrics/MethodLength, Metrics/AbcSize
17
+ # @option options [String] :directory change to directory DIR
18
+ # @option options [Boolean] :multi_volume create/list/extract multi-volume archive
19
+ # @option options [Boolean] :overwrite overwrite existing files when extracting
20
+ # @option options [Boolean] :gzip filter the archive through gzip
21
+ # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
18
22
  def run(options = {})
19
23
  volume_size = options.fetch(:volume_size, nil)
20
24
  validate_volume_size(volume_size) unless volume_size.nil?
@@ -30,6 +34,9 @@ class Features::Tar < ForemanMaintain::Feature
30
34
  tar_command << "--new-volume-script=#{split_tar_script}"
31
35
  end
32
36
 
37
+ tar_command << '--overwrite' if options[:overwrite]
38
+ tar_command << '--gzip' if options[:gzip]
39
+
33
40
  exclude = options.fetch(:exclude, [])
34
41
  exclude.each do |ex|
35
42
  tar_command << "--exclude=#{ex}"
@@ -41,12 +48,17 @@ class Features::Tar < ForemanMaintain::Feature
41
48
  trans = options.fetch(:transform, nil)
42
49
  tar_command << "--transform '#{trans}'" if trans
43
50
 
44
- tar_command << '-S'
45
- tar_command << options.fetch(:files, '*')
51
+ tar_command << '-M' if options[:multi_volume]
52
+ tar_command << "--directory=#{options[:directory]}" if options[:directory]
53
+
54
+ if options[:files]
55
+ tar_command << '-S'
56
+ tar_command << options.fetch(:files, '*')
57
+ end
46
58
 
47
59
  execute!(tar_command.join(' '))
48
60
  end
49
- # rubocop:enable Metrics/MethodLength, Metrics/AbcSize
61
+ # rubocop:enable Metrics/AbcSize, Metrics/MethodLength
50
62
 
51
63
  def validate_volume_size(size)
52
64
  if size.nil? || size !~ /^\d+[bBcGKkMPTw]?$/
@@ -1,6 +1,8 @@
1
1
  module Procedures::Backup
2
2
  module Online
3
3
  class PgGlobalObjects < ForemanMaintain::Procedure
4
+ include ForemanMaintain::Concerns::SystemHelpers
5
+
4
6
  metadata do
5
7
  description 'Backup Postgres global objects online'
6
8
  tags :backup
@@ -11,7 +13,7 @@ module Procedures::Backup
11
13
  end
12
14
 
13
15
  def run
14
- if feature(:foreman_database).local? || feature(:candlepin_database).local?
16
+ if feature(:instance).postgresql_local?
15
17
  local_db = feature(:foreman_database).local? ? :foreman_database : :candlepin_database
16
18
  feature(local_db).backup_global_objects(File.join(@backup_dir, 'pg_globals.dump'))
17
19
  else
@@ -16,7 +16,7 @@ module Procedures::Backup
16
16
  FileUtils.chmod_R 0o770, @backup_dir
17
17
  end
18
18
 
19
- if local_psql_database? && !@preserve_dir
19
+ if feature(:instance).postgresql_local? && !@preserve_dir
20
20
  FileUtils.chown_R(nil, 'postgres', @backup_dir)
21
21
  end
22
22
 
@@ -33,7 +33,8 @@ module Procedures::Backup
33
33
  :exclude => ['var/lib/pulp/katello-export'],
34
34
  :listed_incremental => File.join(@backup_dir, '.pulp.snar'),
35
35
  :transform => 's,^,var/lib/pulp/,S',
36
- :volume_size => @tar_volume_size
36
+ :volume_size => @tar_volume_size,
37
+ :files => '*'
37
38
  )
38
39
  end
39
40
 
@@ -0,0 +1,21 @@
1
+ module Procedures::Pulp
2
+ class Migrate < ForemanMaintain::Procedure
3
+ metadata do
4
+ description 'Migrate pulp db'
5
+ for_feature :pulp
6
+ end
7
+
8
+ def run
9
+ with_spinner('Migrating pulp') do |spinner|
10
+ necessary_services = feature(:mongo).services.keys + ['qpidd']
11
+ pulp_services = %w[pulp_celerybeat pulp_workers pulp_resource_manager]
12
+
13
+ feature(:service).handle_services(spinner, 'start', :only => necessary_services)
14
+ feature(:service).handle_services(spinner, 'stop', :only => pulp_services)
15
+
16
+ spinner.update('Migrating pulp database')
17
+ execute!('su - apache -s /bin/bash -c pulp-manage-db')
18
+ end
19
+ end
20
+ end
21
+ end