From 8b1a5e2ad0bfd40dd27c9cace69ed44ce556aa09 Mon Sep 17 00:00:00 2001 From: Manisha Date: Wed, 26 Feb 2020 16:46:29 +0100 Subject: [PATCH] Proxmox extention for foreman_bootdisk --- .github/workflows/ruby_ci.yaml | 1 + Gemfile | 4 ++ .../compute_resources/proxmox.rb | 48 +++++++++++++ lib/foreman_bootdisk/engine.rb | 5 ++ test/test_plugin_helper.rb | 4 ++ .../compute_resources/proxmox_test.rb | 19 +++++ .../orchestration/proxmox_compute_test.rb | 70 +++++++++++++++++++ 7 files changed, 151 insertions(+) create mode 100644 app/models/concerns/foreman_bootdisk/compute_resources/proxmox.rb create mode 100644 test/unit/concerns/compute_resources/proxmox_test.rb create mode 100644 test/unit/concerns/orchestration/proxmox_compute_test.rb diff --git a/.github/workflows/ruby_ci.yaml b/.github/workflows/ruby_ci.yaml index 56cf1958..c9186e30 100644 --- a/.github/workflows/ruby_ci.yaml +++ b/.github/workflows/ruby_ci.yaml @@ -45,6 +45,7 @@ jobs: - name: Setup Bundler run: | echo "gem 'foreman_bootdisk', path: './foreman_bootdisk'" > bundler.d/foreman_bootdisk.local.rb + echo "gem 'foreman_fog_proxmox'" >> bundler.d/foreman_bootdisk.local.rb - name: Setup Ruby uses: ruby/setup-ruby@v1 with: diff --git a/Gemfile b/Gemfile index 2c29cb91..245ce938 100644 --- a/Gemfile +++ b/Gemfile @@ -4,4 +4,8 @@ source 'http://rubygems.org' gem 'theforeman-rubocop', '~> 0.1.0.pre', groups: %i[development rubocop] +group :test do + eval_gemfile 'gemfile.d/proxmox.rb' +end + gemspec 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..6c9715d2 --- /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 = server.node_id + 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