foreman_maintain 0.2.1 → 0.2.2

Sign up to get free protection for your applications and to get access to all the features.
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