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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 353f969fb5c1563fa146f6f5cca6a9a5c0a42e22564aa6e26d0d5d2b58d1b943
4
- data.tar.gz: b9f71f13a94ffef3cf8b338faf4d43f5aa053c58f84327713581c2f8b8bc6fa8
3
+ metadata.gz: f0e6da9a83b7060020dd78da8a1fd8887e4b76708471b3ee80802335e5252224
4
+ data.tar.gz: 4fdb27b3583aa2665bd76e725d748408c19e956c9959ed2513d17eb234ae4e7b
5
5
  SHA512:
6
- metadata.gz: 81316471839fc3ac86ecc96edccecdf4b6475bdd908ab79c1d55eba6d07a8bad0e75aaa83a440449fc8bf357e1c93df87e0d5f45f4211ba602172be9fde31bd4
7
- data.tar.gz: ae7ae727511fe75f9c517de28a93c66231c5e6e2a362541f769a5d99c364f4b49f9bf80b178b1def23fd6f4d83596fdc0d83e06e89540a17d0751bc838347797
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 continuining."
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,11 @@
1
+ module Reports
2
+ class Instance < ForemanMaintain::Report
3
+ metadata do
4
+ description 'Report information about the instance itself'
5
+ end
6
+
7
+ def run
8
+ data_field('instance_uuid') { YAML.safe_load(sql_setting('instance_id')) }
9
+ end
10
+ end
11
+ 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,12 @@
1
+ module ForemanMaintain::Scenarios
2
+ module Report
3
+ class Generate < ForemanMaintain::Scenario::FilteredScenario
4
+ metadata do
5
+ description 'Generate the usage reports'
6
+ tags :report
7
+ label :generate_report
8
+ manual_detection
9
+ end
10
+ end
11
+ end
12
+ 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
@@ -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 by #{command_name} and therefore was not restarted.
43
+ Remote databases are not managed and therefore no restart was performed.
45
44
  MSG
46
45
  )
47
46
  end
@@ -1,3 +1,3 @@
1
1
  module ForemanMaintain
2
- VERSION = '1.9.2'.freeze
2
+ VERSION = '1.10.0'.freeze
3
3
  end
@@ -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.9.2
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-08 00:00:00.000000000 Z
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