foreman_maintain 1.9.2 → 1.10.0
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/check_ipv6_disable.rb +1 -1
- data/definitions/reports/compliance.rb +28 -0
- data/definitions/reports/external_auth_source.rb +26 -0
- data/definitions/reports/grouping.rb +31 -0
- data/definitions/reports/instance.rb +11 -0
- data/definitions/reports/inventory.rb +84 -0
- data/definitions/reports/kerberos.rb +27 -0
- data/definitions/reports/ldap_auth_source.rb +73 -0
- data/definitions/reports/oidc_usage.rb +16 -0
- data/definitions/reports/platform.rb +98 -0
- data/definitions/reports/provisioning.rb +109 -0
- data/definitions/reports/rbac.rb +68 -0
- data/definitions/reports/recurring_logics.rb +40 -0
- data/definitions/reports/template +21 -0
- data/definitions/reports/virt_who.rb +21 -0
- data/definitions/reports/vmware.rb +13 -0
- data/definitions/scenarios/report.rb +12 -0
- data/lib/foreman_maintain/cli/report_command.rb +25 -0
- data/lib/foreman_maintain/cli.rb +2 -0
- data/lib/foreman_maintain/detector.rb +8 -0
- data/lib/foreman_maintain/report.rb +79 -0
- data/lib/foreman_maintain/scenario.rb +6 -0
- data/lib/foreman_maintain/utils/service/remote_db.rb +1 -2
- data/lib/foreman_maintain/version.rb +1 -1
- data/lib/foreman_maintain.rb +5 -0
- metadata +20 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f0e6da9a83b7060020dd78da8a1fd8887e4b76708471b3ee80802335e5252224
|
4
|
+
data.tar.gz: 4fdb27b3583aa2665bd76e725d748408c19e956c9959ed2513d17eb234ae4e7b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 8cd917d7a19d07c9674b8c7a31ffd45378d936b27fa8442ad18ebf004d1b9084fba86b9e67747c66f4dc91a23b0426c7e1fb9442f501678ece2b950eb45efe7d
|
7
|
+
data.tar.gz: 965c7051bc9623d274d7c38b0a13fb4919f9e84d5651e22cb8b7faf827e9fc0b1f883c9154fae72f56b33c0fa5fe4cae6fd5b4432ebc44d0cc943c8b44982926
|
@@ -12,7 +12,7 @@ class Checks::CheckIpv6Disable < ForemanMaintain::Check
|
|
12
12
|
|
13
13
|
def error_message
|
14
14
|
base = "\nThe kernel contains ipv6.disable=1 which is known to break installation and upgrade"\
|
15
|
-
", remove and reboot before
|
15
|
+
", remove and reboot before continuing."
|
16
16
|
|
17
17
|
if feature(:instance).downstream
|
18
18
|
base += " See https://access.redhat.com/solutions/5045841 for more details."
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module Reports
|
2
|
+
class Compliance < ForemanMaintain::Report
|
3
|
+
metadata do
|
4
|
+
description 'Check if OpenSCAP is used'
|
5
|
+
end
|
6
|
+
|
7
|
+
def run
|
8
|
+
self.data = {}
|
9
|
+
mapping = {
|
10
|
+
'policy':
|
11
|
+
'foreman_openscap_policies',
|
12
|
+
'policy_with_tailoring_file':
|
13
|
+
'foreman_openscap_policies WHERE tailoring_file_id IS NOT NULL',
|
14
|
+
'scap_contents':
|
15
|
+
"foreman_openscap_scap_contents",
|
16
|
+
'non_default_scap_contents':
|
17
|
+
"foreman_openscap_scap_contents WHERE NOT original_filename LIKE 'ssg-rhel%-ds.xml'",
|
18
|
+
'arf_report_last_year':
|
19
|
+
"reports WHERE type = 'ForemanOpenscap::ArfReport'
|
20
|
+
AND reported_at < NOW() - INTERVAL '1 year'",
|
21
|
+
}
|
22
|
+
|
23
|
+
mapping.each do |k, query|
|
24
|
+
data["compliance_#{k}_count"] = sql_count(query)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module Reports
|
2
|
+
class ExternalAuthSource < ForemanMaintain::Report
|
3
|
+
metadata do
|
4
|
+
description 'Checks the use of External auth source'
|
5
|
+
end
|
6
|
+
|
7
|
+
# Do you use external auth source?
|
8
|
+
def run
|
9
|
+
self.data = {}
|
10
|
+
# nil means no user linked to external auth source ever logged in
|
11
|
+
data["last_login_on_through_external_auth_source_in_days"] = nil
|
12
|
+
|
13
|
+
sql = <<~SQL
|
14
|
+
SELECT users.* FROM users
|
15
|
+
INNER JOIN auth_sources ON (auth_sources.id = users.auth_source_id)
|
16
|
+
WHERE auth_sources.type = 'AuthSourceExternal' AND users.last_login_on IS NOT NULL
|
17
|
+
ORDER BY users.last_login_on DESC LIMIT 1
|
18
|
+
SQL
|
19
|
+
user = query(sql).first
|
20
|
+
if user
|
21
|
+
data["last_login_on_through_external_auth_source_in_days"] =
|
22
|
+
(Date.today - Date.parse(user['last_login_on'])).to_i
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module Reports
|
2
|
+
class Grouping < ForemanMaintain::Report
|
3
|
+
metadata do
|
4
|
+
description 'Check how resources are grouped'
|
5
|
+
end
|
6
|
+
|
7
|
+
def run
|
8
|
+
self.data = {}
|
9
|
+
data_field('host_collections_count') { sql_count('katello_host_collections') }
|
10
|
+
|
11
|
+
data_field('host_collections_with_limit_count') do
|
12
|
+
sql_count("katello_host_collections
|
13
|
+
WHERE unlimited_hosts = 'f'")
|
14
|
+
end
|
15
|
+
|
16
|
+
hostgroup = sql_count('hostgroups')
|
17
|
+
hostgroup_nest_level = sql_as_count(
|
18
|
+
"COALESCE(MAX((CHAR_LENGTH(ancestry) - CHAR_LENGTH(REPLACE(ancestry, '/', '')))) + 2, 1)",
|
19
|
+
'hostgroups'
|
20
|
+
)
|
21
|
+
data['hostgroup_nesting'] = hostgroup_nest_level > 1
|
22
|
+
data['hostgroup_max_nesting_level'] = hostgroup.zero? ? 0 : hostgroup_nest_level
|
23
|
+
|
24
|
+
data_field('use_selectable_columns') { sql_count('table_preferences') > 0 }
|
25
|
+
|
26
|
+
if table_exists('config_groups')
|
27
|
+
data_field('config_group_count') { sql_count('config_groups') }
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,84 @@
|
|
1
|
+
module Reports
|
2
|
+
class Inventory < ForemanMaintain::Report
|
3
|
+
metadata do
|
4
|
+
description 'Facts about hosts and the rest of the inventory'
|
5
|
+
end
|
6
|
+
|
7
|
+
def run
|
8
|
+
merge_data('hosts_by_type_count') { hosts_by_type_count }
|
9
|
+
merge_data('hosts_by_os_count') { hosts_by_os_count }
|
10
|
+
merge_data('facts_by_type') { facts_by_type }
|
11
|
+
merge_data('audits') { audits }
|
12
|
+
merge_data('parameters_count') { parameters }
|
13
|
+
end
|
14
|
+
|
15
|
+
# Hosts
|
16
|
+
def hosts_by_type_count
|
17
|
+
query("select type, count(*) from hosts group by type").
|
18
|
+
to_h { |row| [(row['type'] || '').sub('Host::', ''), row['count'].to_i] }
|
19
|
+
end
|
20
|
+
|
21
|
+
# OS usage
|
22
|
+
def hosts_by_os_count
|
23
|
+
query(
|
24
|
+
<<-SQL
|
25
|
+
select max(operatingsystems.name) as os_name, count(*) as hosts_count
|
26
|
+
from hosts inner join operatingsystems on operatingsystem_id = operatingsystems.id
|
27
|
+
group by operatingsystem_id
|
28
|
+
SQL
|
29
|
+
).
|
30
|
+
to_h { |row| [row['os_name'], row['hosts_count'].to_i] }
|
31
|
+
end
|
32
|
+
|
33
|
+
# Facts usage
|
34
|
+
def facts_by_type
|
35
|
+
query(
|
36
|
+
<<-SQL
|
37
|
+
select fact_names.type,
|
38
|
+
min(fact_values.updated_at) as min_update_time,
|
39
|
+
max(fact_values.updated_at) as max_update_time,
|
40
|
+
count(fact_values.id) as values_count
|
41
|
+
from fact_values inner join fact_names on fact_name_id = fact_names.id
|
42
|
+
group by fact_names.type
|
43
|
+
SQL
|
44
|
+
).
|
45
|
+
to_h { |row| [row['type'].sub('FactName', ''), to_fact_hash(row)] }
|
46
|
+
end
|
47
|
+
|
48
|
+
# Audits
|
49
|
+
def audits
|
50
|
+
audits_query =
|
51
|
+
query(
|
52
|
+
<<-SQL
|
53
|
+
select count(*) as records_count,
|
54
|
+
min(created_at) as min_created_at,
|
55
|
+
max(created_at) as max_created_at
|
56
|
+
from audits
|
57
|
+
SQL
|
58
|
+
)
|
59
|
+
to_audits_record(audits_query.first)
|
60
|
+
end
|
61
|
+
|
62
|
+
# Parameters
|
63
|
+
def parameters
|
64
|
+
query("select type, count(*) from parameters group by type").
|
65
|
+
to_h { |row| [row['type'], row['count'].to_i] }
|
66
|
+
end
|
67
|
+
|
68
|
+
def to_fact_hash(row)
|
69
|
+
{
|
70
|
+
min_update_time: row['min_update_time'],
|
71
|
+
max_update_time: row['max_update_time'],
|
72
|
+
values_count: row['values_count'].to_i,
|
73
|
+
}
|
74
|
+
end
|
75
|
+
|
76
|
+
def to_audits_record(row)
|
77
|
+
{
|
78
|
+
records_count: row['records_count'].to_i,
|
79
|
+
min_created_at: row['min_created_at'],
|
80
|
+
max_created_at: row['max_created_at'],
|
81
|
+
}
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module Reports
|
2
|
+
class Kerberos < ForemanMaintain::Report
|
3
|
+
metadata do
|
4
|
+
description 'Checks the use of Kerberos'
|
5
|
+
tags :report
|
6
|
+
end
|
7
|
+
|
8
|
+
# Do you use kerberos?
|
9
|
+
# Do you use kerberos also for API authentication?
|
10
|
+
def run
|
11
|
+
authorize_login_delegation = sql_setting('authorize_login_delegation')
|
12
|
+
authorize_login_delegation_api = sql_setting('authorize_login_delegation_api')
|
13
|
+
oidc_issuer = sql_setting('oidc_issuer')
|
14
|
+
kerberos_result = authorize_login_delegation &&
|
15
|
+
YAML.load(authorize_login_delegation) == true &&
|
16
|
+
(oidc_issuer.nil? || YAML.load(oidc_issuer) == '')
|
17
|
+
kerberos_api_result = kerberos_result &&
|
18
|
+
authorize_login_delegation_api &&
|
19
|
+
YAML.load(authorize_login_delegation_api) == true
|
20
|
+
|
21
|
+
self.data = {
|
22
|
+
kerberos_use: !!kerberos_result,
|
23
|
+
kerberos_api_use: !!kerberos_api_result,
|
24
|
+
}
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
module Reports
|
2
|
+
class LDAPAuthSource < ForemanMaintain::Report
|
3
|
+
metadata do
|
4
|
+
description 'Checks the use of LDAP auth sources'
|
5
|
+
end
|
6
|
+
|
7
|
+
# Do you use FreeIPA LDAP auth source?
|
8
|
+
# Do you use AD LDAP auth source?
|
9
|
+
# Do you use POSIX LDAP auth source?
|
10
|
+
# Do you use netgroups schema?
|
11
|
+
# Do you disable automatic account creation on any LDAP auth source?
|
12
|
+
# Do you disable user group syncrhonization on any LDAP auth source?
|
13
|
+
# Do you have external user groups mapping?
|
14
|
+
def run
|
15
|
+
self.data = {}
|
16
|
+
data_field("external_user_group_mapping_count") { sql_count('external_usergroups') }
|
17
|
+
%w[free_ipa posix active_directory].reduce({}) do |_acc, flavor|
|
18
|
+
record_flavor_usage(flavor)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
# rubocop:disable Metrics/AbcSize
|
25
|
+
def record_flavor_usage(flavor)
|
26
|
+
flavored_query_base = query_base(flavor)
|
27
|
+
data["ldap_auth_source_#{flavor}_count"] = sql_count(flavored_query_base)
|
28
|
+
|
29
|
+
users = query(user_query(flavor))
|
30
|
+
data["users_authenticated_through_ldap_auth_source_#{flavor}"] = users.count
|
31
|
+
|
32
|
+
data["last_login_on_through_ldap_auth_source_#{flavor}_in_days"] = last_login(users)
|
33
|
+
|
34
|
+
data["ldap_auth_source_#{flavor}_with_net_groups_count"] =
|
35
|
+
sql_count("#{flavored_query_base} AND use_netgroups = true")
|
36
|
+
|
37
|
+
data["ldap_auth_source_#{flavor}_with_posix_groups_count"] =
|
38
|
+
sql_count("#{flavored_query_base} AND use_netgroups = false")
|
39
|
+
|
40
|
+
count = sql_count("#{flavored_query_base} AND onthefly_register = false")
|
41
|
+
data["ldap_auth_source_#{flavor}_with_account_creation_disabled_count"] = count
|
42
|
+
|
43
|
+
count = sql_count("#{flavored_query_base} AND usergroup_sync = false")
|
44
|
+
data["ldap_auth_source_#{flavor}_with_user_group_sync_disabled_count"] = count
|
45
|
+
end
|
46
|
+
# rubocop:enable Metrics/AbcSize
|
47
|
+
|
48
|
+
def last_login(users)
|
49
|
+
# nil means no user for a given LDAP type was found
|
50
|
+
if (user = users.first)
|
51
|
+
(Date.today - Date.parse(user['last_login_on'])).to_i
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def query_base(flavor)
|
56
|
+
<<~SQL
|
57
|
+
auth_sources
|
58
|
+
WHERE auth_sources.type = 'AuthSourceLdap' AND auth_sources.server_type = '#{flavor}'
|
59
|
+
SQL
|
60
|
+
end
|
61
|
+
|
62
|
+
def user_query(flavor)
|
63
|
+
<<~SQL
|
64
|
+
SELECT users.* FROM users
|
65
|
+
INNER JOIN auth_sources ON (auth_sources.id = users.auth_source_id)
|
66
|
+
WHERE auth_sources.type = 'AuthSourceLdap'
|
67
|
+
AND auth_sources.server_type = '#{flavor}'
|
68
|
+
AND users.last_login_on IS NOT NULL
|
69
|
+
ORDER BY users.last_login_on DESC
|
70
|
+
SQL
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module Reports
|
2
|
+
class OIDC < ForemanMaintain::Report
|
3
|
+
metadata do
|
4
|
+
description 'Checks the use of Keycloak/OIDC'
|
5
|
+
end
|
6
|
+
|
7
|
+
# Do you use OIDC/keycloak?
|
8
|
+
def run
|
9
|
+
data_field('oidc_use') do
|
10
|
+
oidc_issuer = sql_setting('oidc_issuer') || ''
|
11
|
+
loaded = YAML.load(oidc_issuer)
|
12
|
+
loaded.is_a?(String) && loaded != ''
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,98 @@
|
|
1
|
+
module Reports
|
2
|
+
class Platform < ForemanMaintain::Report
|
3
|
+
metadata do
|
4
|
+
description 'Report about platform usages'
|
5
|
+
end
|
6
|
+
|
7
|
+
def run
|
8
|
+
general_fields
|
9
|
+
rbac_fields
|
10
|
+
settings_fields
|
11
|
+
bookmarks_fields
|
12
|
+
mail_notification_fields
|
13
|
+
user_groups_fields
|
14
|
+
end
|
15
|
+
|
16
|
+
def mail_notification_fields
|
17
|
+
# Mail notifications
|
18
|
+
# users_per_mail_notification =
|
19
|
+
# query(
|
20
|
+
# <<-SQL
|
21
|
+
# select
|
22
|
+
# max(mail_notifications.name) as notification_name,
|
23
|
+
# count(user_mail_notifications.user_id)
|
24
|
+
# from user_mail_notifications inner join mail_notifications
|
25
|
+
# on mail_notification_id = mail_notifications.id
|
26
|
+
# group by mail_notification_id
|
27
|
+
# SQL
|
28
|
+
# ).
|
29
|
+
# to_h { |row| [row['notification_name'], row['count'].to_i] }
|
30
|
+
|
31
|
+
data_field('user_mail_notifications_count') { sql_count('user_mail_notifications') }
|
32
|
+
end
|
33
|
+
|
34
|
+
def user_groups_fields
|
35
|
+
data_field('user_groups_count') { sql_count('usergroups') }
|
36
|
+
end
|
37
|
+
|
38
|
+
def general_fields
|
39
|
+
data_field('smart_proxies_count') { sql_count('smart_proxies') }
|
40
|
+
merge_data('smart_proxies_creation_date') do
|
41
|
+
query("select id, created_at from smart_proxies").
|
42
|
+
to_h { |row| [row['id'], row['created_at']] }
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def rbac_fields
|
47
|
+
data_field('total_users_count') { sql_count('users') }
|
48
|
+
data_field('non_admin_users_count') { sql_count('users where admin = false') }
|
49
|
+
|
50
|
+
data_field('custom_roles_count') { sql_count('roles where origin = null') }
|
51
|
+
|
52
|
+
merge_data('taxonomies_counts') do
|
53
|
+
query("select type, count(*) from taxonomies group by type").
|
54
|
+
to_h { |row| [row['type'], row['count'].to_i] }
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def settings_fields
|
59
|
+
data_field('modified_settings') do
|
60
|
+
query("select name from settings").
|
61
|
+
map { |setting_line| setting_line['name'] }.
|
62
|
+
join(',')
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
def bookmarks_fields
|
67
|
+
merge_data('bookmarks_by_public_by_type') do
|
68
|
+
query(
|
69
|
+
<<-SQL
|
70
|
+
select public, owner_type, count(*)
|
71
|
+
from bookmarks
|
72
|
+
group by public, owner_type
|
73
|
+
SQL
|
74
|
+
).
|
75
|
+
to_h do |row|
|
76
|
+
[
|
77
|
+
"#{row['public'] ? 'public' : 'private'}#{flatten_separator}#{row['owner_type']}",
|
78
|
+
row['count'].to_i,
|
79
|
+
]
|
80
|
+
end
|
81
|
+
end
|
82
|
+
# bookmarks_by_owner =
|
83
|
+
# query(
|
84
|
+
# <<-SQL
|
85
|
+
# select owner_type, owner_id, count(*)
|
86
|
+
# from bookmarks
|
87
|
+
# group by owner_type, owner_id
|
88
|
+
# SQL
|
89
|
+
# ).
|
90
|
+
# to_h do |row|
|
91
|
+
# [
|
92
|
+
# "#{row['owner_type']}#{flatten_separator}#{row['owner_id']}",
|
93
|
+
# row['count'].to_i,
|
94
|
+
# ]
|
95
|
+
# end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
@@ -0,0 +1,109 @@
|
|
1
|
+
module Reports
|
2
|
+
class Provisioning < ForemanMaintain::Report
|
3
|
+
metadata do
|
4
|
+
description 'Provisioning facts about the system'
|
5
|
+
end
|
6
|
+
|
7
|
+
def run
|
8
|
+
data_field('managed_hosts_created_in_last_3_months') do
|
9
|
+
sql_count(
|
10
|
+
<<-SQL
|
11
|
+
hosts WHERE managed = true AND created_at >= current_date - interval '3 months'
|
12
|
+
SQL
|
13
|
+
)
|
14
|
+
end
|
15
|
+
|
16
|
+
compute_resources_fields
|
17
|
+
bare_metal_fields
|
18
|
+
templates_fields
|
19
|
+
end
|
20
|
+
|
21
|
+
def compute_resources_fields
|
22
|
+
hosts_by_compute_resources_type
|
23
|
+
hosts_by_compute_profile
|
24
|
+
|
25
|
+
merge_data('compute_resources_by_type') do
|
26
|
+
query(
|
27
|
+
<<-SQL
|
28
|
+
select type, count(*)
|
29
|
+
from compute_resources
|
30
|
+
group by type
|
31
|
+
SQL
|
32
|
+
).
|
33
|
+
to_h { |row| [row['type'], row['count'].to_i] }
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def hosts_by_compute_resources_type
|
38
|
+
merge_data('hosts_by_compute_resources_type') do
|
39
|
+
query(
|
40
|
+
<<-SQL
|
41
|
+
select compute_resources.type, count(hosts.id)
|
42
|
+
from hosts left outer join compute_resources on compute_resource_id = compute_resources.id
|
43
|
+
group by compute_resources.type
|
44
|
+
SQL
|
45
|
+
).
|
46
|
+
to_h { |row| [row['type'] || 'baremetal', row['count'].to_i] }
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def hosts_by_compute_profile
|
51
|
+
merge_data('hosts_by_compute_profile') do
|
52
|
+
query(
|
53
|
+
<<-SQL
|
54
|
+
select max(compute_profiles.name) as name, count(hosts.id)
|
55
|
+
from hosts left outer join compute_profiles on compute_profile_id = compute_profiles.id
|
56
|
+
group by compute_profile_id
|
57
|
+
SQL
|
58
|
+
).
|
59
|
+
to_h { |row| [row['name'] || 'none', row['count'].to_i] }
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def bare_metal_fields
|
64
|
+
nics_by_type_count
|
65
|
+
hosts_by_managed_count
|
66
|
+
|
67
|
+
data_field('discovery_rules_count') { sql_count('discovery_rules') }
|
68
|
+
end
|
69
|
+
|
70
|
+
def nics_by_type_count
|
71
|
+
merge_data('nics_by_type_count') do
|
72
|
+
query(
|
73
|
+
<<-SQL
|
74
|
+
select type, count(*)
|
75
|
+
from nics
|
76
|
+
group by type
|
77
|
+
SQL
|
78
|
+
).
|
79
|
+
to_h { |row| [(row['type'] || 'none').sub('Nic::', ''), row['count'].to_i] }
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
def hosts_by_managed_count
|
84
|
+
merge_data('hosts_by_managed_count') do
|
85
|
+
query(
|
86
|
+
<<-SQL
|
87
|
+
select managed, count(*)
|
88
|
+
from hosts
|
89
|
+
group by managed
|
90
|
+
SQL
|
91
|
+
).
|
92
|
+
to_h { |row| [row['managed'] == 't' ? 'managed' : 'unmanaged', row['count'].to_i] }
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
def templates_fields
|
97
|
+
merge_data('non_default_templates_per_type') do
|
98
|
+
query(
|
99
|
+
<<-SQL
|
100
|
+
select type, count(*) from templates
|
101
|
+
where templates.default = false
|
102
|
+
group by type
|
103
|
+
SQL
|
104
|
+
).
|
105
|
+
to_h { |row| [row['type'], row['count'].to_i] }
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
module Reports
|
2
|
+
class RBAC < ForemanMaintain::Report
|
3
|
+
metadata do
|
4
|
+
description 'Checks the RBAC use'
|
5
|
+
end
|
6
|
+
|
7
|
+
TAXONOMY_TYPES = %w[Organization Location].freeze
|
8
|
+
|
9
|
+
# How many users do you have in the system?
|
10
|
+
# How many non-admin users do you have?
|
11
|
+
# How many custom roles did you create and assigned to users?
|
12
|
+
def run
|
13
|
+
self.data = {}
|
14
|
+
|
15
|
+
data.merge!(user_counts)
|
16
|
+
data.merge!(custom_roles_counts)
|
17
|
+
data.merge!(taxonomy_counts)
|
18
|
+
data.merge!(taxonomy_ignore_type_uses)
|
19
|
+
end
|
20
|
+
|
21
|
+
def user_counts
|
22
|
+
users = sql_count("users" +
|
23
|
+
" INNER JOIN auth_sources ON auth_sources.id = users.auth_source_id" +
|
24
|
+
" WHERE auth_sources.name != 'Hidden'")
|
25
|
+
|
26
|
+
non_admin_users = sql_count("users" +
|
27
|
+
" LEFT OUTER JOIN cached_usergroup_members" +
|
28
|
+
" ON cached_usergroup_members.user_id = users.id" +
|
29
|
+
" LEFT OUTER JOIN usergroups ON usergroups.id = cached_usergroup_members.usergroup_id" +
|
30
|
+
" INNER JOIN auth_sources ON auth_sources.id = users.auth_source_id" +
|
31
|
+
" WHERE ((users.admin = FALSE OR users.admin IS NULL)" +
|
32
|
+
" AND (usergroups.admin = FALSE OR usergroups.admin IS NULL))" +
|
33
|
+
" AND auth_sources.name != 'Hidden'")
|
34
|
+
|
35
|
+
{ 'users_count' => users, 'non_admin_users_count' => non_admin_users }
|
36
|
+
end
|
37
|
+
|
38
|
+
def custom_roles_counts
|
39
|
+
role_ids = query("SELECT id FROM roles WHERE roles.builtin != 2 AND roles.origin IS NULL")
|
40
|
+
roles_count = role_ids.size
|
41
|
+
role_ids = role_ids.flat_map(&:values)
|
42
|
+
assigned_count = if role_ids.empty?
|
43
|
+
0
|
44
|
+
else
|
45
|
+
sql = "cached_user_roles" +
|
46
|
+
"WHERE cached_user_roles.role_id IN (#{role_ids.join(',')})"
|
47
|
+
sql_count(sql)
|
48
|
+
end
|
49
|
+
|
50
|
+
{ 'custom_roles_count' => roles_count, 'assigned_custom_roles_count' => assigned_count }
|
51
|
+
end
|
52
|
+
|
53
|
+
def taxonomy_counts
|
54
|
+
TAXONOMY_TYPES.to_h do |t|
|
55
|
+
["#{t.downcase}s_count", sql_count("taxonomies WHERE type = '#{t}'")]
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def taxonomy_ignore_type_uses
|
60
|
+
TAXONOMY_TYPES.to_h do |t|
|
61
|
+
count = sql_count("taxonomies" +
|
62
|
+
" WHERE type = '#{t}'" +
|
63
|
+
" AND ignore_types IS NOT NULL")
|
64
|
+
["#{t.downcase}_ignore_types_used", count.positive?]
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Reports
|
4
|
+
class RecurringLogics < ForemanMaintain::Report
|
5
|
+
metadata do
|
6
|
+
description 'Check if recurring logics are used'
|
7
|
+
end
|
8
|
+
|
9
|
+
REX_TASK_GROUP_CTE = <<~SQL
|
10
|
+
WITH recurring_remote_execution_task_group_ids AS (
|
11
|
+
SELECT task_group_id
|
12
|
+
FROM foreman_tasks_task_groups as fttg
|
13
|
+
INNER JOIN foreman_tasks_task_group_members AS fttgm
|
14
|
+
ON fttgm.task_group_id = fttg.id
|
15
|
+
INNER JOIN foreman_tasks_tasks AS ftt
|
16
|
+
ON fttgm.task_id = ftt.id
|
17
|
+
WHERE
|
18
|
+
fttg.type = 'ForemanTasks::TaskGroups::RecurringLogicTaskGroup'
|
19
|
+
AND ftt.label = 'Actions::RemoteExecution::RunHostsJob'
|
20
|
+
), indefinite_rex_recurring_logics AS (
|
21
|
+
SELECT * FROM foreman_tasks_recurring_logics AS ftrl
|
22
|
+
WHERE ftrl.task_group_id IN (SELECT task_group_id FROM recurring_remote_execution_task_group_ids)
|
23
|
+
AND (ftrl.end_time IS NULL OR ftrl.max_iteration IS NULL)
|
24
|
+
)
|
25
|
+
SQL
|
26
|
+
|
27
|
+
def run
|
28
|
+
self.data = {}
|
29
|
+
data['recurring_logics_indefinite_rex_count'] = sql_count('indefinite_rex_recurring_logics')
|
30
|
+
data['recurring_logics_indefinite_rex_ansible_count'] =
|
31
|
+
sql_count("indefinite_rex_recurring_logics WHERE purpose LIKE 'ansible-%'")
|
32
|
+
end
|
33
|
+
|
34
|
+
private
|
35
|
+
|
36
|
+
def sql_count(query)
|
37
|
+
super(query, cte: REX_TASK_GROUP_CTE)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# copy the file and add the .rb suffix
|
2
|
+
module Checks
|
3
|
+
module Report
|
4
|
+
class Template < ForemanMaintain::Report
|
5
|
+
metadata do
|
6
|
+
description 'One sentence description'
|
7
|
+
end
|
8
|
+
|
9
|
+
def run
|
10
|
+
data_field('some_value') { 'hello' }
|
11
|
+
merge_data('key_prefix') do
|
12
|
+
{
|
13
|
+
'key1': 'value1',
|
14
|
+
'key2': 'value2',
|
15
|
+
'nested': { 'another_key': 'another_value', 'more': 'more_value' }
|
16
|
+
}
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module Reports
|
2
|
+
class Virtwho < ForemanMaintain::Report
|
3
|
+
metadata do
|
4
|
+
description 'Check if virt-who is being used and what hypervisor types are present'
|
5
|
+
end
|
6
|
+
|
7
|
+
HYPERVISOR_TYPES = %w[ahv esx fakevirt hyperv kubevirt libvirtd].freeze
|
8
|
+
|
9
|
+
def run
|
10
|
+
self.data = {}
|
11
|
+
data_field('foreman_virt_who_configure_configurations_count') do
|
12
|
+
sql_count('foreman_virt_who_configure_configs')
|
13
|
+
end
|
14
|
+
|
15
|
+
HYPERVISOR_TYPES.each do |type|
|
16
|
+
data["foreman_virt_who_configure_#{type}_count"] =
|
17
|
+
sql_count("foreman_virt_who_configure_configs WHERE hypervisor_type = '#{type}'")
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
module Reports
|
2
|
+
class Vmware < ForemanMaintain::Report
|
3
|
+
metadata do
|
4
|
+
description 'Check if vmware compute resource is used'
|
5
|
+
end
|
6
|
+
|
7
|
+
def run
|
8
|
+
data_field('compute_resource_vmware_count') do
|
9
|
+
sql_count("compute_resources WHERE type = 'Foreman::Model::Vmware'")
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module ForemanMaintain
|
2
|
+
module Cli
|
3
|
+
class ReportCommand < Base
|
4
|
+
extend Concerns::Finders
|
5
|
+
|
6
|
+
option '--output', 'FILE', 'Output the generate report into FILE'
|
7
|
+
subcommand 'generate', 'Generates the usage reports' do
|
8
|
+
def execute
|
9
|
+
scenario = run_scenario(Scenarios::Report::Generate.new({}, [:reports])).first
|
10
|
+
|
11
|
+
# description can be used too
|
12
|
+
report_data = scenario.steps.map(&:data).compact.reduce(&:merge).transform_keys(&:to_s)
|
13
|
+
report_data['version'] = 1
|
14
|
+
yaml = report_data.to_yaml
|
15
|
+
if @output
|
16
|
+
File.write(@output, yaml)
|
17
|
+
else
|
18
|
+
puts yaml
|
19
|
+
end
|
20
|
+
exit runner.exit_code
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
data/lib/foreman_maintain/cli.rb
CHANGED
@@ -11,6 +11,7 @@ require 'foreman_maintain/cli/restore_command'
|
|
11
11
|
require 'foreman_maintain/cli/maintenance_mode_command'
|
12
12
|
require 'foreman_maintain/cli/packages_command'
|
13
13
|
require 'foreman_maintain/cli/plugin_command'
|
14
|
+
require 'foreman_maintain/cli/report_command'
|
14
15
|
require 'foreman_maintain/cli/self_upgrade_command'
|
15
16
|
require 'foreman_maintain/cli/update_command'
|
16
17
|
|
@@ -33,6 +34,7 @@ module ForemanMaintain
|
|
33
34
|
subcommand 'self-upgrade', 'Perform major version self upgrade', SelfUpgradeCommand
|
34
35
|
subcommand 'maintenance-mode', 'Control maintenance-mode for application',
|
35
36
|
MaintenanceModeCommand
|
37
|
+
subcommand 'report', 'Generate usage report', ReportCommand
|
36
38
|
|
37
39
|
def run(*arguments)
|
38
40
|
logger.info("Running foreman-maintain command with arguments #{arguments.inspect}")
|
@@ -41,6 +41,14 @@ module ForemanMaintain
|
|
41
41
|
filter(@available_checks, filter_conditions)
|
42
42
|
end
|
43
43
|
|
44
|
+
def available_reports(filter_conditions = nil)
|
45
|
+
unless @available_reports
|
46
|
+
ensure_features_detected
|
47
|
+
@available_reports = find_present_classes(Report)
|
48
|
+
end
|
49
|
+
filter(@available_reports, filter_conditions)
|
50
|
+
end
|
51
|
+
|
44
52
|
def available_procedures(filter_conditions = nil)
|
45
53
|
unless @available_procedures
|
46
54
|
ensure_features_detected
|
@@ -0,0 +1,79 @@
|
|
1
|
+
module ForemanMaintain
|
2
|
+
class Report < Executable
|
3
|
+
include Concerns::Logger
|
4
|
+
include Concerns::SystemHelpers
|
5
|
+
include Concerns::Metadata
|
6
|
+
include Concerns::Finders
|
7
|
+
|
8
|
+
attr_accessor :data
|
9
|
+
|
10
|
+
def query(sql)
|
11
|
+
feature(:foreman_database).query(sql)
|
12
|
+
end
|
13
|
+
|
14
|
+
def sql_count(sql, column: '*', cte: '')
|
15
|
+
sql_as_count("COUNT(#{column})", sql, cte: cte)
|
16
|
+
end
|
17
|
+
|
18
|
+
def sql_as_count(selection, sql, cte: '')
|
19
|
+
query = "#{cte} SELECT #{selection} AS COUNT FROM #{sql}"
|
20
|
+
feature(:foreman_database).query(query).first['count'].to_i
|
21
|
+
rescue StandardError
|
22
|
+
nil
|
23
|
+
end
|
24
|
+
|
25
|
+
def sql_setting(name)
|
26
|
+
sql = "SELECT value FROM settings WHERE name = '#{name}'"
|
27
|
+
result = feature(:foreman_database).query(sql).first
|
28
|
+
(result || {})['value']
|
29
|
+
end
|
30
|
+
|
31
|
+
def flatten(hash, prefix = '')
|
32
|
+
hash.each_with_object({}) do |(key, value), result|
|
33
|
+
new_key = "#{prefix}#{prefix.empty? ? '' : flatten_separator}#{key}"
|
34
|
+
if value.is_a? Hash
|
35
|
+
result.merge!(flatten(value, new_key))
|
36
|
+
else
|
37
|
+
result[new_key] = value
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def table_exists(table)
|
43
|
+
sql_count("information_schema.tables WHERE table_name = '#{table}'").positive?
|
44
|
+
end
|
45
|
+
|
46
|
+
def data_field(field_name)
|
47
|
+
self.data ||= {}
|
48
|
+
value = yield
|
49
|
+
self.data[field_name] = value
|
50
|
+
rescue StandardError
|
51
|
+
nil
|
52
|
+
end
|
53
|
+
|
54
|
+
def merge_data(prefix)
|
55
|
+
self.data ||= {}
|
56
|
+
data_hash = yield
|
57
|
+
self.data.merge!(flatten(data_hash, prefix))
|
58
|
+
rescue StandardError
|
59
|
+
nil
|
60
|
+
end
|
61
|
+
|
62
|
+
def run
|
63
|
+
raise NoMethodError, 'method not implemented on abstract report classes'
|
64
|
+
end
|
65
|
+
|
66
|
+
# internal method called by executor
|
67
|
+
def __run__(execution)
|
68
|
+
super
|
69
|
+
rescue Error::Fail => e
|
70
|
+
set_fail(e.message)
|
71
|
+
rescue StandardError => e
|
72
|
+
set_warn(e.message)
|
73
|
+
end
|
74
|
+
|
75
|
+
def flatten_separator
|
76
|
+
'|'
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
@@ -23,7 +23,9 @@ module ForemanMaintain
|
|
23
23
|
@definition_kinds = definition_kinds
|
24
24
|
@steps = []
|
25
25
|
@steps += checks(filter) if definition_kinds.include?(:check)
|
26
|
+
@steps += reports(filter) if definition_kinds.include?(:reports)
|
26
27
|
@steps += procedures(filter) if definition_kinds.include?(:procedure)
|
28
|
+
|
27
29
|
@steps = DependencyGraph.sort(@steps)
|
28
30
|
end
|
29
31
|
|
@@ -57,6 +59,10 @@ module ForemanMaintain
|
|
57
59
|
ForemanMaintain.available_checks(filter).map(&:ensure_instance)
|
58
60
|
end
|
59
61
|
|
62
|
+
def reports(filter)
|
63
|
+
ForemanMaintain.available_reports(filter).map(&:ensure_instance)
|
64
|
+
end
|
65
|
+
|
60
66
|
def procedures(filter)
|
61
67
|
ForemanMaintain.available_procedures(filter).map(&:ensure_instance)
|
62
68
|
end
|
@@ -39,9 +39,8 @@ module ForemanMaintain::Utils
|
|
39
39
|
end
|
40
40
|
|
41
41
|
def restart
|
42
|
-
command_name = ForemanMaintain.command_name
|
43
42
|
db_status(<<~MSG
|
44
|
-
Remote databases are not managed
|
43
|
+
Remote databases are not managed and therefore no restart was performed.
|
45
44
|
MSG
|
46
45
|
)
|
47
46
|
end
|
data/lib/foreman_maintain.rb
CHANGED
@@ -36,6 +36,7 @@ module ForemanMaintain
|
|
36
36
|
require 'foreman_maintain/feature'
|
37
37
|
require 'foreman_maintain/executable'
|
38
38
|
require 'foreman_maintain/check'
|
39
|
+
require 'foreman_maintain/report'
|
39
40
|
require 'foreman_maintain/procedure'
|
40
41
|
require 'foreman_maintain/scenario'
|
41
42
|
require 'foreman_maintain/runner'
|
@@ -131,6 +132,10 @@ module ForemanMaintain
|
|
131
132
|
detector.available_checks(*args)
|
132
133
|
end
|
133
134
|
|
135
|
+
def available_reports(*args)
|
136
|
+
detector.available_reports(*args)
|
137
|
+
end
|
138
|
+
|
134
139
|
def available_procedures(*args)
|
135
140
|
detector.available_procedures(*args)
|
136
141
|
end
|
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: 1.
|
4
|
+
version: 1.10.0
|
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: 2025-01-
|
11
|
+
date: 2025-01-29 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: clamp
|
@@ -307,11 +307,27 @@ files:
|
|
307
307
|
- definitions/procedures/service/stop.rb
|
308
308
|
- definitions/procedures/sync_plans/disable.rb
|
309
309
|
- definitions/procedures/sync_plans/enable.rb
|
310
|
+
- definitions/reports/compliance.rb
|
311
|
+
- definitions/reports/external_auth_source.rb
|
312
|
+
- definitions/reports/grouping.rb
|
313
|
+
- definitions/reports/instance.rb
|
314
|
+
- definitions/reports/inventory.rb
|
315
|
+
- definitions/reports/kerberos.rb
|
316
|
+
- definitions/reports/ldap_auth_source.rb
|
317
|
+
- definitions/reports/oidc_usage.rb
|
318
|
+
- definitions/reports/platform.rb
|
319
|
+
- definitions/reports/provisioning.rb
|
320
|
+
- definitions/reports/rbac.rb
|
321
|
+
- definitions/reports/recurring_logics.rb
|
322
|
+
- definitions/reports/template
|
323
|
+
- definitions/reports/virt_who.rb
|
324
|
+
- definitions/reports/vmware.rb
|
310
325
|
- definitions/scenarios/backup.rb
|
311
326
|
- definitions/scenarios/foreman_upgrade.rb
|
312
327
|
- definitions/scenarios/maintenance_mode.rb
|
313
328
|
- definitions/scenarios/packages.rb
|
314
329
|
- definitions/scenarios/puppet.rb
|
330
|
+
- definitions/scenarios/report.rb
|
315
331
|
- definitions/scenarios/restore.rb
|
316
332
|
- definitions/scenarios/satellite_upgrade.rb
|
317
333
|
- definitions/scenarios/self_upgrade.rb
|
@@ -337,6 +353,7 @@ files:
|
|
337
353
|
- lib/foreman_maintain/cli/maintenance_mode_command.rb
|
338
354
|
- lib/foreman_maintain/cli/packages_command.rb
|
339
355
|
- lib/foreman_maintain/cli/plugin_command.rb
|
356
|
+
- lib/foreman_maintain/cli/report_command.rb
|
340
357
|
- lib/foreman_maintain/cli/restore_command.rb
|
341
358
|
- lib/foreman_maintain/cli/self_upgrade_command.rb
|
342
359
|
- lib/foreman_maintain/cli/service_command.rb
|
@@ -378,6 +395,7 @@ files:
|
|
378
395
|
- lib/foreman_maintain/package_manager/dnf.rb
|
379
396
|
- lib/foreman_maintain/param.rb
|
380
397
|
- lib/foreman_maintain/procedure.rb
|
398
|
+
- lib/foreman_maintain/report.rb
|
381
399
|
- lib/foreman_maintain/reporter.rb
|
382
400
|
- lib/foreman_maintain/reporter/cli_reporter.rb
|
383
401
|
- lib/foreman_maintain/repository_manager.rb
|