foreman_maintain 0.4.6 → 0.4.8
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.
- checksums.yaml +4 -4
- data/definitions/checks/version_locking_enabled.rb +1 -1
- data/definitions/features/installer.rb +12 -2
- data/definitions/features/package_manager.rb +1 -2
- data/definitions/procedures/installer/run.rb +15 -0
- data/definitions/procedures/packages/enable_version_locking.rb +1 -6
- data/definitions/procedures/packages/install.rb +13 -2
- data/definitions/procedures/packages/installer_confirmation.rb +18 -0
- data/definitions/procedures/packages/lock_versions.rb +2 -6
- data/definitions/procedures/packages/locking_status.rb +25 -3
- data/definitions/procedures/packages/unlock_versions.rb +1 -1
- data/definitions/procedures/packages/update.rb +10 -1
- data/definitions/scenarios/packages.rb +90 -0
- data/extras/foreman-maintain.sh +14 -0
- data/extras/foreman_protector/foreman-protector.conf +3 -0
- data/extras/foreman_protector/foreman-protector.py +85 -0
- data/extras/foreman_protector/foreman-protector.whitelist +19 -0
- data/extras/passenger-recycler.cron +3 -0
- data/lib/foreman_maintain/cli/packages_command.rb +36 -8
- data/lib/foreman_maintain/cli.rb +1 -1
- data/lib/foreman_maintain/package_manager/base.rb +3 -13
- data/lib/foreman_maintain/package_manager/yum.rb +45 -62
- data/lib/foreman_maintain/runner.rb +11 -8
- data/lib/foreman_maintain/upgrade_runner.rb +6 -2
- data/lib/foreman_maintain/version.rb +1 -1
- metadata +10 -3
- data/definitions/scenarios/version_locking.rb +0 -39
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 0d748d16a88ff4ea39bdefe64acb950f12838579e8b32bf4077c2ce600ee69ee
|
4
|
+
data.tar.gz: fbbdc32ce0d5b282c3c330a7cd6660126f8b07ba732ed323f91f433141f7d306
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 870a8eec52a19ce134968b45435b0913efc3e950ec9cba783528b8588b5830098bd48c864c6916a9bc3e378b57c7ac7056066323914c1beca5fccaa9237c257b
|
7
|
+
data.tar.gz: 0d9c910b5ec28449a47a89615e7df4d1665d178aa7c6b79c24f238ededc751b85a5582889cad8d1a8a235931bf909c136ce9d457d8be946ca9ffe77085c93cd4
|
@@ -24,7 +24,7 @@ class Features::Installer < ForemanMaintain::Feature
|
|
24
24
|
end
|
25
25
|
|
26
26
|
def configuration
|
27
|
-
YAML.load_file(config_file)
|
27
|
+
@configuration ||= YAML.load_file(config_file)
|
28
28
|
end
|
29
29
|
|
30
30
|
def config_file
|
@@ -85,7 +85,9 @@ class Features::Installer < ForemanMaintain::Feature
|
|
85
85
|
end
|
86
86
|
|
87
87
|
def run(arguments = '', exec_options = {})
|
88
|
-
execute!("LANG=en_US.utf-8 #{installer_command} #{arguments}".strip, exec_options)
|
88
|
+
out = execute!("LANG=en_US.utf-8 #{installer_command} #{arguments}".strip, exec_options)
|
89
|
+
@configuration = nil
|
90
|
+
out
|
89
91
|
end
|
90
92
|
|
91
93
|
def upgrade(exec_options = {})
|
@@ -103,6 +105,14 @@ class Features::Installer < ForemanMaintain::Feature
|
|
103
105
|
feature(:installer).answers['foreman']['admin_password']
|
104
106
|
end
|
105
107
|
|
108
|
+
def lock_package_versions?
|
109
|
+
!!(configuration[:custom] && configuration[:custom][:lock_package_versions])
|
110
|
+
end
|
111
|
+
|
112
|
+
def lock_package_versions_supported?
|
113
|
+
!(configuration[:custom] && configuration[:custom][:lock_package_versions]).nil?
|
114
|
+
end
|
115
|
+
|
106
116
|
private
|
107
117
|
|
108
118
|
def load_answers(config)
|
@@ -6,8 +6,7 @@ class Features::PackageManager < ForemanMaintain::Feature
|
|
6
6
|
extend Forwardable
|
7
7
|
def_delegators :manager, :lock_versions, :unlock_versions,
|
8
8
|
:installed?, :find_installed_package, :install, :update,
|
9
|
-
:version_locking_enabled?, :
|
10
|
-
:foreman_related_packages, :version_locking_packages,
|
9
|
+
:version_locking_enabled?, :install_version_locking,
|
11
10
|
:versions_locked?, :clean_cache, :remove, :files_not_owned_by_package
|
12
11
|
|
13
12
|
def self.type
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module Procedures::Installer
|
2
|
+
class Run < ForemanMaintain::Procedure
|
3
|
+
metadata do
|
4
|
+
param :arguments, 'Arguments passed to installer'
|
5
|
+
end
|
6
|
+
|
7
|
+
def run
|
8
|
+
feature(:installer).run(@arguments, :interactive => true)
|
9
|
+
end
|
10
|
+
|
11
|
+
def description
|
12
|
+
"Running #{feature(:installer).installer_command} #{@arguments}"
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -6,12 +6,7 @@ module Procedures::Packages
|
|
6
6
|
end
|
7
7
|
|
8
8
|
def run
|
9
|
-
|
10
|
-
feature(:package_manager).install(packages, :assumeyes => @assumeyes)
|
11
|
-
unless feature(:package_manager).installed?(packages)
|
12
|
-
raise "Unable to install some of the required dependences: #{packages.join(' ')}"
|
13
|
-
end
|
14
|
-
feature(:package_manager).configure_version_locking
|
9
|
+
feature(:package_manager).install_version_locking(:assumeyes => @assumeyes)
|
15
10
|
end
|
16
11
|
end
|
17
12
|
end
|
@@ -1,16 +1,27 @@
|
|
1
1
|
module Procedures::Packages
|
2
2
|
class Install < ForemanMaintain::Procedure
|
3
3
|
metadata do
|
4
|
+
description 'Install packages'
|
4
5
|
param :packages, 'List of packages to install', :array => true
|
5
6
|
param :assumeyes, 'Do not ask for confirmation'
|
7
|
+
param :force, 'Do not skip if package is installed', :flag => true, :default => false
|
8
|
+
param :warn_on_errors, 'Do not interrupt scenario on failure',
|
9
|
+
:flag => true, :default => false
|
6
10
|
end
|
7
11
|
|
8
12
|
def run
|
9
|
-
|
13
|
+
assumeyes_val = @assumeyes.nil? ? assumeyes? : @assumeyes
|
14
|
+
packages_action(:install, @packages, :assumeyes => assumeyes_val)
|
15
|
+
rescue ForemanMaintain::Error::ExecutionError => e
|
16
|
+
if @warn_on_errors
|
17
|
+
set_status(:warning, e.message)
|
18
|
+
else
|
19
|
+
raise
|
20
|
+
end
|
10
21
|
end
|
11
22
|
|
12
23
|
def necessary?
|
13
|
-
@packages.any? { |package| package_version(package).nil? }
|
24
|
+
@force || @packages.any? { |package| package_version(package).nil? }
|
14
25
|
end
|
15
26
|
|
16
27
|
def runtime_message
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module Procedures::Packages
|
2
|
+
class InstallerConfirmation < ForemanMaintain::Procedure
|
3
|
+
metadata do
|
4
|
+
description 'Confirm installer run is allowed'
|
5
|
+
end
|
6
|
+
|
7
|
+
def run
|
8
|
+
question = "WARNING: This script runs #{feature(:installer).installer_command} " \
|
9
|
+
"after the yum execution \n" \
|
10
|
+
"to ensure the #{feature(:instance).product_name} " \
|
11
|
+
"is in a consistent state.\n" \
|
12
|
+
"As a result some of your services may be restarted. \n\n" \
|
13
|
+
'Do you want to proceed?'
|
14
|
+
answer = ask_decision(question, 'y(yes), q(quit)')
|
15
|
+
abort! unless answer == :yes
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -2,16 +2,12 @@ module Procedures::Packages
|
|
2
2
|
class LockVersions < ForemanMaintain::Procedure
|
3
3
|
metadata do
|
4
4
|
for_feature :package_manager
|
5
|
-
description 'Lock
|
5
|
+
description 'Lock packages'
|
6
6
|
preparation_steps { [Checks::VersionLockingEnabled.new] }
|
7
7
|
end
|
8
8
|
|
9
9
|
def run
|
10
|
-
|
11
|
-
package_list = feature(:package_manager).foreman_related_packages
|
12
|
-
spinner.update('Locking packages')
|
13
|
-
feature(:package_manager).lock_versions(package_list)
|
14
|
-
end
|
10
|
+
feature(:package_manager).lock_versions
|
15
11
|
end
|
16
12
|
end
|
17
13
|
end
|
@@ -2,15 +2,37 @@ module Procedures::Packages
|
|
2
2
|
class LockingStatus < ForemanMaintain::Procedure
|
3
3
|
metadata do
|
4
4
|
for_feature :package_manager
|
5
|
-
description 'Check status of version locking of
|
5
|
+
description 'Check status of version locking of packages'
|
6
6
|
preparation_steps { [Checks::VersionLockingEnabled.new] }
|
7
7
|
end
|
8
8
|
|
9
9
|
def run
|
10
|
+
check_automatic_locking
|
11
|
+
check_version_locked
|
12
|
+
end
|
13
|
+
|
14
|
+
private
|
15
|
+
|
16
|
+
def check_version_locked
|
10
17
|
if feature(:package_manager).versions_locked?
|
11
|
-
puts 'Packages are locked.'
|
18
|
+
puts ' Packages are locked.'
|
19
|
+
else
|
20
|
+
puts ' Packages are not locked.'
|
21
|
+
puts " WARNING: When locking is disabled there is a risk of unwanted update\n" \
|
22
|
+
" of #{feature(:instance).product_name}' and its components and possible " \
|
23
|
+
'data inconsistency'
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def check_automatic_locking
|
28
|
+
if feature(:installer).lock_package_versions_supported?
|
29
|
+
if feature(:installer).lock_package_versions?
|
30
|
+
puts ' Automatic locking of package versions is enabled in installer.'
|
31
|
+
else
|
32
|
+
puts ' Automatic locking of package versions is disabled in installer.'
|
33
|
+
end
|
12
34
|
else
|
13
|
-
puts '
|
35
|
+
puts ' Automatic locking of package versions is not supported by installer.'
|
14
36
|
end
|
15
37
|
end
|
16
38
|
end
|
@@ -2,7 +2,7 @@ module Procedures::Packages
|
|
2
2
|
class UnlockVersions < ForemanMaintain::Procedure
|
3
3
|
metadata do
|
4
4
|
for_feature :package_manager
|
5
|
-
description 'Unlock
|
5
|
+
description 'Unlock packages'
|
6
6
|
preparation_steps { [Checks::VersionLockingEnabled.new] }
|
7
7
|
end
|
8
8
|
|
@@ -3,16 +3,25 @@ module Procedures::Packages
|
|
3
3
|
metadata do
|
4
4
|
param :packages, 'List of packages to update', :array => true
|
5
5
|
param :assumeyes, 'Do not ask for confirmation'
|
6
|
+
param :force, 'Do not skip if package is installed', :flag => true, :default => false
|
7
|
+
param :warn_on_errors, 'Do not interrupt scenario on failure',
|
8
|
+
:flag => true, :default => false
|
6
9
|
end
|
7
10
|
|
8
11
|
def run
|
9
12
|
assumeyes_val = @assumeyes.nil? ? assumeyes? : @assumeyes
|
10
13
|
feature(:package_manager).clean_cache
|
11
14
|
packages_action(:update, @packages, :assumeyes => assumeyes_val)
|
15
|
+
rescue ForemanMaintain::Error::ExecutionError => e
|
16
|
+
if @warn_on_errors
|
17
|
+
set_status(:warning, e.message)
|
18
|
+
else
|
19
|
+
raise
|
20
|
+
end
|
12
21
|
end
|
13
22
|
|
14
23
|
def necessary?
|
15
|
-
@packages.any? { |package| package_version(package).nil? }
|
24
|
+
@force || @packages.any? { |package| package_version(package).nil? }
|
16
25
|
end
|
17
26
|
|
18
27
|
def description
|
@@ -0,0 +1,90 @@
|
|
1
|
+
module ForemanMaintain::Scenarios
|
2
|
+
module Packages
|
3
|
+
class Status < ForemanMaintain::Scenario
|
4
|
+
metadata do
|
5
|
+
label :packages_status
|
6
|
+
description 'detection of status of package version locking'
|
7
|
+
manual_detection
|
8
|
+
end
|
9
|
+
|
10
|
+
def compose
|
11
|
+
add_step(Procedures::Packages::LockingStatus)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
class Unlock < ForemanMaintain::Scenario
|
16
|
+
metadata do
|
17
|
+
label :packages_unlock
|
18
|
+
description 'unlocking of package versions'
|
19
|
+
manual_detection
|
20
|
+
end
|
21
|
+
|
22
|
+
def compose
|
23
|
+
add_step(Procedures::Packages::UnlockVersions)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
class Lock < ForemanMaintain::Scenario
|
28
|
+
metadata do
|
29
|
+
label :packages_lock
|
30
|
+
description 'locking of package versions'
|
31
|
+
manual_detection
|
32
|
+
end
|
33
|
+
|
34
|
+
def compose
|
35
|
+
add_step(Procedures::Packages::LockVersions)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
class Install < ForemanMaintain::Scenario
|
40
|
+
metadata do
|
41
|
+
description 'install packages in unlocked session'
|
42
|
+
param :packages, 'List of packages to install', :array => true
|
43
|
+
param :assumeyes, 'Do not ask for confirmation'
|
44
|
+
manual_detection
|
45
|
+
end
|
46
|
+
|
47
|
+
def compose
|
48
|
+
add_step_with_context(Procedures::Packages::InstallerConfirmation)
|
49
|
+
add_step_with_context(Procedures::Packages::UnlockVersions)
|
50
|
+
add_step_with_context(Procedures::Packages::Install,
|
51
|
+
:force => true, :warn_on_errors => true)
|
52
|
+
add_step_with_context(Procedures::Installer::Run,
|
53
|
+
:arguments => '--upgrade --disable-system-checks')
|
54
|
+
add_step(Procedures::Packages::LockingStatus)
|
55
|
+
end
|
56
|
+
|
57
|
+
def set_context_mapping
|
58
|
+
context.map(:packages,
|
59
|
+
Procedures::Packages::Install => :packages)
|
60
|
+
context.map(:assumeyes,
|
61
|
+
Procedures::Packages::Install => :assumeyes)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
class Update < ForemanMaintain::Scenario
|
66
|
+
metadata do
|
67
|
+
description 'update packages in unlocked session'
|
68
|
+
param :packages, 'List of packages to Update', :array => true
|
69
|
+
param :assumeyes, 'Do not ask for confirmation'
|
70
|
+
manual_detection
|
71
|
+
end
|
72
|
+
|
73
|
+
def compose
|
74
|
+
add_step_with_context(Procedures::Packages::InstallerConfirmation)
|
75
|
+
add_step_with_context(Procedures::Packages::UnlockVersions)
|
76
|
+
add_step_with_context(Procedures::Packages::Update, :force => true, :warn_on_errors => true)
|
77
|
+
add_step_with_context(Procedures::Installer::Run,
|
78
|
+
:arguments => '--upgrade --disable-system-checks')
|
79
|
+
add_step(Procedures::Packages::LockingStatus)
|
80
|
+
end
|
81
|
+
|
82
|
+
def set_context_mapping
|
83
|
+
context.map(:packages,
|
84
|
+
Procedures::Packages::Update => :packages)
|
85
|
+
context.map(:assumeyes,
|
86
|
+
Procedures::Packages::Update => :assumeyes)
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/bin/bash
|
2
|
+
function quit_if_in_maintenance_mode {
|
3
|
+
/usr/bin/foreman-maintain maintenance-mode is-enabled
|
4
|
+
if [ $? -eq 0 ]
|
5
|
+
then
|
6
|
+
echo "Maintenance mode is on can not continue"
|
7
|
+
exit 1
|
8
|
+
fi
|
9
|
+
}
|
10
|
+
|
11
|
+
function is_maintenance_mode_enabled {
|
12
|
+
/usr/bin/foreman-maintain maintenance-mode is-enabled
|
13
|
+
echo $?
|
14
|
+
}
|
@@ -0,0 +1,85 @@
|
|
1
|
+
from yum.plugins import PluginYumExit
|
2
|
+
from yum.plugins import TYPE_CORE
|
3
|
+
from rpmUtils.miscutils import splitFilename
|
4
|
+
from yum.packageSack import packagesNewestByName
|
5
|
+
|
6
|
+
import urlgrabber
|
7
|
+
import urlgrabber.grabber
|
8
|
+
|
9
|
+
import os
|
10
|
+
import fnmatch
|
11
|
+
import tempfile
|
12
|
+
import time
|
13
|
+
|
14
|
+
requires_api_version = '2.1'
|
15
|
+
plugin_type = (TYPE_CORE,)
|
16
|
+
|
17
|
+
_package_whitelist = set()
|
18
|
+
fileurl = None
|
19
|
+
|
20
|
+
def _load_whitelist():
|
21
|
+
try:
|
22
|
+
if fileurl:
|
23
|
+
llfile = urlgrabber.urlopen(fileurl)
|
24
|
+
for line in llfile.readlines():
|
25
|
+
if line.startswith('#') or line.strip() == '':
|
26
|
+
continue
|
27
|
+
_package_whitelist.add(line.rstrip().lower())
|
28
|
+
llfile.close()
|
29
|
+
except urlgrabber.grabber.URLGrabError, e:
|
30
|
+
raise PluginYumExit('Unable to read Foreman protector"s configuration: %s' % e)
|
31
|
+
|
32
|
+
def _add_obsoletes(conduit):
|
33
|
+
if _package_whitelist:
|
34
|
+
# If anything obsoletes something that we have whitelisted ... then
|
35
|
+
# whitelist that too.
|
36
|
+
for (pkgtup, instTup) in conduit._base.up.getObsoletesTuples():
|
37
|
+
if instTup[0] not in _package_whitelist:
|
38
|
+
continue
|
39
|
+
_package_whitelist.add(pkgtup[0].lower())
|
40
|
+
|
41
|
+
def _get_updates(base):
|
42
|
+
updates = {}
|
43
|
+
|
44
|
+
for p in base.pkgSack.returnNewestByName():
|
45
|
+
if p.name in _package_whitelist:
|
46
|
+
# This one is whitelisted, skip
|
47
|
+
continue
|
48
|
+
updates[p.name] = p
|
49
|
+
|
50
|
+
return updates
|
51
|
+
|
52
|
+
def config_hook(conduit):
|
53
|
+
global fileurl
|
54
|
+
|
55
|
+
fileurl = conduit.confString('main', 'whitelist')
|
56
|
+
|
57
|
+
def _add_package_whitelist_excluders(conduit):
|
58
|
+
if hasattr(conduit, 'registerPackageName'):
|
59
|
+
conduit.registerPackageName("yum-plugin-foreman-protector")
|
60
|
+
ape = conduit._base.pkgSack.addPackageExcluder
|
61
|
+
exid = 'foreman-protector.W.'
|
62
|
+
ape(None, exid + str(1), 'mark.washed')
|
63
|
+
ape(None, exid + str(2), 'wash.name.in', _package_whitelist)
|
64
|
+
ape(None, exid + str(3), 'exclude.marked')
|
65
|
+
|
66
|
+
def exclude_hook(conduit):
|
67
|
+
conduit.info(3, 'Reading Foreman protector configuration')
|
68
|
+
|
69
|
+
_load_whitelist()
|
70
|
+
_add_obsoletes(conduit)
|
71
|
+
|
72
|
+
total = len(_get_updates(conduit._base))
|
73
|
+
conduit.info(3, '*** Excluded total: %s' % total)
|
74
|
+
if total:
|
75
|
+
if total > 1:
|
76
|
+
suffix = 's'
|
77
|
+
else:
|
78
|
+
suffix = ''
|
79
|
+
conduit.info(1, '\n'
|
80
|
+
'WARNING: Excluding %d update%s due to foreman-protector. \n'
|
81
|
+
'Use foreman-maintain packages install/update <package> \n'
|
82
|
+
'to safely install packages without restrictions.\n'
|
83
|
+
% (total, suffix))
|
84
|
+
|
85
|
+
_add_package_whitelist_excluders(conduit)
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# list of packages allowed to be installed and updated freely
|
2
|
+
# foreman-maintain runtime installed deps
|
3
|
+
# fio and deps
|
4
|
+
fio
|
5
|
+
libaio
|
6
|
+
libibverbs
|
7
|
+
librbd1
|
8
|
+
libpmem
|
9
|
+
numactl-libs
|
10
|
+
ndctl-libs
|
11
|
+
daxctl-libs
|
12
|
+
libpmemblk
|
13
|
+
librados2
|
14
|
+
librdmacm
|
15
|
+
rdma-core
|
16
|
+
boost-random
|
17
|
+
boost-iostreams
|
18
|
+
# foreman-maintain
|
19
|
+
rubygem-foreman_maintain
|
@@ -1,32 +1,60 @@
|
|
1
1
|
module ForemanMaintain
|
2
2
|
module Cli
|
3
3
|
class PackagesCommand < Base
|
4
|
-
subcommand 'lock', 'Prevent
|
4
|
+
subcommand 'lock', 'Prevent packages from automatic update' do
|
5
5
|
interactive_option
|
6
6
|
def execute
|
7
|
-
run_scenarios_and_exit(Scenarios::
|
7
|
+
run_scenarios_and_exit(Scenarios::Packages::Lock.new)
|
8
8
|
end
|
9
9
|
end
|
10
10
|
|
11
|
-
subcommand 'unlock', 'Enable
|
11
|
+
subcommand 'unlock', 'Enable packages for automatic update' do
|
12
12
|
interactive_option
|
13
13
|
def execute
|
14
|
-
run_scenarios_and_exit(Scenarios::
|
14
|
+
run_scenarios_and_exit(Scenarios::Packages::Unlock.new)
|
15
15
|
end
|
16
16
|
end
|
17
17
|
|
18
|
-
subcommand 'status', 'Check if
|
18
|
+
subcommand 'status', 'Check if packages are protected against update' do
|
19
19
|
interactive_option
|
20
20
|
def execute
|
21
|
-
run_scenarios_and_exit(Scenarios::
|
21
|
+
run_scenarios_and_exit(Scenarios::Packages::Status.new)
|
22
22
|
end
|
23
23
|
end
|
24
24
|
|
25
|
-
subcommand '
|
25
|
+
subcommand 'install', 'Install packages in an unlocked session' do
|
26
|
+
interactive_option
|
27
|
+
parameter 'PACKAGES ...', 'packages to install', :attribute_name => :packages
|
28
|
+
|
29
|
+
def execute
|
30
|
+
run_scenarios_and_exit(
|
31
|
+
Scenarios::Packages::Install.new(
|
32
|
+
:packages => packages,
|
33
|
+
:assumeyes => assumeyes?
|
34
|
+
)
|
35
|
+
)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
subcommand 'update', 'Update packages in an unlocked session' do
|
40
|
+
interactive_option
|
41
|
+
parameter 'PACKAGES ...', 'packages to update', :attribute_name => :packages
|
42
|
+
|
43
|
+
def execute
|
44
|
+
run_scenarios_and_exit(
|
45
|
+
Scenarios::Packages::Update.new(
|
46
|
+
:packages => packages,
|
47
|
+
:assumeyes => assumeyes?
|
48
|
+
)
|
49
|
+
)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
subcommand 'is-locked', 'Check if update of packages is allowed' do
|
26
54
|
interactive_option
|
27
55
|
def execute
|
28
56
|
locked = feature(:package_manager).versions_locked?
|
29
|
-
puts "
|
57
|
+
puts "Packages are#{locked ? '' : ' not'} locked"
|
30
58
|
exit_code = locked ? 0 : 1
|
31
59
|
exit exit_code
|
32
60
|
end
|
data/lib/foreman_maintain/cli.rb
CHANGED
@@ -21,7 +21,7 @@ module ForemanMaintain
|
|
21
21
|
subcommand 'service', 'Control applicable services', ServiceCommand
|
22
22
|
subcommand 'backup', 'Backup server', BackupCommand
|
23
23
|
subcommand 'restore', 'Restore a backup', RestoreCommand
|
24
|
-
subcommand 'packages', 'Lock/Unlock
|
24
|
+
subcommand 'packages', 'Lock/Unlock package protection, install, update', PackagesCommand
|
25
25
|
subcommand 'advanced', 'Advanced tools for server maintenance', AdvancedCommand
|
26
26
|
subcommand 'maintenance-mode', 'Control maintenance-mode for application',
|
27
27
|
MaintenanceModeCommand
|
@@ -1,23 +1,13 @@
|
|
1
1
|
module ForemanMaintain::PackageManager
|
2
2
|
# rubocop:disable Lint/UnusedMethodArgument
|
3
3
|
class Base
|
4
|
-
def foreman_related_packages
|
5
|
-
raise NotImplementedError
|
6
|
-
end
|
7
|
-
|
8
|
-
# list of packages providing the version locking
|
9
|
-
def version_locking_packages
|
10
|
-
raise NotImplementedError
|
11
|
-
end
|
12
|
-
|
13
4
|
# check tools are installed and enabled
|
14
5
|
def version_locking_enabled?
|
15
6
|
raise NotImplementedError
|
16
7
|
end
|
17
8
|
|
18
9
|
# make sure the version locking tools are configured
|
19
|
-
|
20
|
-
def configure_version_locking
|
10
|
+
def install_version_locking(assumeyes: false)
|
21
11
|
raise NotImplementedError
|
22
12
|
end
|
23
13
|
|
@@ -46,8 +36,8 @@ module ForemanMaintain::PackageManager
|
|
46
36
|
raise NotImplementedError
|
47
37
|
end
|
48
38
|
|
49
|
-
# prevent
|
50
|
-
def lock_versions
|
39
|
+
# prevent selected packages from update or install
|
40
|
+
def lock_versions
|
51
41
|
raise NotImplementedError
|
52
42
|
end
|
53
43
|
|
@@ -1,77 +1,36 @@
|
|
1
1
|
module ForemanMaintain::PackageManager
|
2
2
|
class Yum < Base
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
VERSIONLOCK_DEFAULT_LIST_FILE = '/etc/yum/pluginconf.d/versionlock.list'.freeze
|
3
|
+
PROTECTOR_CONFIG_FILE = '/etc/yum/pluginconf.d/foreman-protector.conf'.freeze
|
4
|
+
PROTECTOR_WHITELIST_FILE = '/etc/yum/pluginconf.d/foreman-protector.whitelist'.freeze
|
5
|
+
PROTECTOR_PLUGIN_FILE = '/usr/lib/yum-plugins/foreman-protector.py'.freeze
|
7
6
|
|
8
7
|
def self.parse_envra(envra)
|
9
8
|
# envra format: 0:foreman-1.20.1.10-1.el7sat.noarch
|
10
9
|
parsed = envra.match(/\d*:?(?<name>.*)-[^-]+-[^-]+\.[^.]+/)
|
11
|
-
parsed ? Hash[parsed.names.zip(parsed.captures)].merge(:envra => envra) : nil
|
10
|
+
parsed ? Hash[parsed.names.map(&:to_sym).zip(parsed.captures)].merge(:envra => envra) : nil
|
12
11
|
end
|
13
12
|
|
14
|
-
def
|
15
|
-
|
16
|
-
foreman_repo = sys.execute(query).split[1]
|
17
|
-
query_installed = "repoquery -a --qf='%{envra}' --repoid='#{foreman_repo}'"
|
18
|
-
sys.execute(query_installed). split("\n").map do |pkg|
|
19
|
-
self.class.parse_envra(pkg)
|
20
|
-
end
|
21
|
-
end
|
22
|
-
|
23
|
-
def version_locking_packages
|
24
|
-
%w[yum-utils yum-plugin-versionlock]
|
25
|
-
end
|
26
|
-
|
27
|
-
def lock_versions(package_list)
|
28
|
-
unlock_versions
|
29
|
-
File.open(versionlock_file, 'a') do |f|
|
30
|
-
f.puts VERSIONLOCK_START_CLAUSE
|
31
|
-
f.puts '# The following packages are locked by foreman-maintain. Do not modify!'
|
32
|
-
package_list.each { |package| f.puts "#{package[:envra]}.*" }
|
33
|
-
f.puts '# End of list of packages locked by foreman-maintain'
|
34
|
-
f.puts VERSIONLOCK_END_CLAUSE
|
35
|
-
end
|
13
|
+
def lock_versions
|
14
|
+
enable_protector
|
36
15
|
end
|
37
16
|
|
38
17
|
def unlock_versions
|
39
|
-
|
40
|
-
content = File.read(lock_file)
|
41
|
-
content = content.gsub(/#{VERSIONLOCK_START_CLAUSE}.*#{VERSIONLOCK_END_CLAUSE}\n/m, '')
|
42
|
-
File.open(lock_file, 'w') { |f| f.write content }
|
18
|
+
disable_protector
|
43
19
|
end
|
44
20
|
|
45
21
|
def versions_locked?
|
46
|
-
|
47
|
-
return false if lock_file.nil?
|
48
|
-
content = File.read(lock_file)
|
49
|
-
!!content.match(/#{VERSIONLOCK_START_CLAUSE}.*#{VERSIONLOCK_END_CLAUSE}\n/m)
|
22
|
+
!!(protector_config =~ /^\s*enabled\s*=\s*1/)
|
50
23
|
end
|
51
24
|
|
52
25
|
def version_locking_enabled?
|
53
|
-
|
54
|
-
|
26
|
+
File.exist?(PROTECTOR_PLUGIN_FILE) && File.exist?(PROTECTOR_CONFIG_FILE) &&
|
27
|
+
File.exist?(PROTECTOR_WHITELIST_FILE)
|
55
28
|
end
|
56
29
|
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
def configure_version_locking
|
62
|
-
config = versionlock_config
|
63
|
-
config += "\n" unless config[-1] == "\n"
|
64
|
-
enabled_re = /^\s*enabled\s*=.*$/
|
65
|
-
if enabled_re.match(config)
|
66
|
-
config = config.gsub(enabled_re, 'enabled = 1')
|
67
|
-
else
|
68
|
-
config += "enabled = 1\n"
|
69
|
-
end
|
70
|
-
unless config =~ /^\s*locklist\s*=.*$/
|
71
|
-
config += "locklist = #{VERSIONLOCK_DEFAULT_LIST_FILE}\n"
|
72
|
-
end
|
73
|
-
File.open(versionlock_config_file, 'w') { |file| file.puts config }
|
74
|
-
FileUtils.touch(versionlock_file)
|
30
|
+
def install_version_locking(*)
|
31
|
+
install_extras('foreman_protector/foreman-protector.py', PROTECTOR_PLUGIN_FILE)
|
32
|
+
install_extras('foreman_protector/foreman-protector.conf', PROTECTOR_CONFIG_FILE)
|
33
|
+
install_extras('foreman_protector/foreman-protector.whitelist', PROTECTOR_WHITELIST_FILE)
|
75
34
|
end
|
76
35
|
|
77
36
|
def installed?(packages)
|
@@ -109,17 +68,32 @@ module ForemanMaintain::PackageManager
|
|
109
68
|
|
110
69
|
private
|
111
70
|
|
112
|
-
def
|
113
|
-
File.exist?(
|
71
|
+
def protector_config
|
72
|
+
File.exist?(protector_config_file) ? File.read(protector_config_file) : ''
|
114
73
|
end
|
115
74
|
|
116
|
-
def
|
117
|
-
|
75
|
+
def protector_config_file
|
76
|
+
PROTECTOR_CONFIG_FILE
|
118
77
|
end
|
119
78
|
|
120
|
-
def
|
121
|
-
|
122
|
-
|
79
|
+
def enable_protector
|
80
|
+
setup_protector(true)
|
81
|
+
end
|
82
|
+
|
83
|
+
def disable_protector
|
84
|
+
setup_protector(false)
|
85
|
+
end
|
86
|
+
|
87
|
+
def setup_protector(enabled)
|
88
|
+
config = protector_config
|
89
|
+
config += "\n" unless config[-1] == "\n"
|
90
|
+
enabled_re = /^\s*enabled\s*=.*$/
|
91
|
+
if enabled_re.match(config)
|
92
|
+
config = config.gsub(enabled_re, "enabled = #{enabled ? '1' : '0'}")
|
93
|
+
else
|
94
|
+
config += "enabled = #{enabled ? '1' : '0'}\n"
|
95
|
+
end
|
96
|
+
File.open(protector_config_file, 'w') { |file| file.puts config }
|
123
97
|
end
|
124
98
|
|
125
99
|
def yum_action(action, packages, assumeyes: false)
|
@@ -131,5 +105,14 @@ module ForemanMaintain::PackageManager
|
|
131
105
|
sys.execute!("yum#{yum_options_s} #{action}#{packages_s}",
|
132
106
|
:interactive => true)
|
133
107
|
end
|
108
|
+
|
109
|
+
def install_extras(src, dest, override: false)
|
110
|
+
extras_src = File.expand_path('../../../../extras', __FILE__)
|
111
|
+
if override ||
|
112
|
+
(File.directory?(dest) && !File.exist?(File.join(dest, src))) ||
|
113
|
+
!File.exist?(dest)
|
114
|
+
FileUtils.cp(File.join(extras_src, src), dest)
|
115
|
+
end
|
116
|
+
end
|
134
117
|
end
|
135
118
|
end
|
@@ -16,6 +16,7 @@ module ForemanMaintain
|
|
16
16
|
@scenarios = Array(scenarios)
|
17
17
|
@quit = false
|
18
18
|
@last_scenario = nil
|
19
|
+
@last_scenario_continuation_confirmed = false
|
19
20
|
@exit_code = 0
|
20
21
|
end
|
21
22
|
|
@@ -40,18 +41,19 @@ module ForemanMaintain
|
|
40
41
|
end
|
41
42
|
end
|
42
43
|
|
43
|
-
def run_scenario(scenario
|
44
|
+
def run_scenario(scenario)
|
44
45
|
return if scenario.steps.empty?
|
45
46
|
raise 'The runner is already in quit state' if quit?
|
46
47
|
|
47
|
-
|
48
|
-
|
49
|
-
return if quit?
|
50
|
-
end
|
48
|
+
confirm_scenario(scenario)
|
49
|
+
return if quit?
|
51
50
|
|
52
51
|
execute_scenario_steps(scenario)
|
53
52
|
ensure
|
54
|
-
|
53
|
+
unless scenario.steps.empty?
|
54
|
+
@last_scenario = scenario
|
55
|
+
@last_scenario_continuation_confirmed = false
|
56
|
+
end
|
55
57
|
@exit_code = 1 if scenario.failed?
|
56
58
|
end
|
57
59
|
|
@@ -60,12 +62,13 @@ module ForemanMaintain
|
|
60
62
|
end
|
61
63
|
|
62
64
|
def confirm_scenario(scenario)
|
63
|
-
return
|
65
|
+
return if @last_scenario.nil? || @last_scenario_continuation_confirmed
|
64
66
|
|
65
67
|
decision = if @last_scenario.steps_with_error(:whitelisted => false).any? ||
|
66
68
|
@last_scenario.steps_with_abort(:whitelisted => false).any?
|
67
69
|
:quit
|
68
70
|
elsif @last_scenario.steps_with_warning(:whitelisted => false).any?
|
71
|
+
@last_scenario_continuation_confirmed = true
|
69
72
|
reporter.ask_decision("Continue with [#{scenario.description}]")
|
70
73
|
end
|
71
74
|
|
@@ -94,7 +97,7 @@ module ForemanMaintain
|
|
94
97
|
|
95
98
|
def execute_scenario_steps(scenario, force = false)
|
96
99
|
scenario.before_scenarios.flatten.each { |before_scenario| run_scenario(before_scenario) }
|
97
|
-
confirm_scenario(scenario)
|
100
|
+
confirm_scenario(scenario)
|
98
101
|
return if !force && quit? # the before scenarios caused the stop of the execution
|
99
102
|
|
100
103
|
@reporter.before_scenario_starts(scenario)
|
@@ -135,7 +135,7 @@ module ForemanMaintain
|
|
135
135
|
confirm_scenario(scenario)
|
136
136
|
return if quit?
|
137
137
|
self.phase = phase
|
138
|
-
run_scenario(scenario
|
138
|
+
run_scenario(scenario)
|
139
139
|
# if we started from the :pre_upgrade_checks, ensure to ask before
|
140
140
|
# continuing with the rest of the upgrade
|
141
141
|
@ask_to_confirm_upgrade = phase == :pre_upgrade_checks
|
@@ -155,12 +155,15 @@ module ForemanMaintain
|
|
155
155
|
|
156
156
|
private
|
157
157
|
|
158
|
+
# rubocop:disable Metrics/MethodLength
|
158
159
|
def rollback_pre_migrations
|
159
160
|
raise "Unexpected phase #{phase}, expecting pre_migrations" unless phase == :pre_migrations
|
160
161
|
rollback_needed = scenario(:pre_migrations).steps.any? { |s| s.executed? && s.success? }
|
161
162
|
if rollback_needed
|
162
163
|
@quit = false
|
163
|
-
|
164
|
+
# prevent the unnecessary confirmation questions
|
165
|
+
@last_scenario = nil
|
166
|
+
@last_scenario_continuation_confirmed = true
|
164
167
|
[:post_migrations, :post_upgrade_checks].each do |phase|
|
165
168
|
if quit? && phase == :post_upgrade_checks
|
166
169
|
self.phase = :pre_migrations
|
@@ -174,6 +177,7 @@ module ForemanMaintain
|
|
174
177
|
The upgrade failed and system was restored to pre-upgrade state.
|
175
178
|
MESSAGE
|
176
179
|
end
|
180
|
+
# rubocop:enable Metrics/MethodLength
|
177
181
|
|
178
182
|
def with_non_empty_scenario(phase)
|
179
183
|
next_scenario = scenario(phase)
|
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.4.
|
4
|
+
version: 0.4.8
|
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: 2019-
|
11
|
+
date: 2019-09-05 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: clamp
|
@@ -219,6 +219,7 @@ files:
|
|
219
219
|
- definitions/procedures/foreman_tasks/resume.rb
|
220
220
|
- definitions/procedures/foreman_tasks/ui_investigate.rb
|
221
221
|
- definitions/procedures/hammer_setup.rb
|
222
|
+
- definitions/procedures/installer/run.rb
|
222
223
|
- definitions/procedures/installer/upgrade.rb
|
223
224
|
- definitions/procedures/iptables/add_maintenance_mode_chain.rb
|
224
225
|
- definitions/procedures/iptables/remove_maintenance_mode_chain.rb
|
@@ -226,6 +227,7 @@ files:
|
|
226
227
|
- definitions/procedures/maintenance_mode/is_enabled.rb
|
227
228
|
- definitions/procedures/packages/enable_version_locking.rb
|
228
229
|
- definitions/procedures/packages/install.rb
|
230
|
+
- definitions/procedures/packages/installer_confirmation.rb
|
229
231
|
- definitions/procedures/packages/lock_versions.rb
|
230
232
|
- definitions/procedures/packages/locking_status.rb
|
231
233
|
- definitions/procedures/packages/unlock_versions.rb
|
@@ -261,6 +263,7 @@ files:
|
|
261
263
|
- definitions/procedures/sync_plans/enable.rb
|
262
264
|
- definitions/scenarios/backup.rb
|
263
265
|
- definitions/scenarios/maintenance_mode.rb
|
266
|
+
- definitions/scenarios/packages.rb
|
264
267
|
- definitions/scenarios/restore.rb
|
265
268
|
- definitions/scenarios/services.rb
|
266
269
|
- definitions/scenarios/upgrade_to_satellite_6_2.rb
|
@@ -273,7 +276,11 @@ files:
|
|
273
276
|
- definitions/scenarios/upgrade_to_satellite_6_5_z.rb
|
274
277
|
- definitions/scenarios/upgrade_to_satellite_6_6.rb
|
275
278
|
- definitions/scenarios/upgrade_to_satellite_6_6_z.rb
|
276
|
-
-
|
279
|
+
- extras/foreman-maintain.sh
|
280
|
+
- extras/foreman_protector/foreman-protector.conf
|
281
|
+
- extras/foreman_protector/foreman-protector.py
|
282
|
+
- extras/foreman_protector/foreman-protector.whitelist
|
283
|
+
- extras/passenger-recycler.cron
|
277
284
|
- lib/foreman_maintain.rb
|
278
285
|
- lib/foreman_maintain/check.rb
|
279
286
|
- lib/foreman_maintain/cli.rb
|
@@ -1,39 +0,0 @@
|
|
1
|
-
module ForemanMaintain::Scenarios
|
2
|
-
module VersionLocking
|
3
|
-
class Status < ForemanMaintain::Scenario
|
4
|
-
metadata do
|
5
|
-
label :version_locking_status
|
6
|
-
description 'detection of status of package version locking'
|
7
|
-
manual_detection
|
8
|
-
end
|
9
|
-
|
10
|
-
def compose
|
11
|
-
add_step(Procedures::Packages::LockingStatus)
|
12
|
-
end
|
13
|
-
end
|
14
|
-
|
15
|
-
class Unlock < ForemanMaintain::Scenario
|
16
|
-
metadata do
|
17
|
-
label :version_locking_unlock
|
18
|
-
description 'unlocking of package versions'
|
19
|
-
manual_detection
|
20
|
-
end
|
21
|
-
|
22
|
-
def compose
|
23
|
-
add_step(Procedures::Packages::UnlockVersions)
|
24
|
-
end
|
25
|
-
end
|
26
|
-
|
27
|
-
class Lock < ForemanMaintain::Scenario
|
28
|
-
metadata do
|
29
|
-
label :version_locking_lock
|
30
|
-
description 'locking of package versions'
|
31
|
-
manual_detection
|
32
|
-
end
|
33
|
-
|
34
|
-
def compose
|
35
|
-
add_step(Procedures::Packages::LockVersions)
|
36
|
-
end
|
37
|
-
end
|
38
|
-
end
|
39
|
-
end
|