diff --git a/app/models/manageiq/providers/openshift/container_manager.rb b/app/models/manageiq/providers/openshift/container_manager.rb index a26f012..d59c07b 100644 --- a/app/models/manageiq/providers/openshift/container_manager.rb +++ b/app/models/manageiq/providers/openshift/container_manager.rb @@ -3,6 +3,13 @@ class ManageIQ::Providers::Openshift::ContainerManager < ManageIQ::Providers::Kubernetes::ContainerManager DEFAULT_EXTERNAL_LOGGING_ROUTE_NAME = "logging-kibana-ops".freeze + has_one :infra_manager, + :foreign_key => :parent_ems_id, + :class_name => "ManageIQ::Providers::Openshift::InfraManager", + :autosave => true, + :inverse_of => :parent_manager, + :dependent => :destroy + include ManageIQ::Providers::Openshift::ContainerManager::Options supports :catalog @@ -21,6 +28,20 @@ def self.event_monitor_class ManageIQ::Providers::Openshift::ContainerManager::EventCatcher end + def virtualization_options + [ + { + :label => _('Disabled'), + :value => 'none', + }, + { + :label => _('OpenShift Virtualization'), + :value => 'openshift_infra', + :pivot => 'endpoints.openshift_infra.hostname', + }, + ] + end + def self.raw_connect(hostname, port, options) options[:service] ||= "openshift" send("#{options[:service]}_connect", hostname, port, options) diff --git a/app/models/manageiq/providers/openshift/infra_manager.rb b/app/models/manageiq/providers/openshift/infra_manager.rb new file mode 100644 index 0000000..fedbc45 --- /dev/null +++ b/app/models/manageiq/providers/openshift/infra_manager.rb @@ -0,0 +1,34 @@ +ManageIQ::Providers::Kubevirt::InfraManager.include(ActsAsStiLeafClass) + +class ManageIQ::Providers::OpenShift::InfraManager < ManageIQ::Providers::Kubevirt::InfraManager + belongs_to :parent_manager, + :foreign_key => :parent_ems_id, + :inverse_of => :infra_manager, + :class_name => "ManageIQ::Providers::Openshift::ContainerManager" + + # + # This is the list of features that this provider supports: + # + supports :catalog + supports :provisioning + + def self.ems_type + @ems_type ||= "openshift_infra".freeze + end + + def self.description + @description ||= "OpenShift Virtualization".freeze + end + + def self.catalog_types + {"openshift" => N_("OpenShift Virtualization")} + end + + def default_authentication_type + :openshift + end + + def self.display_name(number = 1) + n_('Infrastructure Provider (OpenShift Virtualization)', 'Infrastructure Providers (OpenShift Virtualization)', number) + end +end diff --git a/app/models/manageiq/providers/openshift/infra_manager/cluster.rb b/app/models/manageiq/providers/openshift/infra_manager/cluster.rb new file mode 100644 index 0000000..fb1d5ee --- /dev/null +++ b/app/models/manageiq/providers/openshift/infra_manager/cluster.rb @@ -0,0 +1,4 @@ +ManageIQ::Providers::Kubevirt::InfraManager::Cluster.include(ActsAsStiLeafClass) + +class ManageIQ::Providers::Openshift::InfraManager::Cluster < ManageIQ::Providers::Kubevirt::InfraManager::Cluster +end diff --git a/app/models/manageiq/providers/openshift/infra_manager/connection.rb b/app/models/manageiq/providers/openshift/infra_manager/connection.rb new file mode 100644 index 0000000..038067d --- /dev/null +++ b/app/models/manageiq/providers/openshift/infra_manager/connection.rb @@ -0,0 +1,4 @@ +ManageIQ::Providers::Kubevirt::InfraManager::Connection.include(ActsAsStiLeafClass) + +class ManageIQ::Providers::Openshift::InfraManager::Connection < ManageIQ::Providers::Kubevirt::InfraManager::Connection +end diff --git a/app/models/manageiq/providers/openshift/infra_manager/host.rb b/app/models/manageiq/providers/openshift/infra_manager/host.rb new file mode 100644 index 0000000..cbba263 --- /dev/null +++ b/app/models/manageiq/providers/openshift/infra_manager/host.rb @@ -0,0 +1,4 @@ +ManageIQ::Providers::Kubevirt::InfraManager::Host.include(ActsAsStiLeafClass) + +class ManageIQ::Providers::Openshift::InfraManager::Host < ManageIQ::Providers::Kubevirt::InfraManager::Host +end diff --git a/app/models/manageiq/providers/openshift/infra_manager/provision.rb b/app/models/manageiq/providers/openshift/infra_manager/provision.rb new file mode 100644 index 0000000..2539ce1 --- /dev/null +++ b/app/models/manageiq/providers/openshift/infra_manager/provision.rb @@ -0,0 +1,4 @@ +ManageIQ::Providers::Kubevirt::InfraManager::Provision.include(ActsAsStiLeafClass) + +class ManageIQ::Providers::Openshift::InfraManager::Provision < ManageIQ::Providers::Kubevirt::InfraManager::Provision +end diff --git a/app/models/manageiq/providers/openshift/infra_manager/provision_workflow.rb b/app/models/manageiq/providers/openshift/infra_manager/provision_workflow.rb new file mode 100644 index 0000000..e7d80df --- /dev/null +++ b/app/models/manageiq/providers/openshift/infra_manager/provision_workflow.rb @@ -0,0 +1,9 @@ +class ManageIQ::Providers::Openshift::InfraManager::ProvisionWorkflow < ManageIQ::Providers::Kubevirt::InfraManager::ProvisionWorkflow + def self.provider_model + ManageIQ::Providers::Openshift::InfraManager + end + + def dialog_name_from_automate(message = 'get_dialog_name') + super(message, {'platform' => 'openshift'}) + end +end diff --git a/app/models/manageiq/providers/openshift/infra_manager/refresh_worker.rb b/app/models/manageiq/providers/openshift/infra_manager/refresh_worker.rb new file mode 100644 index 0000000..b062ec8 --- /dev/null +++ b/app/models/manageiq/providers/openshift/infra_manager/refresh_worker.rb @@ -0,0 +1,2 @@ +class ManageIQ::Providers::Openshift::InfraManager::RefreshWorker < ManageIQ::Providers::Kubevirt::InfraManager::RefreshWorker +end diff --git a/app/models/manageiq/providers/openshift/infra_manager/refresh_worker/runner.rb b/app/models/manageiq/providers/openshift/infra_manager/refresh_worker/runner.rb new file mode 100644 index 0000000..bebf520 --- /dev/null +++ b/app/models/manageiq/providers/openshift/infra_manager/refresh_worker/runner.rb @@ -0,0 +1,168 @@ +require 'concurrent/atomic/atomic_boolean' + +class ManageIQ::Providers::Openshift::InfraManager::RefreshWorker::Runner < ManageIQ::Providers::Kubevirt::InfraManager::RefreshWorker::Runner + + private + + # + # Returns the reference to the manager. + # + # @return [ManageIQ::Providers::Kubevirt::InfraManager] The manager. + # + def manager + @manager ||= ManageIQ::Providers::Openshift::InfraManager.find(@cfg[:ems_id]) + end + + # + # Returns the refresh memory. + # + # @return [ManageIQ::Providers::Kubevirt::RefreshMemory] The refresh memory. + # + def memory + @memory ||= ManageIQ::Providers::Openshift::RefreshMemory.new + end + + # + # Performs a full refresh. + # + def full_refresh + # Create and populate the collector, persister and parser + # and parse inventories + inventory = ManageIQ::Providers::Openshift::Inventory.build(manager, nil) + collector = inventory.collector + persister = inventory.parse + + # execute persist: + persister&.persist! + + # Update the memory: + memory.add_list_version(:nodes, collector.nodes.resource_version) + memory.add_list_version(:vms, collector.vms.resource_version) + memory.add_list_version(:vm_instances, collector.vm_instances.resource_version) + memory.add_list_version(:templates, collector.templates.resource_version) + + manager.update(:last_refresh_error => nil, :last_refresh_date => Time.now.utc) + rescue StandardError => error + _log.error('Full refresh failed.') + _log.log_backtrace(error) + manager.update(:last_refresh_error => error.to_s, :last_refresh_date => Time.now.utc) + end + + # + # Performs a partial refresh. + # + # @param notices [Array] The set of notices to process. + # + def partial_refresh(notices) + # check whether we get error about stale resource version + if notices.any? { |notice| notice.object&.kind == "Status" && notice.object&.code == 410 } + # base on the structure we do not know which watch uses stale version so we stop all + # we can't join with all the threads since we are in one + stop_watches + + # clear queue + @queue.clear + + # get the latest state and resource versions + full_refresh + + # restart watches + start_watches + return + end + + # Filter out the notices that have already been processed: + notices.reject! do |notice| + if memory.contains_notice?(notice) + _log.info( + "Notice of kind '#{notice.kind}, id '#{notice.uid}', type '#{notice.type}' and version '#{notice.resource_version}' " \ + "has already been processed, will ignore it." + ) + true + else + false + end + end + + # The notices returned by the Kubernetes API contain always the complete representation of the object, so it isn't + # necessary to process all of them, only the last one for each object. + relevant = notices.reverse! + relevant.uniq!(&:uid) + relevant.reverse! + + # Create and populate the collector: + collector = ManageIQ::Providers::Openshift::Inventory::Collector.new(manager, nil) + collector.nodes = notices_of_kind(relevant, 'Node') + collector.vms = notices_of_kind(relevant, 'VirtualMachine') + collector.vm_instances = notices_of_kind(relevant, 'VirtualMachineInstance') + collector.templates = notices_of_kind(relevant, 'VirtualMachineTemplate') + + # Create the parser and persister, wire them, and execute the persist: + persister = ManageIQ::Providers::Openshift::Inventory::Persister.new(manager, nil) + parser = ManageIQ::Providers::Openshift::Inventory::Parser::PartialRefresh.new + parser.collector = collector + parser.persister = persister + parser.parse + persister.persist! + + # Update the memory: + notices.each do |notice| + memory.add_notice(notice) + end + + manager.update(:last_refresh_error => nil, :last_refresh_date => Time.now.utc) + rescue StandardError => error + _log.error('Partial refresh failed.') + _log.log_backtrace(error) + manager.update(:last_refresh_error => error.to_s, :last_refresh_date => Time.now.utc) + end + + # + # Returns the notices that contain objects of the given kind. + # + # @param notices [Array] An array of notices. + # @param kind [String] The kind of object, for example `Node`. + # @return [Array] An array containing the notices that have the given kind. + # + def notices_of_kind(notices, kind) + notices.select { |notice| notice.kind == kind } + end + + # + # Start watches + # + def start_watches + # This flag will be used to tell the threads to get out of their loops: + @finish = Concurrent::AtomicBoolean.new(false) + + # Create the watches: + @watches = [] + manager.with_provider_connection do |connection| + @watches << connection.watch_nodes(:resource_version => memory.get_list_version(:nodes)) + @watches << connection.watch_vms(:resource_version => memory.get_list_version(:vms)) + @watches << connection.watch_vm_instances(:resource_version => memory.get_list_version(:vm_instances)) + @watches << connection.watch_templates(:resource_version => memory.get_list_version(:templates)) + end + + # Create the threads that run the watches and put the notices in the queue: + @watchers = [] + @watches.each do |watch| + thread = Thread.new do + until @finish.value + watch.each do |notice| + @queue.push(notice) + end + end + end + @watchers << thread + end + end + + # + # Stop all the watches + # + def stop_watches + @finish&.value = true + @watches&.each(&:finish) + end +end diff --git a/app/models/manageiq/providers/openshift/infra_manager/refresher.rb b/app/models/manageiq/providers/openshift/infra_manager/refresher.rb new file mode 100644 index 0000000..6b6270a --- /dev/null +++ b/app/models/manageiq/providers/openshift/infra_manager/refresher.rb @@ -0,0 +1,2 @@ +class ManageIQ::Providers::Openshift::InfraManager::Refresher < ManageIQ::Providers::Kubevirt::InfraManager::Refresher +end diff --git a/app/models/manageiq/providers/openshift/infra_manager/storage.rb b/app/models/manageiq/providers/openshift/infra_manager/storage.rb new file mode 100644 index 0000000..91ab349 --- /dev/null +++ b/app/models/manageiq/providers/openshift/infra_manager/storage.rb @@ -0,0 +1,4 @@ +ManageIQ::Providers::Kubevirt::InfraManager::Storage.include(ActsAsStiLeafClass) + +class ManageIQ::Providers::Openshift::InfraManager::Storage < ManageIQ::Providers::Kubevirt::InfraManager::Storage +end diff --git a/app/models/manageiq/providers/openshift/infra_manager/template.rb b/app/models/manageiq/providers/openshift/infra_manager/template.rb new file mode 100644 index 0000000..7202258 --- /dev/null +++ b/app/models/manageiq/providers/openshift/infra_manager/template.rb @@ -0,0 +1,7 @@ +ManageIQ::Providers::Kubevirt::InfraManager::Template.include(ActsAsStiLeafClass) + +class ManageIQ::Providers::Openshift::InfraManager::Template < ManageIQ::Providers::Kubevirt::InfraManager::Template + def self.display_name(number = 1) + n_('Template (OpenShift Virtualization)', 'Templates (OpenShift Virtualization)', number) + end +end diff --git a/app/models/manageiq/providers/openshift/infra_manager/vm.rb b/app/models/manageiq/providers/openshift/infra_manager/vm.rb new file mode 100644 index 0000000..a5ab435 --- /dev/null +++ b/app/models/manageiq/providers/openshift/infra_manager/vm.rb @@ -0,0 +1,7 @@ +ManageIQ::Providers::Kubevirt::InfraManager::Vm.include(ActsAsStiLeafClass) + +class ManageIQ::Providers::Openshift::InfraManager::Vm < ManageIQ::Providers::Kubevirt::InfraManager::Vm + def self.display_name(number = 1) + n_('Virtual Machine (OpenShift Virtualization)', 'Virtual Machines (OpenShift Virtualization)', number) + end +end diff --git a/app/models/manageiq/providers/openshift/inventory/parser/partial_refresh.rb b/app/models/manageiq/providers/openshift/inventory/parser/partial_refresh.rb new file mode 100644 index 0000000..5b99b2b --- /dev/null +++ b/app/models/manageiq/providers/openshift/inventory/parser/partial_refresh.rb @@ -0,0 +1,5 @@ +# +# This class is responsible for parsing the inventory for partial refreshes. +# +class ManageIQ::Providers::Openshift::Inventory::Parser::PartialRefresh < ManageIQ::Providers::Kubevirt::Inventory::Parser::PartialRefresh +end