foreman_maintain 1.12.2 → 1.12.4
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/container/podman_login.rb +1 -1
- data/definitions/features/iop.rb +44 -0
- data/definitions/features/service.rb +4 -58
- data/definitions/features/timer.rb +96 -0
- data/definitions/procedures/iop/image_prune.rb +19 -0
- data/definitions/procedures/iop/update.rb +25 -0
- data/definitions/procedures/timer/start.rb +20 -0
- data/definitions/procedures/timer/stop.rb +20 -0
- data/definitions/reports/bookmarks.rb +51 -0
- data/definitions/reports/disconnected_environment.rb +23 -0
- data/definitions/reports/grouping.rb +58 -0
- data/definitions/reports/lab_features.rb +19 -0
- data/definitions/reports/personal_access_token.rb +26 -0
- data/definitions/reports/selinux.rb +27 -0
- data/definitions/reports/webhooks.rb +37 -0
- data/definitions/scenarios/foreman_upgrade.rb +2 -0
- data/definitions/scenarios/maintenance_mode.rb +2 -0
- data/definitions/scenarios/restore.rb +3 -0
- data/definitions/scenarios/satellite_upgrade.rb +4 -0
- data/definitions/scenarios/update.rb +5 -1
- data/lib/foreman_maintain/cli/base.rb +3 -3
- data/lib/foreman_maintain/concerns/systemd.rb +50 -0
- data/lib/foreman_maintain/feature.rb +9 -0
- data/lib/foreman_maintain/utils/service/systemd.rb +7 -2
- data/lib/foreman_maintain/version.rb +1 -1
- metadata +13 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a99136516325dc9d13b83ddca246f197ecc63199d74cd950a582946a503fc2eb
|
4
|
+
data.tar.gz: c59533ff3ae35ffdece203759eef0d16b3bc67fc9bd5146b32a0988c00821770
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d2f883dfe1a9959d2790d856be260073a57a874d3d3e0fadd2c54b4eef1dbaf4979fe20070ffde6699dd2ff86dcb8733426f1eaa4a7213f1c1493a419678bc58
|
7
|
+
data.tar.gz: 00a9ee5352ef84dd5b36451e862eedb070806e2b6233042cc35b72a1cd43c6c483b163246f301f833f4c2f3f6edf8bfbd1379463c9cf0060adef2e3ec6fb7258
|
@@ -5,7 +5,7 @@ module Checks::Container
|
|
5
5
|
confine do
|
6
6
|
feature(:satellite)&.connected? && feature(:containers)
|
7
7
|
end
|
8
|
-
description 'Check whether podman
|
8
|
+
description 'Check whether podman needs to be logged in to the registry'
|
9
9
|
tags :pre_upgrade
|
10
10
|
end
|
11
11
|
|
data/definitions/features/iop.rb
CHANGED
@@ -8,6 +8,22 @@ class Features::Iop < ForemanMaintain::Feature
|
|
8
8
|
end
|
9
9
|
end
|
10
10
|
|
11
|
+
CONTAINER_NAMES =
|
12
|
+
[
|
13
|
+
'insights-engine',
|
14
|
+
'gateway',
|
15
|
+
'host-inventory',
|
16
|
+
'ingress',
|
17
|
+
'puptoo',
|
18
|
+
'yuptoo',
|
19
|
+
'advisor-backend',
|
20
|
+
'advisor-frontend',
|
21
|
+
'remediations',
|
22
|
+
'vmaas',
|
23
|
+
'vulnerability-engine',
|
24
|
+
'vulnerability-frontend',
|
25
|
+
].freeze
|
26
|
+
|
11
27
|
def config_files
|
12
28
|
[
|
13
29
|
'/var/lib/containers/storage/volumes/iop-core-kafka-data',
|
@@ -42,4 +58,32 @@ class Features::Iop < ForemanMaintain::Feature
|
|
42
58
|
]
|
43
59
|
end
|
44
60
|
# rubocop:enable Metrics/MethodLength
|
61
|
+
|
62
|
+
def timers
|
63
|
+
[
|
64
|
+
system_service('iop-service-vuln-vmaas-sync.timer', 20),
|
65
|
+
]
|
66
|
+
end
|
67
|
+
|
68
|
+
def container_base
|
69
|
+
if feature(:instance).downstream
|
70
|
+
'registry.redhat.io/satellite'
|
71
|
+
else
|
72
|
+
'quay.io/iop'
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
def container_names
|
77
|
+
if feature(:instance).downstream
|
78
|
+
CONTAINER_NAMES.map { |container_name| "#{container_name}-rhel9" }
|
79
|
+
else
|
80
|
+
CONTAINER_NAMES
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
def container_images(container_version)
|
85
|
+
container_names.map do |container_name|
|
86
|
+
"#{container_base}/#{container_name}:#{container_version}"
|
87
|
+
end
|
88
|
+
end
|
45
89
|
end
|
@@ -1,4 +1,7 @@
|
|
1
|
+
require 'foreman_maintain/concerns/systemd'
|
2
|
+
|
1
3
|
class Features::Service < ForemanMaintain::Feature
|
4
|
+
include ForemanMaintain::Concerns::Systemd
|
2
5
|
metadata do
|
3
6
|
label :service
|
4
7
|
end
|
@@ -13,10 +16,7 @@ class Features::Service < ForemanMaintain::Feature
|
|
13
16
|
|
14
17
|
def existing_services
|
15
18
|
ForemanMaintain.available_features.flat_map(&:services).
|
16
|
-
sort.
|
17
|
-
inject([]) do |pool, service| # uniq(&:to_s) for ruby 1.8.7
|
18
|
-
(pool.last.nil? || !pool.last.matches?(service)) ? pool << service : pool
|
19
|
-
end.
|
19
|
+
sort.uniq(&:to_s).
|
20
20
|
select(&:exist?)
|
21
21
|
end
|
22
22
|
|
@@ -30,14 +30,6 @@ class Features::Service < ForemanMaintain::Feature
|
|
30
30
|
Hash[services.sort_by { |k, _| k.to_i }.reverse]
|
31
31
|
end
|
32
32
|
|
33
|
-
def action_noun(action)
|
34
|
-
action_word_modified(action) + 'ing'
|
35
|
-
end
|
36
|
-
|
37
|
-
def action_past_tense(action)
|
38
|
-
action_word_modified(action) + 'ed'
|
39
|
-
end
|
40
|
-
|
41
33
|
def filter_disabled_services!(action, service_list)
|
42
34
|
if %w[start stop restart status].include?(action)
|
43
35
|
service_list.select! { |service| !service.respond_to?(:enabled?) || service.enabled? }
|
@@ -45,13 +37,6 @@ class Features::Service < ForemanMaintain::Feature
|
|
45
37
|
service_list
|
46
38
|
end
|
47
39
|
|
48
|
-
def unit_file_available?(name)
|
49
|
-
cmd = "systemctl --no-legend --no-pager list-unit-files --type=service #{name} |\
|
50
|
-
grep --word-regexp --quiet #{name}"
|
51
|
-
exit_status, = execute_with_status(cmd)
|
52
|
-
exit_status == 0
|
53
|
-
end
|
54
|
-
|
55
40
|
private
|
56
41
|
|
57
42
|
def use_system_service(action, options, spinner)
|
@@ -96,28 +81,6 @@ class Features::Service < ForemanMaintain::Feature
|
|
96
81
|
services_and_statuses.map! { |service, status| [service, status.value] }
|
97
82
|
end
|
98
83
|
|
99
|
-
def format_status(output, exit_code, options)
|
100
|
-
status = ''
|
101
|
-
if !options[:failing] || exit_code > 0
|
102
|
-
if options[:brief]
|
103
|
-
status += format_brief_status(exit_code)
|
104
|
-
elsif !(output.nil? || output.empty?)
|
105
|
-
status += "\n" + output
|
106
|
-
end
|
107
|
-
end
|
108
|
-
status
|
109
|
-
end
|
110
|
-
|
111
|
-
def format_brief_status(exit_code)
|
112
|
-
result = (exit_code == 0) ? reporter.status_label(:success) : reporter.status_label(:fail)
|
113
|
-
padding = reporter.max_length - reporter.last_line.to_s.length - 30
|
114
|
-
"#{' ' * padding} #{result}"
|
115
|
-
end
|
116
|
-
|
117
|
-
def allowed_action?(action)
|
118
|
-
%w[start stop restart status enable disable].include?(action)
|
119
|
-
end
|
120
|
-
|
121
84
|
def extend_service_list_with_sockets(service_list, options)
|
122
85
|
return service_list unless options[:include_sockets]
|
123
86
|
|
@@ -162,21 +125,4 @@ class Features::Service < ForemanMaintain::Feature
|
|
162
125
|
service_list.concat(unregistered_service_list)
|
163
126
|
service_list
|
164
127
|
end
|
165
|
-
|
166
|
-
def action_word_modified(action)
|
167
|
-
case action
|
168
|
-
when 'status'
|
169
|
-
'display'
|
170
|
-
when 'enable', 'disable'
|
171
|
-
action.chomp('e')
|
172
|
-
when 'stop'
|
173
|
-
action + 'p'
|
174
|
-
else
|
175
|
-
action
|
176
|
-
end
|
177
|
-
end
|
178
|
-
|
179
|
-
def exclude_services_only(options)
|
180
|
-
existing_services - filtered_services(options)
|
181
|
-
end
|
182
128
|
end
|
@@ -0,0 +1,96 @@
|
|
1
|
+
require 'foreman_maintain/concerns/systemd'
|
2
|
+
|
3
|
+
class Features::Timer < ForemanMaintain::Feature
|
4
|
+
include ForemanMaintain::Concerns::Systemd
|
5
|
+
metadata do
|
6
|
+
label :timer
|
7
|
+
end
|
8
|
+
|
9
|
+
def handle_timers(spinner, action, options = {})
|
10
|
+
# options is used to handle "exclude" and "only" i.e.
|
11
|
+
# { :only => ["httpd"] }
|
12
|
+
# { :exclude => ["pulp-workers", "tomcat"] }
|
13
|
+
use_system_timer(action, options, spinner)
|
14
|
+
end
|
15
|
+
|
16
|
+
def existing_timers
|
17
|
+
ForemanMaintain.available_features.flat_map(&:timers).
|
18
|
+
sort.uniq(&:to_s).
|
19
|
+
select(&:exist?)
|
20
|
+
end
|
21
|
+
|
22
|
+
def filtered_timers(options, action = '')
|
23
|
+
timers = filter_timers(existing_timers, options, action)
|
24
|
+
|
25
|
+
raise 'No timers found matching your parameters' unless timers.any?
|
26
|
+
return timers unless options[:reverse]
|
27
|
+
|
28
|
+
Hash[timers.sort_by { |k, _| k.to_i }.reverse]
|
29
|
+
end
|
30
|
+
|
31
|
+
def filter_disabled_timers!(action, timer_list)
|
32
|
+
if %w[start stop restart status].include?(action)
|
33
|
+
timer_list.select! { |timer| !timer.respond_to?(:enabled?) || timer.enabled? }
|
34
|
+
end
|
35
|
+
timer_list
|
36
|
+
end
|
37
|
+
|
38
|
+
private
|
39
|
+
|
40
|
+
def use_system_timer(action, options, spinner)
|
41
|
+
options[:reverse] = action == 'stop'
|
42
|
+
raise 'Unsupported action detected' unless allowed_action?(action)
|
43
|
+
|
44
|
+
status, failed_timers = run_action_on_timers(action, options, spinner)
|
45
|
+
|
46
|
+
spinner.update("All timers #{action_past_tense(action)}")
|
47
|
+
if action == 'status'
|
48
|
+
raise "Some timers are not running (#{failed_timers.join(', ')})" if status > 0
|
49
|
+
|
50
|
+
spinner.update('All timers are running')
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def run_action_on_timers(action, options, spinner)
|
55
|
+
status = 0
|
56
|
+
failed_timers = []
|
57
|
+
filtered_timers(options, action).each_value do |group|
|
58
|
+
fork_threads_for_timers(action, group, spinner).each do |timer, status_and_output|
|
59
|
+
spinner.update("#{action_noun(action)} #{timer}") if action == 'status'
|
60
|
+
item_status, output = status_and_output
|
61
|
+
formatted = format_status(output, item_status, options)
|
62
|
+
puts formatted unless formatted.empty?
|
63
|
+
|
64
|
+
if item_status > 0
|
65
|
+
status = item_status
|
66
|
+
failed_timers << timer
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
[status, failed_timers]
|
71
|
+
end
|
72
|
+
|
73
|
+
def fork_threads_for_timers(action, timers, spinner)
|
74
|
+
timers_and_statuses = []
|
75
|
+
timers.each do |timer|
|
76
|
+
spinner.update("#{action_noun(action)} #{timer}") if action != 'status'
|
77
|
+
timers_and_statuses << [timer, Thread.new { timer.send(action.to_sym) }]
|
78
|
+
end
|
79
|
+
timers_and_statuses.map! { |timer, status| [timer, status.value] }
|
80
|
+
end
|
81
|
+
|
82
|
+
def filter_timers(timer_list, options, action)
|
83
|
+
if options[:only]&.any?
|
84
|
+
timer_list = timer_list.select do |timer|
|
85
|
+
options[:only].any? { |opt| timer.matches?(opt) }
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
if options[:exclude]&.any?
|
90
|
+
timer_list = timer_list.reject { |timer| options[:exclude].include?(timer.name) }
|
91
|
+
end
|
92
|
+
|
93
|
+
timer_list = filter_disabled_timers!(action, timer_list)
|
94
|
+
timer_list.group_by(&:priority).to_h
|
95
|
+
end
|
96
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module Procedures::Iop
|
2
|
+
class ImagePrune < ForemanMaintain::Procedure
|
3
|
+
metadata do
|
4
|
+
description 'Prune unused IoP container images'
|
5
|
+
|
6
|
+
confine do
|
7
|
+
feature(:iop)
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
def run
|
12
|
+
prune_images
|
13
|
+
end
|
14
|
+
|
15
|
+
def prune_images
|
16
|
+
execute!("podman image prune --force")
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module Procedures::Iop
|
2
|
+
class Update < ForemanMaintain::Procedure
|
3
|
+
metadata do
|
4
|
+
description 'Update IoP containers'
|
5
|
+
|
6
|
+
confine do
|
7
|
+
feature(:iop) && (feature(:satellite)&.connected? || !feature(:satellite))
|
8
|
+
end
|
9
|
+
|
10
|
+
param :version,
|
11
|
+
'Version of the containers to pull',
|
12
|
+
:required => true
|
13
|
+
end
|
14
|
+
|
15
|
+
def run
|
16
|
+
pull_images
|
17
|
+
end
|
18
|
+
|
19
|
+
def pull_images
|
20
|
+
feature(:iop).container_images(@version).each do |container_image|
|
21
|
+
execute!("podman pull #{container_image}")
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module Procedures::Timer
|
2
|
+
class Start < ForemanMaintain::Procedure
|
3
|
+
metadata do
|
4
|
+
description 'Start systemd timers'
|
5
|
+
|
6
|
+
for_feature :timer
|
7
|
+
confine do
|
8
|
+
feature(:timer)&.existing_timers&.any?
|
9
|
+
end
|
10
|
+
|
11
|
+
tags :post_migrations
|
12
|
+
end
|
13
|
+
|
14
|
+
def run
|
15
|
+
with_spinner('Starting systemd timers') do |spinner|
|
16
|
+
feature(:timer).handle_timers(spinner, 'start')
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module Procedures::Timer
|
2
|
+
class Stop < ForemanMaintain::Procedure
|
3
|
+
metadata do
|
4
|
+
description 'Stop systemd timers'
|
5
|
+
|
6
|
+
for_feature :timer
|
7
|
+
confine do
|
8
|
+
feature(:timer)&.existing_timers&.any?
|
9
|
+
end
|
10
|
+
|
11
|
+
tags :pre_migrations
|
12
|
+
end
|
13
|
+
|
14
|
+
def run
|
15
|
+
with_spinner('Stopping systemd timers') do |spinner|
|
16
|
+
feature(:timer).handle_timers(spinner, 'stop')
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Reports
|
4
|
+
class Bookmarks < ForemanMaintain::Report
|
5
|
+
metadata do
|
6
|
+
description 'Report about bookmark usage'
|
7
|
+
end
|
8
|
+
|
9
|
+
def run
|
10
|
+
public_count = bookmarks_custom_public_count
|
11
|
+
private_count = bookmarks_custom_private_count
|
12
|
+
|
13
|
+
data_field('bookmarks_custom_public_count') { public_count }
|
14
|
+
data_field('bookmarks_custom_private_count') { private_count }
|
15
|
+
data_field('bookmarks_custom_count') { public_count + private_count }
|
16
|
+
end
|
17
|
+
|
18
|
+
private
|
19
|
+
|
20
|
+
def bookmarks_custom_public_count
|
21
|
+
# Count public bookmarks that are NOT owned by internal users
|
22
|
+
bookmarks_not_owned_by_internal_users(public: true)
|
23
|
+
end
|
24
|
+
|
25
|
+
def bookmarks_custom_private_count
|
26
|
+
# Count private bookmarks that are NOT owned by internal users
|
27
|
+
bookmarks_not_owned_by_internal_users(public: false)
|
28
|
+
end
|
29
|
+
|
30
|
+
def bookmarks_not_owned_by_internal_users(public:)
|
31
|
+
# Helper method to count bookmarks not owned by internal users with optional public filter
|
32
|
+
public_condition = public ? 'AND b.public = true' : 'AND b.public = false'
|
33
|
+
|
34
|
+
sql_count(
|
35
|
+
<<~SQL
|
36
|
+
bookmarks b
|
37
|
+
WHERE (
|
38
|
+
b.owner_type = 'User'
|
39
|
+
#{public_condition}
|
40
|
+
AND b.owner_id NOT IN (
|
41
|
+
SELECT u.id
|
42
|
+
FROM users u
|
43
|
+
INNER JOIN auth_sources a ON u.auth_source_id = a.id
|
44
|
+
WHERE a.type = 'AuthSourceHidden'
|
45
|
+
)
|
46
|
+
)
|
47
|
+
SQL
|
48
|
+
)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Reports
|
4
|
+
class DisconnectedEnvironment < ForemanMaintain::Report
|
5
|
+
metadata do
|
6
|
+
description 'Checks if the instance is in a disconnected environment'
|
7
|
+
end
|
8
|
+
|
9
|
+
def run
|
10
|
+
data_field('disconnected_environment') do
|
11
|
+
subscription_connection_setting = sql_setting('subscription_connection_enabled')
|
12
|
+
|
13
|
+
# If setting doesn't exist, assume connected (not disconnected)
|
14
|
+
if subscription_connection_setting.nil?
|
15
|
+
false
|
16
|
+
else
|
17
|
+
# disconnected when subscription_connection_enabled is false
|
18
|
+
YAML.safe_load(subscription_connection_setting) == false
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -4,6 +4,7 @@ module Reports
|
|
4
4
|
description 'Check how resources are grouped'
|
5
5
|
end
|
6
6
|
|
7
|
+
# rubocop:disable Metrics/AbcSize, Metrics/MethodLength
|
7
8
|
def run
|
8
9
|
self.data = {}
|
9
10
|
data_field('host_collections_count') { sql_count('katello_host_collections') }
|
@@ -26,6 +27,63 @@ module Reports
|
|
26
27
|
if table_exists('config_groups')
|
27
28
|
data_field('config_group_count') { sql_count('config_groups') }
|
28
29
|
end
|
30
|
+
|
31
|
+
data_field('usergroup_max_nesting_level') { usergroup_max_nesting_level }
|
32
|
+
|
33
|
+
usergroup_roles_stats = usergroup_roles_statistics
|
34
|
+
data['user_group_roles_max_count'] = usergroup_roles_stats[:max_count]
|
35
|
+
data['user_group_roles_min_count'] = usergroup_roles_stats[:min_count]
|
36
|
+
end
|
37
|
+
# rubocop:enable Metrics/AbcSize, Metrics/MethodLength
|
38
|
+
|
39
|
+
private
|
40
|
+
|
41
|
+
def usergroup_max_nesting_level
|
42
|
+
# Use recursive CTE to find maximum nesting level of usergroups
|
43
|
+
cte_sql = <<~SQL
|
44
|
+
WITH RECURSIVE usergroup_hierarchy AS (
|
45
|
+
-- Base case: root usergroups (not members of any other usergroup)
|
46
|
+
SELECT id, 1 as level
|
47
|
+
FROM usergroups
|
48
|
+
WHERE id NOT IN (
|
49
|
+
SELECT member_id
|
50
|
+
FROM usergroup_members
|
51
|
+
WHERE member_type = 'Usergroup'
|
52
|
+
)
|
53
|
+
UNION ALL
|
54
|
+
-- Recursive case: usergroups that are members of other usergroups
|
55
|
+
SELECT ug.id, uh.level + 1
|
56
|
+
FROM usergroups ug
|
57
|
+
INNER JOIN usergroup_members ugm ON ug.id = ugm.member_id
|
58
|
+
INNER JOIN usergroup_hierarchy uh ON ugm.usergroup_id = uh.id
|
59
|
+
WHERE ugm.member_type = 'Usergroup'
|
60
|
+
)
|
61
|
+
SQL
|
62
|
+
|
63
|
+
sql_as_count('COALESCE(MAX(level) - 1, 0)', 'usergroup_hierarchy', cte: cte_sql)
|
64
|
+
end
|
65
|
+
|
66
|
+
def usergroup_roles_statistics
|
67
|
+
# Query to get role counts per usergroup, including usergroups with 0 roles
|
68
|
+
roles_per_usergroup = query(
|
69
|
+
<<~SQL
|
70
|
+
SELECT ug.id, COALESCE(ur.role_count, 0) as role_count
|
71
|
+
FROM usergroups ug
|
72
|
+
LEFT JOIN (
|
73
|
+
SELECT owner_id, COUNT(*) as role_count
|
74
|
+
FROM user_roles
|
75
|
+
WHERE owner_type = 'Usergroup'
|
76
|
+
GROUP BY owner_id
|
77
|
+
) ur ON ug.id = ur.owner_id
|
78
|
+
SQL
|
79
|
+
)
|
80
|
+
|
81
|
+
if roles_per_usergroup.empty?
|
82
|
+
{ max_count: 0, min_count: 0 }
|
83
|
+
else
|
84
|
+
role_counts = roles_per_usergroup.map { |row| row['role_count'].to_i }
|
85
|
+
{ max_count: role_counts.max, min_count: role_counts.min }
|
86
|
+
end
|
29
87
|
end
|
30
88
|
end
|
31
89
|
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Reports
|
4
|
+
class LabFeatures < ForemanMaintain::Report
|
5
|
+
metadata do
|
6
|
+
description 'Checks if lab features are enabled'
|
7
|
+
end
|
8
|
+
|
9
|
+
def run
|
10
|
+
data_field('lab_features_enabled') do
|
11
|
+
lab_features_setting = sql_setting('lab_features')
|
12
|
+
return false if lab_features_setting.nil?
|
13
|
+
|
14
|
+
# Parse the YAML setting value and convert to boolean
|
15
|
+
YAML.safe_load(lab_features_setting) == true
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Reports
|
4
|
+
class PersonalAccessToken < ForemanMaintain::Report
|
5
|
+
metadata do
|
6
|
+
description 'Report about Personal Access Token usage'
|
7
|
+
end
|
8
|
+
|
9
|
+
def run
|
10
|
+
# Total count of non-revoked personal access tokens
|
11
|
+
data_field('pat_counts') do
|
12
|
+
sql_count('personal_access_tokens WHERE revoked = false')
|
13
|
+
end
|
14
|
+
|
15
|
+
# Count of tokens that were used recently (updated in last 2 months)
|
16
|
+
data_field('pat_recently_used_count') do
|
17
|
+
sql_count("personal_access_tokens WHERE updated_at >= NOW() - INTERVAL '2 months'")
|
18
|
+
end
|
19
|
+
|
20
|
+
# Count of revoked personal access tokens
|
21
|
+
data_field('revoked_pats_count') do
|
22
|
+
sql_count('personal_access_tokens WHERE revoked = true')
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Reports
|
4
|
+
class Selinux < ForemanMaintain::Report
|
5
|
+
metadata do
|
6
|
+
description 'Report about SELinux enforcement status'
|
7
|
+
end
|
8
|
+
|
9
|
+
def run
|
10
|
+
data_field('selinux_enforced') { selinux_enforced? }
|
11
|
+
end
|
12
|
+
|
13
|
+
private
|
14
|
+
|
15
|
+
def selinux_enforced?
|
16
|
+
# Check if getenforce command exists and SELinux is installed
|
17
|
+
return false unless command_present?('getenforce')
|
18
|
+
|
19
|
+
# Execute getenforce command and check if SELinux is enforcing
|
20
|
+
status = execute('getenforce').strip.downcase
|
21
|
+
status == 'enforcing'
|
22
|
+
rescue StandardError
|
23
|
+
# If any error occurs, assume SELinux is not enforced
|
24
|
+
false
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Reports
|
4
|
+
class Webhooks < ForemanMaintain::Report
|
5
|
+
metadata do
|
6
|
+
description 'Report about webhook usage'
|
7
|
+
end
|
8
|
+
|
9
|
+
def run
|
10
|
+
data_field('webhooks_enabled_count') { sql_count('webhooks WHERE enabled = true') }
|
11
|
+
data_field('webhooks_subscribed_events') { webhooks_subscribed_events }
|
12
|
+
data_field('shell_hooks_count') { sql_count('webhooks WHERE proxy_authorization = true') }
|
13
|
+
end
|
14
|
+
|
15
|
+
private
|
16
|
+
|
17
|
+
def webhooks_subscribed_events
|
18
|
+
# Extract all events from webhooks table and join into comma-separated string
|
19
|
+
events_data = query('SELECT events FROM webhooks')
|
20
|
+
|
21
|
+
all_events = []
|
22
|
+
events_data.each do |row|
|
23
|
+
events_value = row['events']
|
24
|
+
next if events_value.nil? || events_value.empty?
|
25
|
+
|
26
|
+
# Parse the array-like string - handle both JSON array format and simple arrays
|
27
|
+
# Remove outer brackets/braces, quotes, and split by comma
|
28
|
+
cleaned_value = events_value.gsub(/^[\[{]|[\]}]$/, '').gsub(/["']/, '')
|
29
|
+
parsed_events = cleaned_value.split(',').map(&:strip)
|
30
|
+
all_events.concat(parsed_events)
|
31
|
+
end
|
32
|
+
|
33
|
+
# Remove duplicates and join with commas
|
34
|
+
all_events.uniq.reject(&:empty?).join(',')
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -72,6 +72,7 @@ module Scenarios::Foreman
|
|
72
72
|
add_steps(
|
73
73
|
Procedures::MaintenanceMode::EnableMaintenanceMode,
|
74
74
|
Procedures::Crond::Stop,
|
75
|
+
Procedures::Timer::Stop,
|
75
76
|
Procedures::SyncPlans::Disable
|
76
77
|
)
|
77
78
|
end
|
@@ -113,6 +114,7 @@ module Scenarios::Foreman
|
|
113
114
|
Procedures::RefreshFeatures,
|
114
115
|
Procedures::Service::Start,
|
115
116
|
Procedures::Crond::Start,
|
117
|
+
Procedures::Timer::Start,
|
116
118
|
Procedures::SyncPlans::Enable,
|
117
119
|
Procedures::MaintenanceMode::DisableMaintenanceMode
|
118
120
|
)
|
@@ -10,6 +10,7 @@ module ForemanMaintain::Scenarios
|
|
10
10
|
def compose
|
11
11
|
add_step(Procedures::MaintenanceMode::EnableMaintenanceMode)
|
12
12
|
add_step(Procedures::Crond::Stop)
|
13
|
+
add_step(Procedures::Timer::Stop)
|
13
14
|
add_step(Procedures::SyncPlans::Disable)
|
14
15
|
end
|
15
16
|
end
|
@@ -25,6 +26,7 @@ module ForemanMaintain::Scenarios
|
|
25
26
|
def compose
|
26
27
|
add_step(Procedures::SyncPlans::Enable)
|
27
28
|
add_step(Procedures::Crond::Start)
|
29
|
+
add_step(Procedures::Timer::Start)
|
28
30
|
add_step(Procedures::MaintenanceMode::DisableMaintenanceMode)
|
29
31
|
end
|
30
32
|
end
|
@@ -29,6 +29,7 @@ module ForemanMaintain::Scenarios
|
|
29
29
|
Procedures::Restore::RequiredPackages,
|
30
30
|
Procedures::Restore::Configs)
|
31
31
|
add_step_with_context(Procedures::Crond::Stop)
|
32
|
+
add_step_with_context(Procedures::Timer::Stop)
|
32
33
|
unless backup.incremental?
|
33
34
|
add_steps_with_context(Procedures::Restore::InstallerReset)
|
34
35
|
end
|
@@ -49,6 +50,7 @@ module ForemanMaintain::Scenarios
|
|
49
50
|
add_step(Procedures::Installer::Run.new(:assumeyes => true))
|
50
51
|
add_step_with_context(Procedures::Installer::UpgradeRakeTask)
|
51
52
|
add_step_with_context(Procedures::Crond::Start)
|
53
|
+
add_step_with_context(Procedures::Timer::Start)
|
52
54
|
end
|
53
55
|
# rubocop:enable Metrics/MethodLength,Metrics/AbcSize
|
54
56
|
|
@@ -119,6 +121,7 @@ module ForemanMaintain::Scenarios
|
|
119
121
|
|
120
122
|
def compose
|
121
123
|
add_step_with_context(Procedures::Crond::Stop)
|
124
|
+
add_step_with_context(Procedures::Timer::Stop)
|
122
125
|
end
|
123
126
|
end
|
124
127
|
end
|
@@ -74,6 +74,7 @@ module Scenarios::Satellite
|
|
74
74
|
add_steps(
|
75
75
|
Procedures::MaintenanceMode::EnableMaintenanceMode,
|
76
76
|
Procedures::Crond::Stop,
|
77
|
+
Procedures::Timer::Stop,
|
77
78
|
Procedures::SyncPlans::Disable,
|
78
79
|
)
|
79
80
|
end
|
@@ -97,6 +98,7 @@ module Scenarios::Satellite
|
|
97
98
|
:assumeyes => true,
|
98
99
|
:download_only => true
|
99
100
|
),
|
101
|
+
Procedures::Iop::Update.new(:version => target_version),
|
100
102
|
Procedures::Service::Stop,
|
101
103
|
Procedures::Packages::Update.new(:assumeyes => true, :clean_cache => false),
|
102
104
|
)
|
@@ -116,8 +118,10 @@ module Scenarios::Satellite
|
|
116
118
|
Procedures::RefreshFeatures,
|
117
119
|
Procedures::Service::Start,
|
118
120
|
Procedures::Crond::Start,
|
121
|
+
Procedures::Timer::Start,
|
119
122
|
Procedures::SyncPlans::Enable,
|
120
123
|
Procedures::MaintenanceMode::DisableMaintenanceMode,
|
124
|
+
Procedures::Iop::ImagePrune,
|
121
125
|
)
|
122
126
|
end
|
123
127
|
end
|
@@ -69,8 +69,10 @@ module Scenarios::Update
|
|
69
69
|
:assumeyes => true,
|
70
70
|
:download_only => true
|
71
71
|
),
|
72
|
+
Procedures::Iop::Update.new(:version => feature(:instance).current_major_version),
|
72
73
|
Procedures::MaintenanceMode::EnableMaintenanceMode,
|
73
74
|
Procedures::Crond::Stop,
|
75
|
+
Procedures::Timer::Stop,
|
74
76
|
Procedures::SyncPlans::Disable
|
75
77
|
)
|
76
78
|
end
|
@@ -104,8 +106,10 @@ module Scenarios::Update
|
|
104
106
|
Procedures::RefreshFeatures,
|
105
107
|
Procedures::Service::Start,
|
106
108
|
Procedures::Crond::Start,
|
109
|
+
Procedures::Timer::Start,
|
107
110
|
Procedures::SyncPlans::Enable,
|
108
|
-
Procedures::MaintenanceMode::DisableMaintenanceMode
|
111
|
+
Procedures::MaintenanceMode::DisableMaintenanceMode,
|
112
|
+
Procedures::Iop::ImagePrune,
|
109
113
|
)
|
110
114
|
end
|
111
115
|
end
|
@@ -137,7 +137,7 @@ module ForemanMaintain
|
|
137
137
|
option '--label', 'label',
|
138
138
|
'Run only a specific check with a label. ' \
|
139
139
|
'(Use "list" command to see available labels)' do |label|
|
140
|
-
raise ArgumentError, 'value
|
140
|
+
raise ArgumentError, 'no value provided' if label.nil? || label.empty?
|
141
141
|
underscorize(label).to_sym
|
142
142
|
end
|
143
143
|
end
|
@@ -147,7 +147,7 @@ module ForemanMaintain
|
|
147
147
|
'Run only those with all specific set of tags. ' \
|
148
148
|
'(Use list-tags command to see available tags)',
|
149
149
|
:multivalued => true) do |tags|
|
150
|
-
raise ArgumentError, 'value
|
150
|
+
raise ArgumentError, 'no value provided' if tags.nil? || tags.empty?
|
151
151
|
tags.map { |tag| underscorize(tag).to_sym }
|
152
152
|
end
|
153
153
|
end
|
@@ -166,7 +166,7 @@ module ForemanMaintain
|
|
166
166
|
if opts.include?('whitelist')
|
167
167
|
option(['-w', '--whitelist'], 'whitelist',
|
168
168
|
'Comma-separated list of labels of steps to be skipped') do |whitelist|
|
169
|
-
raise ArgumentError, 'value
|
169
|
+
raise ArgumentError, 'no value provided' if whitelist.nil? || whitelist.empty?
|
170
170
|
whitelist.split(',').map(&:strip)
|
171
171
|
end
|
172
172
|
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
module ForemanMaintain
|
2
|
+
module Concerns
|
3
|
+
module Systemd
|
4
|
+
def action_noun(action)
|
5
|
+
action_word_modified(action) + 'ing'
|
6
|
+
end
|
7
|
+
|
8
|
+
def action_past_tense(action)
|
9
|
+
action_word_modified(action) + 'ed'
|
10
|
+
end
|
11
|
+
|
12
|
+
private
|
13
|
+
|
14
|
+
def format_status(output, exit_code, options)
|
15
|
+
status = ''
|
16
|
+
if !options[:failing] || exit_code > 0
|
17
|
+
if options[:brief]
|
18
|
+
status += format_brief_status(exit_code)
|
19
|
+
elsif !(output.nil? || output.empty?)
|
20
|
+
status += "\n" + output
|
21
|
+
end
|
22
|
+
end
|
23
|
+
status
|
24
|
+
end
|
25
|
+
|
26
|
+
def format_brief_status(exit_code)
|
27
|
+
result = (exit_code == 0) ? reporter.status_label(:success) : reporter.status_label(:fail)
|
28
|
+
padding = reporter.max_length - reporter.last_line.to_s.length - 30
|
29
|
+
"#{' ' * padding} #{result}"
|
30
|
+
end
|
31
|
+
|
32
|
+
def allowed_action?(action)
|
33
|
+
%w[start stop restart status enable disable].include?(action)
|
34
|
+
end
|
35
|
+
|
36
|
+
def action_word_modified(action)
|
37
|
+
case action
|
38
|
+
when 'status'
|
39
|
+
'display'
|
40
|
+
when 'enable', 'disable'
|
41
|
+
action.chomp('e')
|
42
|
+
when 'stop'
|
43
|
+
action + 'p'
|
44
|
+
else
|
45
|
+
action
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -25,6 +25,15 @@ module ForemanMaintain
|
|
25
25
|
[]
|
26
26
|
end
|
27
27
|
|
28
|
+
# Override method with list of applicable timers for feature.
|
29
|
+
# Services have a number for priority in order to ensure
|
30
|
+
# they are started and stopped in the correct order.
|
31
|
+
# example:
|
32
|
+
# [ system_service('foo', 10), system_service('bar', 20) ]
|
33
|
+
def timers
|
34
|
+
[]
|
35
|
+
end
|
36
|
+
|
28
37
|
# Override to generate additional feature instances that can't be
|
29
38
|
# autodetected directly
|
30
39
|
def additional_features
|
@@ -12,8 +12,9 @@ module ForemanMaintain::Utils
|
|
12
12
|
def command(action)
|
13
13
|
all = @options.fetch(:all, false)
|
14
14
|
skip_enablement = @options.fetch(:skip_enablement, false)
|
15
|
-
if
|
16
|
-
return skip_enablement_message(action, @name)
|
15
|
+
if %w[enable disable].include?(action)
|
16
|
+
return skip_enablement_message(action, @name) if skip_enablement
|
17
|
+
return if generated?
|
17
18
|
end
|
18
19
|
|
19
20
|
cmd = "systemctl #{action} #{@name}"
|
@@ -59,6 +60,10 @@ module ForemanMaintain::Utils
|
|
59
60
|
end
|
60
61
|
end
|
61
62
|
|
63
|
+
def generated?
|
64
|
+
service_enabled_status == 'generated'
|
65
|
+
end
|
66
|
+
|
62
67
|
def matches?(service)
|
63
68
|
if service.is_a? String
|
64
69
|
service == @name || File.fnmatch(service, @name)
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: foreman_maintain
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.12.
|
4
|
+
version: 1.12.4
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Ivan Nečas
|
@@ -245,6 +245,7 @@ files:
|
|
245
245
|
- definitions/features/service.rb
|
246
246
|
- definitions/features/sync_plans.rb
|
247
247
|
- definitions/features/tar.rb
|
248
|
+
- definitions/features/timer.rb
|
248
249
|
- definitions/procedures/backup/accessibility_confirmation.rb
|
249
250
|
- definitions/procedures/backup/clean.rb
|
250
251
|
- definitions/procedures/backup/compress_data.rb
|
@@ -275,6 +276,8 @@ files:
|
|
275
276
|
- definitions/procedures/hammer_setup.rb
|
276
277
|
- definitions/procedures/installer/run.rb
|
277
278
|
- definitions/procedures/installer/upgrade_rake_task.rb
|
279
|
+
- definitions/procedures/iop/image_prune.rb
|
280
|
+
- definitions/procedures/iop/update.rb
|
278
281
|
- definitions/procedures/knowledge_base_article.rb
|
279
282
|
- definitions/procedures/maintenance_mode/disable_maintenance_mode.rb
|
280
283
|
- definitions/procedures/maintenance_mode/enable_maintenance_mode.rb
|
@@ -329,26 +332,34 @@ files:
|
|
329
332
|
- definitions/procedures/service/stop.rb
|
330
333
|
- definitions/procedures/sync_plans/disable.rb
|
331
334
|
- definitions/procedures/sync_plans/enable.rb
|
335
|
+
- definitions/procedures/timer/start.rb
|
336
|
+
- definitions/procedures/timer/stop.rb
|
332
337
|
- definitions/reports/advisor_on_prem_remediations.rb
|
333
338
|
- definitions/reports/alternate_content_sources.rb
|
339
|
+
- definitions/reports/bookmarks.rb
|
334
340
|
- definitions/reports/compliance.rb
|
335
341
|
- definitions/reports/content.rb
|
342
|
+
- definitions/reports/disconnected_environment.rb
|
336
343
|
- definitions/reports/external_auth_source.rb
|
337
344
|
- definitions/reports/grouping.rb
|
338
345
|
- definitions/reports/image_mode_hosts.rb
|
339
346
|
- definitions/reports/instance.rb
|
340
347
|
- definitions/reports/inventory.rb
|
341
348
|
- definitions/reports/kerberos.rb
|
349
|
+
- definitions/reports/lab_features.rb
|
342
350
|
- definitions/reports/ldap_auth_source.rb
|
343
351
|
- definitions/reports/networking.rb
|
344
352
|
- definitions/reports/oidc_usage.rb
|
353
|
+
- definitions/reports/personal_access_token.rb
|
345
354
|
- definitions/reports/platform.rb
|
346
355
|
- definitions/reports/provisioning.rb
|
347
356
|
- definitions/reports/rbac.rb
|
348
357
|
- definitions/reports/recurring_logics.rb
|
358
|
+
- definitions/reports/selinux.rb
|
349
359
|
- definitions/reports/template
|
350
360
|
- definitions/reports/virt_who.rb
|
351
361
|
- definitions/reports/vmware.rb
|
362
|
+
- definitions/reports/webhooks.rb
|
352
363
|
- definitions/scenarios/backup.rb
|
353
364
|
- definitions/scenarios/foreman_upgrade.rb
|
354
365
|
- definitions/scenarios/maintenance_mode.rb
|
@@ -405,6 +416,7 @@ files:
|
|
405
416
|
- lib/foreman_maintain/concerns/scenario_metadata.rb
|
406
417
|
- lib/foreman_maintain/concerns/system_helpers.rb
|
407
418
|
- lib/foreman_maintain/concerns/system_service.rb
|
419
|
+
- lib/foreman_maintain/concerns/systemd.rb
|
408
420
|
- lib/foreman_maintain/concerns/upstream.rb
|
409
421
|
- lib/foreman_maintain/concerns/versions.rb
|
410
422
|
- lib/foreman_maintain/config.rb
|