From ae6cb5fadad9bb01c857873fe79d050ca9b39ee7 Mon Sep 17 00:00:00 2001 From: Bastian Schmidt Date: Tue, 19 Nov 2024 09:42:27 +0100 Subject: [PATCH] Introduce host_resources model The HostResources extend the Host::Managed by creating a table to track the quota-related resources of a host. Moreover, the quota-host relation is replaced by an additional ResourceQuotaHost table. --- .../host_managed_extensions.rb | 44 ++++++++++++++----- .../foreman_resource_quota/host_resources.rb | 43 ++++++++++++++++++ .../foreman_resource_quota/resource_quota.rb | 8 ++-- .../resource_quota_host.rb | 10 +++++ .../resource_quota_missing_host.rb | 10 ----- ...remove_utilization_from_resource_quotas.rb | 9 ++++ .../20240611141939_drop_missing_hosts.rb | 7 +++ .../20240611142813_create_hosts_resources.rb | 21 +++++++++ ...163434_remove_resource_quota_from_hosts.rb | 7 +++ 9 files changed, 133 insertions(+), 26 deletions(-) create mode 100644 app/models/foreman_resource_quota/host_resources.rb create mode 100644 app/models/foreman_resource_quota/resource_quota_host.rb delete mode 100644 app/models/foreman_resource_quota/resource_quota_missing_host.rb create mode 100644 db/migrate/20240611141744_remove_utilization_from_resource_quotas.rb create mode 100644 db/migrate/20240611141939_drop_missing_hosts.rb create mode 100644 db/migrate/20240611142813_create_hosts_resources.rb create mode 100644 db/migrate/20240618163434_remove_resource_quota_from_hosts.rb diff --git a/app/models/concerns/foreman_resource_quota/host_managed_extensions.rb b/app/models/concerns/foreman_resource_quota/host_managed_extensions.rb index 85b79b3..989a555 100644 --- a/app/models/concerns/foreman_resource_quota/host_managed_extensions.rb +++ b/app/models/concerns/foreman_resource_quota/host_managed_extensions.rb @@ -7,16 +7,22 @@ module HostManagedExtensions include ForemanResourceQuota::Exceptions included do - validate :check_resource_quota_capacity - - belongs_to :resource_quota, class_name: '::ForemanResourceQuota::ResourceQuota' - has_one :resource_quota_missing_resources, class_name: '::ForemanResourceQuota::ResourceQuotaMissingHost', - inverse_of: :missing_host, foreign_key: :missing_host_id, dependent: :destroy + validate :verify_resource_quota + + has_one :host_resources, class_name: '::ForemanResourceQuota::HostResources', + inverse_of: :host, foreign_key: :host_id, dependent: :destroy + has_one :resource_quota_host, class_name: '::ForemanResourceQuota::ResourceQuotaHost', + inverse_of: :host, foreign_key: :host_id, dependent: :destroy + has_one :resource_quota, class_name: '::ForemanResourceQuota::ResourceQuota', + through: :resource_quota_host scoped_search relation: :resource_quota, on: :name, complete_value: true, rename: :resource_quota + + # A host shall always have a .host_resources attribute + before_validation :build_host_resources, unless: -> { host_resources.present? } end - def check_resource_quota_capacity - handle_quota_check + def verify_resource_quota + handle_quota_check(resource_quota) true rescue ResourceQuotaException => e handle_error('resource_quota_id', @@ -32,13 +38,27 @@ def check_resource_quota_capacity format('An unknown error occured while checking the resource quota capacity: %s', e)) end + def resource_quota_id + resource_quota&.id + end + + def resource_quota_id=(val) + if val.blank? + resource_quota_host&.destroy + else + quota = ForemanResourceQuota::ResourceQuota.find_by(id: val) + raise ActiveRecord::RecordNotFound, "ResourceQuota with ID \"#{val}\" not found" unless quota + self.resource_quota = quota + end + end + private - def handle_quota_check - return if early_return? - quota_utilization = determine_quota_utilization - host_resources = determine_host_resources - verify_resource_quota_limits(quota_utilization, host_resources) + def handle_quota_check(quota) + return if early_return?(quota) + quota_utilization = determine_quota_utilization(quota) + current_host_resources = determine_host_resources(quota.active_resources) + check_resource_quota_limits(quota, quota_utilization, current_host_resources) end def handle_error(error_module, error_message, log_message) diff --git a/app/models/foreman_resource_quota/host_resources.rb b/app/models/foreman_resource_quota/host_resources.rb new file mode 100644 index 0000000..7230c50 --- /dev/null +++ b/app/models/foreman_resource_quota/host_resources.rb @@ -0,0 +1,43 @@ +# frozen_string_literal: true + +module ForemanResourceQuota + class HostResources < ApplicationRecord + self.table_name = 'hosts_resources' + + belongs_to :host, class_name: '::Host::Managed' + validates :host, { presence: true, uniqueness: true } + + def resources + { + cpu_cores: cpu_cores, + memory_mb: memory_mb, + disk_gb: disk_gb, + } + end + + def resources=(val) + allowed_attributes = val.slice(:cpu_cores, :memory_mb, :disk_gb) + assign_attributes(allowed_attributes) # Set multiple attributes at once (given a hash) + end + + # Returns an array of unknown host resources (returns an empty array if all are known) + # For example, completely unknown host resources returns: + # [ + # :cpu_cores, + # :memory_mb, + # :disk_gb, + # ] + # Consider only the resource_quota's active resources by default. + def missing_resources(only_active_resources: true) + empty_resources = [] + resources_to_check = %i[cpu_cores memory_mb disk_gb] + resources_to_check = host.resource_quota.active_resources if only_active_resources && host.resource_quota.present? + + resources_to_check.each do |single_resource| + empty_resources << single_resource if send(single_resource).nil? + end + + empty_resources + end + end +end diff --git a/app/models/foreman_resource_quota/resource_quota.rb b/app/models/foreman_resource_quota/resource_quota.rb index 04ffcd4..509f5ae 100644 --- a/app/models/foreman_resource_quota/resource_quota.rb +++ b/app/models/foreman_resource_quota/resource_quota.rb @@ -12,12 +12,12 @@ class ResourceQuota < ApplicationRecord self.table_name = 'resource_quotas' + has_many :resource_quotas_hosts, class_name: 'ResourceQuotaHost', inverse_of: :resource_quota, dependent: :destroy has_many :resource_quotas_users, class_name: 'ResourceQuotaUser', inverse_of: :resource_quota, dependent: :destroy has_many :resource_quotas_usergroups, class_name: 'ResourceQuotaUsergroup', inverse_of: :resource_quota, dependent: :destroy - has_many :resource_quotas_missing_hosts, class_name: 'ResourceQuotaMissingHost', inverse_of: :resource_quota, - dependent: :destroy - has_many :hosts, class_name: '::Host::Managed', dependent: :nullify + has_many :hosts, -> { distinct }, class_name: '::Host::Managed', through: :resource_quotas_hosts + has_many :hosts_resources, class_name: 'HostResources', through: :hosts has_many :users, class_name: '::User', through: :resource_quotas_users has_many :usergroups, class_name: '::Usergroup', through: :resource_quotas_usergroups @@ -54,7 +54,7 @@ def number_of_missing_hosts def missing_hosts(exclude: []) missing_hosts = {} active_resources.each do |single_resource| - hosts_resources.where(single_resource => nil).find_each do |host_resources_item| + hosts_resources.where(single_resource => nil).includes([:host]).find_each do |host_resources_item| host_name = host_resources_item.host.name next if exclude.include?(host_name) missing_hosts[host_name] ||= [] diff --git a/app/models/foreman_resource_quota/resource_quota_host.rb b/app/models/foreman_resource_quota/resource_quota_host.rb new file mode 100644 index 0000000..59725c7 --- /dev/null +++ b/app/models/foreman_resource_quota/resource_quota_host.rb @@ -0,0 +1,10 @@ +# frozen_string_literal: true + +module ForemanResourceQuota + class ResourceQuotaHost < ApplicationRecord + self.table_name = 'resource_quotas_hosts' + + belongs_to :resource_quota, class_name: 'ResourceQuota' + belongs_to :host, class_name: '::Host::Managed' + end +end diff --git a/app/models/foreman_resource_quota/resource_quota_missing_host.rb b/app/models/foreman_resource_quota/resource_quota_missing_host.rb deleted file mode 100644 index 00786b8..0000000 --- a/app/models/foreman_resource_quota/resource_quota_missing_host.rb +++ /dev/null @@ -1,10 +0,0 @@ -# frozen_string_literal: true - -module ForemanResourceQuota - class ResourceQuotaMissingHost < ApplicationRecord - self.table_name = 'resource_quotas_missing_hosts' - - belongs_to :resource_quota, inverse_of: :resource_quotas_missing_hosts - belongs_to :missing_host, class_name: '::Host::Managed', inverse_of: :resource_quota_missing_resources - end -end diff --git a/db/migrate/20240611141744_remove_utilization_from_resource_quotas.rb b/db/migrate/20240611141744_remove_utilization_from_resource_quotas.rb new file mode 100644 index 0000000..81adbaf --- /dev/null +++ b/db/migrate/20240611141744_remove_utilization_from_resource_quotas.rb @@ -0,0 +1,9 @@ +# frozen_string_literal: true + +class RemoveUtilizationFromResourceQuotas < ActiveRecord::Migration[6.1] + def change + remove_column :resource_quotas, :utilization_cpu_cores, :integer + remove_column :resource_quotas, :utilization_memory_mb, :integer + remove_column :resource_quotas, :utilization_disk_gb, :integer + end +end diff --git a/db/migrate/20240611141939_drop_missing_hosts.rb b/db/migrate/20240611141939_drop_missing_hosts.rb new file mode 100644 index 0000000..83222bb --- /dev/null +++ b/db/migrate/20240611141939_drop_missing_hosts.rb @@ -0,0 +1,7 @@ +# frozen_string_literal: true + +class DropMissingHosts < ActiveRecord::Migration[6.1] + def up + drop_table :resource_quotas_missing_hosts + end +end diff --git a/db/migrate/20240611142813_create_hosts_resources.rb b/db/migrate/20240611142813_create_hosts_resources.rb new file mode 100644 index 0000000..3f4e5fe --- /dev/null +++ b/db/migrate/20240611142813_create_hosts_resources.rb @@ -0,0 +1,21 @@ +# frozen_string_literal: true + +class CreateHostsResources < ActiveRecord::Migration[6.1] + def change + create_table :hosts_resources do |t| + t.belongs_to :host, index: { unique: true }, foreign_key: true, null: false + t.integer :cpu_cores, default: nil + t.integer :memory_mb, default: nil + t.integer :disk_gb, default: nil + + t.timestamps + end + + create_table :resource_quotas_hosts do |t| + t.belongs_to :host, index: { unique: true }, foreign_key: true, null: false + t.belongs_to :resource_quota, foreign_key: true, null: false + + t.timestamps + end + end +end diff --git a/db/migrate/20240618163434_remove_resource_quota_from_hosts.rb b/db/migrate/20240618163434_remove_resource_quota_from_hosts.rb new file mode 100644 index 0000000..30a2997 --- /dev/null +++ b/db/migrate/20240618163434_remove_resource_quota_from_hosts.rb @@ -0,0 +1,7 @@ +# frozen_string_literal: true + +class RemoveResourceQuotaFromHosts < ActiveRecord::Migration[6.1] + def change + remove_reference :hosts, :resource_quota, foreign_key: true + end +end