diff --git a/app/models/concerns/foreman_bootdisk/compute_resources/proxmox.rb b/app/models/concerns/foreman_bootdisk/compute_resources/proxmox.rb new file mode 100644 index 00000000..6157cbbe --- /dev/null +++ b/app/models/concerns/foreman_bootdisk/compute_resources/proxmox.rb @@ -0,0 +1,48 @@ +# frozen_string_literal: true + +module ForemanBootdisk + module ComputeResources + module Proxmox + def capabilities + super + [:bootdisk] + end + + def iso_upload(iso, vm_uuid) + server = find_vm_by_uuid(vm_uuid) + server.ssh_options = { password: fog_credentials[:proxmox_password] } + server.ssh_ip_address = proxmox_host + server.username = client.credentials[:current_user].split('@').first + server.scp_upload(iso, '/var/lib/vz/template/iso/') + server.reload + storage = storages(server.node_id, 'iso')[0] + storage.volumes.any? { |v| v.volid.include? File.basename(iso) } + end + + def iso_attach(iso, vm_uuid) + server = find_vm_by_uuid(vm_uuid) + storage = storages(server.node_id, 'iso')[0] + volume = storage.volumes.detect { |v| v.volid.include? File.basename(iso) } + disks = server.disks.map { |disk| disk.split(":")[0] }.join(";") + server.update({ ide2: "#{volume.volid},media=cdrom" }) + server.update({ boot: "order=ide2;#{disks}" }) + server.reboot + end + + def iso_detach(vm_uuid) + server = find_vm_by_uuid(vm_uuid) + + # get volid to delete iso after detaching from vm + volid = server.volumes.get("ide2").volid + server.update({ ide2: "none,media=cdrom" }) + + # cdrom will be ejected on next power off + server.detach('ide2') + + # delete the iso file from proxmox server + storage = storages(server.node_id, 'iso')[0] + volume = storage.volumes.detect { |v| v.volid.include? volid } + volume.destroy + end + end + end +end diff --git a/lib/foreman_bootdisk/engine.rb b/lib/foreman_bootdisk/engine.rb index 8eda7c11..dd023184 100644 --- a/lib/foreman_bootdisk/engine.rb +++ b/lib/foreman_bootdisk/engine.rb @@ -145,6 +145,7 @@ class Engine < ::Rails::Engine Host::Managed.prepend ForemanBootdisk::HostExt Host::Managed.include ForemanBootdisk::Orchestration::Compute Foreman::Model::Vmware.prepend ForemanBootdisk::ComputeResources::Vmware if Foreman::Model::Vmware.available? + ForemanFogProxmox::Proxmox.prepend ForemanBootdisk::ComputeResources::Proxmox if ForemanBootdisk.with_proxmox? rescue StandardError => e Rails.logger.warn "#{ForemanBootdisk::ENGINE_NAME}: skipping engine hook (#{e})" end @@ -154,4 +155,8 @@ class Engine < ::Rails::Engine def self.logger Foreman::Logging.logger('foreman_bootdisk') end + + def self.with_proxmox? + Foreman::Plugin.installed?('foreman_fog_proxmox') + end end diff --git a/test/test_plugin_helper.rb b/test/test_plugin_helper.rb index b4d6d9e0..b2b1b142 100644 --- a/test/test_plugin_helper.rb +++ b/test/test_plugin_helper.rb @@ -2,6 +2,10 @@ require 'test_helper' +FactoryBot.definition_file_paths << File.join(ForemanFogProxmox::Engine.root, 'test', 'factories') if defined?(ForemanFogProxmox::Engine) +FactoryBot.definition_file_paths << File.join(__dir__, 'factories') +FactoryBot.reload + module ForemanBootdiskTestHelper def create_tempfile file = Tempfile.new('bootdisk-test', '/tmp') diff --git a/test/unit/concerns/compute_resources/proxmox_test.rb b/test/unit/concerns/compute_resources/proxmox_test.rb new file mode 100644 index 00000000..4d1a5568 --- /dev/null +++ b/test/unit/concerns/compute_resources/proxmox_test.rb @@ -0,0 +1,19 @@ +# frozen_string_literal: true + +require 'test_plugin_helper' + +module ForemanBootdisk + class ProxmoxTest < ActiveSupport::TestCase + + describe '#capabilities' do + setup do + skip unless ForemanBootdisk.with_proxmox? + @cr = FactoryBot.build(:proxmox_cr) + end + + test 'should include bootdisk' do + assert_includes @cr.capabilities, :bootdisk + end + end + end +end diff --git a/test/unit/concerns/orchestration/proxmox_compute_test.rb b/test/unit/concerns/orchestration/proxmox_compute_test.rb new file mode 100644 index 00000000..c888e4d9 --- /dev/null +++ b/test/unit/concerns/orchestration/proxmox_compute_test.rb @@ -0,0 +1,70 @@ +# frozen_string_literal: true + +require 'test_plugin_helper' + +module ForemanBootdisk + class OrchestrationProxmoxComputeTest < ActiveSupport::TestCase + setup do + disable_orchestration + skip unless ForemanBootdisk.with_proxmox? + @proxmox_cr = FactoryBot.build(:proxmox_cr) + @proxmox_host = FactoryBot.build(:host, :managed, + compute_resource: @proxmox_cr, + provision_method: 'bootdisk') + end + + test 'provisioning a host with provision method bootdisk in proxmox should upload iso' do + @proxmox_cr.expects(:iso_upload) + @proxmox_host.send(:setIsoImage) + end + + test 'provisioning a host with provision method bootdisk in proxmox should attach iso' do + @proxmox_cr.expects(:iso_attach) + @proxmox_host.send(:setAttachIsoImage) + end + + test 'provisioning a host with provision method bootdisk in proxmox should detach iso' do + @proxmox_cr.expects(:iso_detach) + @proxmox_host.send(:setDetachIsoImage) + end + + test 'provisioning a new proxmox host with provision method bootdisk should queue bootdisk tasks' do + @proxmox_host.stubs(:compute?).returns(true) + @proxmox_host.stubs(:build?).returns(true) + @proxmox_host.send(:queue_bootdisk_compute) + tasks = @proxmox_host.queue.all.map(&:name) + assert_includes tasks, "Generating ISO image for #{@proxmox_host.name}" + assert_includes tasks, "Upload ISO image to datastore for #{@proxmox_host.name}" + assert_includes tasks, "Attach ISO image to CDROM drive for #{@proxmox_host.name}" + assert_not_includes tasks, "Detach ISO image from CDROM drive for #{@proxmox_host.name}" + end + + test 'rebuilding a proxmox host with provision method bootdisk should queue bootdisk tasks' do + @proxmox_host.stubs(:compute?).returns(true) + old = stub() + old.stubs(:build?).returns(false) + @proxmox_host.stubs(:old).returns(old) + @proxmox_host.stubs(:build?).returns(true) + @proxmox_host.send(:queue_bootdisk_compute) + tasks = @proxmox_host.queue.all.map(&:name) + assert_includes tasks, "Generating ISO image for #{@proxmox_host.name}" + assert_includes tasks, "Upload ISO image to datastore for #{@proxmox_host.name}" + assert_includes tasks, "Attach ISO image to CDROM drive for #{@proxmox_host.name}" + assert_not_includes tasks, "Detach ISO image from CDROM drive for #{@proxmox_host.name}" + end + + test 'the iso should be detached when the proxmox host leaves build mode' do + @proxmox_host.stubs(:compute?).returns(true) + old = stub() + old.stubs(:build?).returns(true) + @proxmox_host.stubs(:old).returns(old) + @proxmox_host.stubs(:build?).returns(false) + @proxmox_host.send(:queue_bootdisk_compute) + tasks = @proxmox_host.queue.all.map(&:name) + assert_not_includes tasks, "Generating ISO image for #{@proxmox_host.name}" + assert_not_includes tasks, "Upload ISO image to datastore for #{@proxmox_host.name}" + assert_not_includes tasks, "Attach ISO image to CDROM drive for #{@proxmox_host.name}" + assert_includes tasks, "Detach ISO image from CDROM drive for #{@proxmox_host.name}" + end + end +end