From 6f6ef3bba1eb76f5b2587693b13368019d49a378 Mon Sep 17 00:00:00 2001 From: Shimon Shtein Date: Thu, 16 Jan 2025 15:47:13 +0200 Subject: [PATCH] Add MVP reports (#7) * Add MVP reports * Remove unused report --- definitions/reports/inventory.rb | 92 ++++++++++++++++++ definitions/reports/platform.rb | 108 ++++++++++++++++++++++ definitions/reports/provisioning.rb | 101 ++++++++++++++++++++ definitions/reports/provisioning_usage.rb | 16 ---- lib/foreman_maintain/report.rb | 17 ++++ 5 files changed, 318 insertions(+), 16 deletions(-) create mode 100644 definitions/reports/inventory.rb create mode 100644 definitions/reports/platform.rb create mode 100644 definitions/reports/provisioning.rb delete mode 100644 definitions/reports/provisioning_usage.rb diff --git a/definitions/reports/inventory.rb b/definitions/reports/inventory.rb new file mode 100644 index 000000000..8e6d23688 --- /dev/null +++ b/definitions/reports/inventory.rb @@ -0,0 +1,92 @@ +# copy the file and add the .rb suffix +module Checks + module Report + class Inventory < ForemanMaintain::Report + metadata do + description 'Facts about hosts and the rest of the inventory' + end + + # rubocop:disable Metrics/AbcSize, Metrics/MethodLength + def run + # Hosts + hosts_by_type_count = + feature(:foreman_database). + query("select type, count(*) from hosts group by type"). + to_h { |row| [(row['type'] || '').sub('Host::', ''), row['count'].to_i] } + + # OS usage + hosts_by_os_count = + feature(:foreman_database). + query( + <<-SQL + select max(operatingsystems.name) as os_name, count(*) as hosts_count + from hosts inner join operatingsystems on operatingsystem_id = operatingsystems.id + group by operatingsystem_id + SQL + ). + to_h { |row| [row['os_name'], row['hosts_count'].to_i] } + + # Facts usage + facts_by_type = + feature(:foreman_database). + query( + <<-SQL + select fact_names.type, + min(fact_values.updated_at) as min_update_time, + max(fact_values.updated_at) as max_update_time, + count(fact_values.id) as values_count + from fact_values inner join fact_names on fact_name_id = fact_names.id + group by fact_names.type + SQL + ). + to_h { |row| [row['type'].sub('FactName', ''), to_fact_hash(row)] } + + # Audits + audits_query = + feature(:foreman_database). + query( + <<-SQL + select count(*) as records_count, + min(created_at) as min_created_at, + max(created_at) as max_created_at + from audits + SQL + ) + audits = to_audits_record(audits_query.first) + + # Parameters + parameters = + feature(:foreman_database). + query("select type, count(*) from parameters group by type"). + to_h { |row| [row['type'], row['count'].to_i] } + + data = {} + + data.merge!(flatten(hosts_by_type_count, 'hosts_by_type_count')) + data.merge!(flatten(hosts_by_os_count, 'hosts_by_os_count')) + data.merge!(flatten(facts_by_type, 'facts_by_type')) + data.merge!(flatten(audits, 'audits')) + data.merge!(flatten(parameters, 'parameters_count')) + + self.data = data + end + # rubocop:enable Metrics/AbcSize, Metrics/MethodLength + + def to_fact_hash(row) + { + min_update_time: row['min_update_time'], + max_update_time: row['max_update_time'], + values_count: row['values_count'].to_i, + } + end + + def to_audits_record(row) + { + records_count: row['records_count'].to_i, + min_created_at: row['min_created_at'], + max_created_at: row['max_created_at'], + } + end + end + end +end diff --git a/definitions/reports/platform.rb b/definitions/reports/platform.rb new file mode 100644 index 000000000..54c410435 --- /dev/null +++ b/definitions/reports/platform.rb @@ -0,0 +1,108 @@ +# copy the file and add the .rb suffix +module Checks + module Report + class Platform < ForemanMaintain::Report + metadata do + description 'Report about platform usages' + end + + # rubocop:disable Metrics/AbcSize, Metrics/MethodLength + def run + # General + smart_proxies_count = sql_count('smart_proxies') + smart_proxies_creation_date = + feature(:foreman_database). + query("select id, created_at from smart_proxies"). + to_h { |row| [row['id'], row['created_at']] } + + # RBAC + total_users_count = sql_count('users') + non_admin_users_count = sql_count('users where admin = false') + + custom_roles_count = sql_count('roles where origin = null') + taxonomies_counts = + feature(:foreman_database). + query("select type, count(*) from taxonomies group by type"). + to_h { |row| [row['type'], row['count'].to_i] } + + # Settings + modified_settings = + feature(:foreman_database). + query("select name from settings"). + map { |setting_line| setting_line['name'] }. + join(',') + + # User groups + user_groups_count = sql_count('usergroups') + + # Bookmarks + bookmarks_by_public_by_type = + feature(:foreman_database). + query( + <<-SQL + select public, owner_type, count(*) + from bookmarks + group by public, owner_type + SQL + ). + to_h do |row| + [ + "#{row['public'] ? 'public' : 'private'}#{flatten_separator}#{row['owner_type']}", + row['count'].to_i, + ] + end + # bookmarks_by_owner = + # feature(:foreman_database). + # query( + # <<-SQL + # select owner_type, owner_id, count(*) + # from bookmarks + # group by owner_type, owner_id + # SQL + # ). + # to_h do |row| + # [ + # "#{row['owner_type']}#{flatten_separator}#{row['owner_id']}", + # row['count'].to_i, + # ] + # end + + # Mail notifications + # users_per_mail_notification = + # feature(:foreman_database). + # query( + # <<-SQL + # select + # max(mail_notifications.name) as notification_name, + # count(user_mail_notifications.user_id) + # from user_mail_notifications inner join mail_notifications + # on mail_notification_id = mail_notifications.id + # group by mail_notification_id + # SQL + # ). + # to_h { |row| [row['notification_name'], row['count'].to_i] } + + user_mail_notifications_count = sql_count('user_mail_notifications') + + data = { + smart_proxies_count: smart_proxies_count, + total_users_count: total_users_count, + non_admin_users_count: non_admin_users_count, + custom_roles_count: custom_roles_count, + modified_settings: modified_settings, + user_groups_count: user_groups_count, + user_mail_notifications_count: user_mail_notifications_count, + } + + data.merge!(flatten(smart_proxies_creation_date, 'smart_proxies_creation_date')) + data.merge!(flatten(taxonomies_counts, 'taxonomies_counts')) + data.merge!(flatten(bookmarks_by_public_by_type, 'bookmarks_by_public_by_type')) + # data.merge!(flatten(bookmarks_by_owner, 'bookmarks_by_owner')) + # data.merge!(flatten(users_per_mail_notification, 'users_per_mail_notification')) + + self.data = data + end + # rubocop:enable Metrics/AbcSize, Metrics/MethodLength + end + end +end diff --git a/definitions/reports/provisioning.rb b/definitions/reports/provisioning.rb new file mode 100644 index 000000000..ff42d253b --- /dev/null +++ b/definitions/reports/provisioning.rb @@ -0,0 +1,101 @@ +module Checks + module Report + class Provisioning < ForemanMaintain::Report + metadata do + description 'Provisioning facts about the system' + end + + # rubocop:disable Metrics/AbcSize, Metrics/MethodLength, Metrics/CyclomaticComplexity + def run + hosts_in_3_months = + sql_count( + <<-SQL + hosts WHERE managed = true AND created_at >= current_date - interval '3 months' + SQL + ) + + # Compute resources + compute_resources_by_type = + feature(:foreman_database). + query( + <<-SQL + select type, count(*) + from compute_resources + group by type + SQL + ). + to_h { |row| [row['type'], row['count'].to_i] } + + hosts_by_compute_resources_type = + feature(:foreman_database). + query( + <<-SQL + select compute_resources.type, count(hosts.id) + from hosts left outer join compute_resources on compute_resource_id = compute_resources.id + group by compute_resources.type + SQL + ). + to_h { |row| [row['type'] || 'baremetal', row['count'].to_i] } + hosts_by_compute_profile = + feature(:foreman_database). + query( + <<-SQL + select max(compute_profiles.name) as name, count(hosts.id) + from hosts left outer join compute_profiles on compute_profile_id = compute_profiles.id + group by compute_profile_id + SQL + ). + to_h { |row| [row['name'] || 'none', row['count'].to_i] } + + # Bare metal + nics_by_type_count = + feature(:foreman_database). + query( + <<-SQL + select type, count(*) + from nics + group by type + SQL + ). + to_h { |row| [(row['type'] || 'none').sub('Nic::', ''), row['count'].to_i] } + discovery_rules_count = sql_count('discovery_rules') + hosts_by_managed_count = + feature(:foreman_database). + query( + <<-SQL + select managed, count(*) + from hosts + group by managed + SQL + ). + to_h { |row| [row['managed'] == 't' ? 'managed' : 'unmanaged', row['count'].to_i] } + + # Templates + non_default_templates_per_type = + feature(:foreman_database). + query( + <<-SQL + select type, count(*) from templates + where templates.default = false + group by type + SQL + ). + to_h { |row| [row['type'], row['count'].to_i] } + + data = { + discovery_rules_count: discovery_rules_count, + managed_hosts_created_in_last_3_months: hosts_in_3_months, + } + data.merge!(flatten(compute_resources_by_type, 'compute_resources_by_type')) + data.merge!(flatten(hosts_by_compute_resources_type, 'hosts_by_compute_resources_type')) + data.merge!(flatten(hosts_by_compute_profile, 'hosts_by_compute_profile')) + data.merge!(flatten(nics_by_type_count, 'nics_by_type')) + data.merge!(flatten(hosts_by_managed_count, 'managed_hosts_count')) + data.merge!(flatten(non_default_templates_per_type, 'non_default_templates_per_type')) + + self.data = data + end + # rubocop:enable Metrics/AbcSize, Metrics/MethodLength, Metrics/CyclomaticComplexity + end + end +end diff --git a/definitions/reports/provisioning_usage.rb b/definitions/reports/provisioning_usage.rb deleted file mode 100644 index aa4b596f0..000000000 --- a/definitions/reports/provisioning_usage.rb +++ /dev/null @@ -1,16 +0,0 @@ -module Checks - module Report - class Provisioning < ForemanMaintain::Report - metadata do - description 'Count hosts that have been provisioned in the last 3 months.' - end - - def run - sql = "hosts WHERE managed = true AND created_at >= current_date - interval '3 months'" - result = sql_count(sql) - - self.data = { managed_hosts_created_in_last_3_months: result } - end - end - end -end diff --git a/lib/foreman_maintain/report.rb b/lib/foreman_maintain/report.rb index 4bdbe0a7d..163cdb2f9 100644 --- a/lib/foreman_maintain/report.rb +++ b/lib/foreman_maintain/report.rb @@ -14,6 +14,8 @@ def sql_count(sql, column: '*', cte: '') def sql_as_count(selection, sql, cte: '') query = "#{cte} SELECT #{selection} AS COUNT FROM #{sql}" feature(:foreman_database).query(query).first['count'].to_i + rescue StandardError + nil end def sql_setting(name) @@ -22,6 +24,17 @@ def sql_setting(name) (result || {})['value'] end + def flatten(hash, prefix = '') + hash.each_with_object({}) do |(key, value), result| + new_key = "#{prefix}#{prefix.empty? ? '' : flatten_separator}#{key}" + if value.is_a? Hash + result.merge!(flatten(value, new_key)) + else + result[new_key] = value + end + end + end + def table_exists(table) sql_count("information_schema.tables WHERE table_name = '#{table}'").positive? end @@ -38,5 +51,9 @@ def __run__(execution) rescue StandardError => e set_warn(e.message) end + + def flatten_separator + '|' + end end end