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