diff --git a/app/models/concerns/orchestration/dhcp.rb b/app/models/concerns/orchestration/dhcp.rb index a7899c1089c..b60d577a301 100644 --- a/app/models/concerns/orchestration/dhcp.rb +++ b/app/models/concerns/orchestration/dhcp.rb @@ -108,6 +108,20 @@ def build_dhcp_record(record_mac) end end + def dhcp_filename(record_mac) + filename = operatingsystem.boot_filename(host) + if filename.include? "@@subdir@@" + if host.subnet&.tftp&.has_capability?(:TFTP, :bootloader_universe) + filename = filename.gsub("@@subdir@@", "host-config/#{record_mac.tr(':', '-').downcase}") + filename = filename.gsub(/\/grub\w*\.efi$/, "/boot.efi") + return filename.gsub(/\/shim\w*\.efi$/, "/boot-sb.efi") + else + return filename.gsub("@@subdir@@/", "") + end + end + filename + end + # returns a hash of dhcp record settings def dhcp_attrs(record_mac) raise ::Foreman::Exception.new(N_("DHCP not supported for this NIC")) unless dhcp? @@ -124,7 +138,7 @@ def dhcp_attrs(record_mac) if provision? dhcp_attr[:nextServer] = boot_server unless host.pxe_loader == 'None' - filename = operatingsystem.boot_filename(host) + filename = dhcp_filename(record_mac) dhcp_attr[:filename] = filename if filename.present? if jumpstart? jumpstart_arguments = os.jumpstart_params host, model.vendor_class diff --git a/app/models/concerns/orchestration/tftp.rb b/app/models/concerns/orchestration/tftp.rb index 7325fb5155b..3148fbb3dd1 100644 --- a/app/models/concerns/orchestration/tftp.rb +++ b/app/models/concerns/orchestration/tftp.rb @@ -87,7 +87,13 @@ def setTFTP(kind) logger.info "Deploying TFTP #{kind} configuration for #{host.name}" each_unique_feasible_tftp_proxy do |proxy| mac_addresses_for_provisioning.each do |mac_addr| - proxy.set(kind, mac_addr, :pxeconfig => content) + proxy.set(kind, mac_addr, { + :pxeconfig => content, + :targetos => host.operatingsystem.name.downcase, + :release => host.operatingsystem.release, + :arch => host.arch.name, + :bootfilename_efi => host.arch.bootfilename_efi, + }) end end else diff --git a/app/models/concerns/pxe_loader_support.rb b/app/models/concerns/pxe_loader_support.rb index c98632d7691..ff4c63b9b7c 100644 --- a/app/models/concerns/pxe_loader_support.rb +++ b/app/models/concerns/pxe_loader_support.rb @@ -5,7 +5,7 @@ module PxeLoaderSupport PXE_KINDS = { :PXELinux => /^(pxelinux.*|PXELinux (BIOS|UEFI))$/, :PXEGrub => /^(grub\/|Grub UEFI).*/, - :PXEGrub2 => /^(grub2\/|Grub2 (BIOS|UEFI|ELF)|http.*grub2\/).*/, + :PXEGrub2 => /(^Grub2 (BIOS|UEFI|ELF).*|\/?grub2\/)/, :iPXE => /^((iPXE|http.*\/ipxe-).*|ipxe\.efi|undionly\.kpxe)$/, }.with_indifferent_access.freeze @@ -26,11 +26,11 @@ def all_loaders_map(precision = 'x64', httpboot_host = "httpboot_host") "Grub UEFI" => "grub/grub#{precision}.efi", "Grub2 BIOS" => "grub2/grub#{precision}.0", "Grub2 ELF" => "grub2/grub#{precision}.elf", - "Grub2 UEFI" => "grub2/grub#{precision}.efi", - "Grub2 UEFI SecureBoot" => "grub2/shim#{precision}.efi", - "Grub2 UEFI HTTP" => "http://#{httpboot_host}/httpboot/grub2/grub#{precision}.efi", - "Grub2 UEFI HTTPS" => "https://#{httpboot_host}/httpboot/grub2/grub#{precision}.efi", - "Grub2 UEFI HTTPS SecureBoot" => "https://#{httpboot_host}/httpboot/grub2/shim#{precision}.efi", + "Grub2 UEFI" => "@@subdir@@/grub2/grub#{precision}.efi", + "Grub2 UEFI SecureBoot" => "@@subdir@@/grub2/shim#{precision}.efi", + "Grub2 UEFI HTTP" => "http://#{httpboot_host}/httpboot/@@subdir@@/grub2/grub#{precision}.efi", + "Grub2 UEFI HTTPS" => "https://#{httpboot_host}/httpboot/@@subdir@@/grub2/grub#{precision}.efi", + "Grub2 UEFI HTTPS SecureBoot" => "https://#{httpboot_host}/httpboot/@@subdir@@/grub2/shim#{precision}.efi", "iPXE Embedded" => nil, # renders directly as foreman_url('iPXE') "iPXE UEFI HTTP" => "http://#{httpboot_host}/httpboot/ipxe-#{precision}.efi", "iPXE Chain BIOS" => "undionly-ipxe.0", diff --git a/app/services/proxy_api/tftp.rb b/app/services/proxy_api/tftp.rb index dc4f0582ca7..4cf8ca3cf28 100644 --- a/app/services/proxy_api/tftp.rb +++ b/app/services/proxy_api/tftp.rb @@ -10,6 +10,10 @@ def initialize(args) # [+mac+] : MAC address # [+args+] : Hash containing # :pxeconfig => String containing the configuration + # :targetos => String containing the lowercase operating system name + # :release => String containing the operating system major and minor version + # :arch => String containing the operating system architecture + # :bootfilename_efi => String containing the architecture specific boot filename suffix # Returns : Boolean status def set(kind, mac, args) parse(post(args, "#{kind}/#{mac}")) diff --git a/test/factories/architecture.rb b/test/factories/architecture.rb index 24de8e3daa8..14b587f74cc 100644 --- a/test/factories/architecture.rb +++ b/test/factories/architecture.rb @@ -5,5 +5,9 @@ trait :for_snapshots_x86_64 do name { 'x86_64' } end + + trait :x64 do + name { 'x64' } + end end end diff --git a/test/factories/operatingsystem.rb b/test/factories/operatingsystem.rb index 8eeb54f7369..3448667081c 100644 --- a/test/factories/operatingsystem.rb +++ b/test/factories/operatingsystem.rb @@ -160,6 +160,17 @@ title { 'Red Hat Enterprise Linux 7.5' } end + factory :rhel9, class: Redhat do + name { 'RHEL' } + major { '9' } + minor { '0' } + type { 'Redhat' } + title { 'Red Hat Enterprise Linux 9.0' } + architectures { [FactoryBot.build(:architecture, :x64)] } + media { [FactoryBot.build(:rhel_for_snapshots)] } + ptables { [FactoryBot.build(:ptable, name: 'ptable')] } + end + factory :for_snapshots_centos_7_0, class: Redhat do name { 'CentOS' } major { '7' } diff --git a/test/models/concerns/pxe_loader_support_test.rb b/test/models/concerns/pxe_loader_support_test.rb index 9a431957234..7835f7c384d 100644 --- a/test/models/concerns/pxe_loader_support_test.rb +++ b/test/models/concerns/pxe_loader_support_test.rb @@ -32,18 +32,28 @@ def setup assert_equal :PXEGrub, @subject.pxe_loader_kind(@host) end - test "PXEGrub2 is found for given filename" do - @host.pxe_loader = "grub2/grubx64.efi" + test "PXEGrub2 is found for grubx64.elf filename" do + @host.pxe_loader = "grub2/grubx64.elf" + assert_equal :PXEGrub2, @subject.pxe_loader_kind(@host) + end + + test "PXEGrub2 is found for grubx64.0 filename" do + @host.pxe_loader = "grub2/grubx64.0" + assert_equal :PXEGrub2, @subject.pxe_loader_kind(@host) + end + + test "PXEGrub2 is found for grubx64.efi filename" do + @host.pxe_loader = "host-config/#{@host.mac.tr(':', '-')}/grub2/grubx64.efi" assert_equal :PXEGrub2, @subject.pxe_loader_kind(@host) end test "PXEGrub2 is found for shimx64.efi filename" do - @host.pxe_loader = "grub2/shimx64.efi" + @host.pxe_loader = "host-config/#{@host.mac.tr(':', '-')}/grub2/shimx64.efi" assert_equal :PXEGrub2, @subject.pxe_loader_kind(@host) end test "PXEGrub2 is found for shimia32.efi filename" do - @host.pxe_loader = "grub2/shimia32.efi" + @host.pxe_loader = "host-config/#{@host.mac.tr(':', '-')}/grub2/shimia32.efi" assert_equal :PXEGrub2, @subject.pxe_loader_kind(@host) end @@ -88,22 +98,17 @@ def setup end test "PXEGrub2 is found for http://smart_proxy/tftp/grub2/grubx64.efi filename" do - @host.pxe_loader = "http://smart_proxy/tftp/grub2/grubx64.efi" + @host.pxe_loader = "http://smart_proxy/tftp/host-config/#{@host.mac.tr(':', '-')}/grub2/grubx64.efi" assert_equal :PXEGrub2, @subject.pxe_loader_kind(@host) end test "PXEGrub2 is found for https://smart_proxy/tftp/grub2/grubx64.efi filename" do - @host.pxe_loader = "https://smart_proxy/tftp/grub2/grubx64.efi" - assert_equal :PXEGrub2, @subject.pxe_loader_kind(@host) - end - - test "PXEGrub2 is found for https://smart_proxy/tftp/grub2/shimx64.efi filename" do - @host.pxe_loader = "https://smart_proxy/tftp/grub2/shimx64.efi" + @host.pxe_loader = "https://smart_proxy/tftp/host-config/#{@host.mac.tr(':', '-')}/grub2/grubx64.efi" assert_equal :PXEGrub2, @subject.pxe_loader_kind(@host) end test "PXEGrub2 is found for https://smart_proxy/tftp/grub2/shimx64.efi filename" do - @host.pxe_loader = "https://smart_proxy/tftp/grub2/shimx64.efi" + @host.pxe_loader = "https://smart_proxy/tftp/host-config/#{@host.mac.tr(':', '-')}/grub2/shimx64.efi" assert_equal :PXEGrub2, @subject.pxe_loader_kind(@host) end diff --git a/test/models/operatingsystem_test.rb b/test/models/operatingsystem_test.rb index 825934bfede..63ca371ae77 100644 --- a/test/models/operatingsystem_test.rb +++ b/test/models/operatingsystem_test.rb @@ -379,13 +379,13 @@ class OperatingsystemTest < ActiveSupport::TestCase test 'should be the smart proxy and httpboot port for UEFI HTTP' do SmartProxy.any_instance.expects(:setting).with(:HTTPBoot, 'http_port').returns(1234) host = FactoryBot.build(:host, :managed, :with_tftp_and_httpboot_subnet, pxe_loader: 'Grub2 UEFI HTTP') - assert_match(%r{http://somewhere.*net:1234/httpboot/grub2/grubx64.efi}, host.operatingsystem.boot_filename(host)) + assert_match(%r{http://somewhere.*net:1234/httpboot/@@subdir@@/grub2/grubx64.efi}, host.operatingsystem.boot_filename(host)) end test 'should be the smart proxy and httpboot port for UEFI HTTPS' do SmartProxy.any_instance.expects(:setting).with(:HTTPBoot, 'https_port').returns(1235) host = FactoryBot.build(:host, :managed, :with_tftp_and_httpboot_subnet, pxe_loader: 'Grub2 UEFI HTTPS') - assert_match(%r{https://somewhere.*net:1235/httpboot/grub2/grubx64.efi}, host.operatingsystem.boot_filename(host)) + assert_match(%r{https://somewhere.*net:1235/httpboot/@@subdir@@/grub2/grubx64.efi}, host.operatingsystem.boot_filename(host)) end test 'should not raise an error without httpboot feature for PXE' do diff --git a/test/models/orchestration/dhcp_test.rb b/test/models/orchestration/dhcp_test.rb index feb7cad8e52..26ec9c43526 100644 --- a/test/models/orchestration/dhcp_test.rb +++ b/test/models/orchestration/dhcp_test.rb @@ -371,7 +371,8 @@ def host_with_loader(loader) h.build = true assert h.valid?, h.errors.messages.to_s - assert_equal ["dhcp_remove_aa:bb:cc:dd:ee:f1", "dhcp_create_aa:bb:cc:dd:ee:f1"], h.queue.task_ids + assert_includes h.queue.task_ids, "dhcp_remove_aa:bb:cc:dd:ee:f1" + assert_includes h.queue.task_ids, "dhcp_create_aa:bb:cc:dd:ee:f1" end test "when an existing host trigger a 'rebuild', its dhcp records should not be updated if valid dhcp records are found" do @@ -383,7 +384,8 @@ def host_with_loader(loader) h.build = true assert h.valid? assert_empty h.errors - assert_equal ["dhcp_create_aa:bb:cc:dd:ee:f1"], h.queue.task_ids + assert_includes h.queue.task_ids, "dhcp_create_aa:bb:cc:dd:ee:f1" + assert_not_includes h.queue.task_ids, "dhcp_remove_aa:bb:cc:dd:ee:f1" end test "when an existing host change its bmc mac address, its dhcp record should be updated" do diff --git a/test/models/orchestration/tftp_test.rb b/test/models/orchestration/tftp_test.rb index f9d2fbde25a..6b8a8eda699 100644 --- a/test/models/orchestration/tftp_test.rb +++ b/test/models/orchestration/tftp_test.rb @@ -5,7 +5,8 @@ class TFTPOrchestrationTest < ActiveSupport::TestCase context 'host without tftp orchestration' do setup do - @host = FactoryBot.create(:host) + os = FactoryBot.create(:rhel9) + @host = FactoryBot.create(:host, :operatingsystem => os) end test 'should not have any tftp' do @@ -30,7 +31,8 @@ class TFTPOrchestrationTest < ActiveSupport::TestCase context 'host with ipv4 tftp' do setup do - @host = FactoryBot.build_stubbed(:host, :managed, :with_tftp_orchestration, :build => true) + os = FactoryBot.create(:rhel9) + @host = FactoryBot.build_stubbed(:host, :managed, :with_tftp_orchestration, :build => true, :operatingsystem => os) end test 'should have tftp' do @@ -62,7 +64,8 @@ class TFTPOrchestrationTest < ActiveSupport::TestCase context 'host with ipv6 tftp' do setup do - @host = FactoryBot.build_stubbed(:host, :managed, :with_tftp_v6_orchestration, :build => true) + os = FactoryBot.create(:rhel9) + @host = FactoryBot.build_stubbed(:host, :managed, :with_tftp_v6_orchestration, :build => true, :operatingsystem => os) end test "should have ipv6 tftp" do @@ -89,7 +92,8 @@ class TFTPOrchestrationTest < ActiveSupport::TestCase context 'host with ipv4 and ipv6 tftp' do setup do - @host = FactoryBot.build_stubbed(:host, :managed, :with_tftp_dual_stack_orchestration, :build => true) + os = FactoryBot.create(:rhel9) + @host = FactoryBot.build_stubbed(:host, :managed, :with_tftp_dual_stack_orchestration, :build => true, :operatingsystem => os) end test "host should have ipv4 and ipv6 tftp" do @@ -145,6 +149,9 @@ class TFTPOrchestrationTest < ActiveSupport::TestCase ), ] end + let(:os) do + FactoryBot.create(:rhel9) + end let(:host) do FactoryBot.create(:host, :with_tftp_orchestration, @@ -152,19 +159,32 @@ class TFTPOrchestrationTest < ActiveSupport::TestCase :interfaces => interfaces, :build => true, :location => subnet.locations.first, - :organization => subnet.organizations.first) + :organization => subnet.organizations.first, + :operatingsystem => os) end test '#setTFTP should provision tftp for all bond child macs' do ProxyAPI::TFTP.any_instance.expects(:set).with( 'PXEGrub2', '00:53:67:ab:dd:00', - :pxeconfig => 'Template' + { + :pxeconfig => 'Template', + :targetos => os.name.downcase.to_s, + :release => host.operatingsystem.release, + :arch => host.arch.name, + :bootfilename_efi => host.arch.bootfilename_efi, + } ).once ProxyAPI::TFTP.any_instance.expects(:set).with( 'PXEGrub2', '00:53:67:ab:dd:01', - :pxeconfig => 'Template' + { + :pxeconfig => 'Template', + :targetos => os.name.downcase.to_s, + :release => host.operatingsystem.release, + :arch => host.arch.name, + :bootfilename_efi => host.arch.bootfilename_efi, + } ).once host.provision_interface.stubs(:generate_pxe_template).returns('Template') host.provision_interface.send(:setTFTP, 'PXEGrub2')