foreman_bootdisk 14.0.0 → 17.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (62) hide show
  1. checksums.yaml +4 -4
  2. data/AUTHORS +30 -0
  3. data/README.md +25 -0
  4. data/app/controllers/foreman_bootdisk/api/v2/disks_controller.rb +5 -7
  5. data/app/controllers/foreman_bootdisk/api/v2/subnet_disks_controller.rb +9 -4
  6. data/app/controllers/foreman_bootdisk/disks_controller.rb +11 -10
  7. data/app/helpers/concerns/foreman_bootdisk/hosts_helper_ext.rb +14 -14
  8. data/app/lib/foreman_bootdisk/scope/bootdisk.rb +4 -1
  9. data/app/lib/foreman_bootdisk/scope/full_host_bootdisk_efi.rb +15 -0
  10. data/app/models/concerns/foreman_bootdisk/compute_resources/vmware.rb +10 -1
  11. data/app/models/concerns/foreman_bootdisk/host_ext.rb +7 -10
  12. data/app/models/concerns/foreman_bootdisk/orchestration/compute.rb +28 -19
  13. data/app/models/setting/bootdisk.rb +16 -19
  14. data/app/services/foreman_bootdisk/iso_generator.rb +129 -52
  15. data/app/services/foreman_bootdisk/renderer.rb +36 -18
  16. data/app/views/foreman_bootdisk/generic_efi_host.erb +64 -0
  17. data/app/views/foreman_bootdisk/generic_static_host.erb +34 -0
  18. data/app/views/foreman_bootdisk/host.erb +27 -11
  19. data/db/seeds.d/50-bootdisk_templates.rb +15 -31
  20. data/lib/foreman_bootdisk/engine.rb +6 -6
  21. data/lib/foreman_bootdisk/version.rb +1 -1
  22. data/lib/tasks/bootdisk.rake +10 -6
  23. data/locale/action_names.rb +5 -0
  24. data/locale/ca/LC_MESSAGES/foreman_bootdisk.mo +0 -0
  25. data/locale/ca/foreman_bootdisk.po +65 -16
  26. data/locale/de/LC_MESSAGES/foreman_bootdisk.mo +0 -0
  27. data/locale/de/foreman_bootdisk.po +74 -23
  28. data/locale/en/LC_MESSAGES/foreman_bootdisk.mo +0 -0
  29. data/locale/en/foreman_bootdisk.po +58 -10
  30. data/locale/en_GB/LC_MESSAGES/foreman_bootdisk.mo +0 -0
  31. data/locale/en_GB/foreman_bootdisk.po +65 -17
  32. data/locale/es/LC_MESSAGES/foreman_bootdisk.mo +0 -0
  33. data/locale/es/foreman_bootdisk.po +82 -34
  34. data/locale/foreman_bootdisk.pot +124 -48
  35. data/locale/fr/LC_MESSAGES/foreman_bootdisk.mo +0 -0
  36. data/locale/fr/foreman_bootdisk.po +65 -17
  37. data/locale/it/LC_MESSAGES/foreman_bootdisk.mo +0 -0
  38. data/locale/it/foreman_bootdisk.po +63 -15
  39. data/locale/ja/LC_MESSAGES/foreman_bootdisk.mo +0 -0
  40. data/locale/ja/foreman_bootdisk.po +64 -16
  41. data/locale/ko/LC_MESSAGES/foreman_bootdisk.mo +0 -0
  42. data/locale/ko/foreman_bootdisk.po +63 -15
  43. data/locale/pt_BR/LC_MESSAGES/foreman_bootdisk.mo +0 -0
  44. data/locale/pt_BR/foreman_bootdisk.po +76 -27
  45. data/locale/ru/LC_MESSAGES/foreman_bootdisk.mo +0 -0
  46. data/locale/ru/foreman_bootdisk.po +63 -15
  47. data/locale/sv_SE/LC_MESSAGES/foreman_bootdisk.mo +0 -0
  48. data/locale/sv_SE/foreman_bootdisk.po +61 -13
  49. data/locale/zh_CN/LC_MESSAGES/foreman_bootdisk.mo +0 -0
  50. data/locale/zh_CN/foreman_bootdisk.po +79 -31
  51. data/locale/zh_TW/LC_MESSAGES/foreman_bootdisk.mo +0 -0
  52. data/locale/zh_TW/foreman_bootdisk.po +63 -15
  53. data/release-gem +84 -0
  54. data/test/functional/foreman_bootdisk/api/v2/disks_controller_test.rb +51 -17
  55. data/test/functional/foreman_bootdisk/api/v2/subnet_disks_controller_test.rb +23 -10
  56. data/test/functional/foreman_bootdisk/disks_controller_test.rb +65 -25
  57. data/test/test_plugin_helper.rb +23 -3
  58. data/test/unit/concerns/host_test.rb +12 -1
  59. data/test/unit/concerns/orchestration/compute_test.rb +32 -13
  60. data/test/unit/foreman_bootdisk/renderer_test.rb +1 -1
  61. data/test/unit/iso_generator_test.rb +16 -7
  62. metadata +65 -4
@@ -2,30 +2,27 @@
2
2
 
3
3
  class Setting
4
4
  class Bootdisk < ::Setting
5
- def self.load_defaults
6
- return unless ApplicationRecord.connection.table_exists?('settings')
7
- return unless super
8
-
5
+ def self.default_settings
9
6
  ipxe = ['/usr/lib/ipxe'].find { |p| File.exist?(p) } || '/usr/share/ipxe'
10
7
  isolinux = ['/usr/lib/ISOLINUX'].find { |p| File.exist?(p) } || '/usr/share/syslinux'
11
8
  syslinux = ['/usr/lib/syslinux/modules/bios', '/usr/lib/syslinux'].find { |p| File.exist?(p) } || '/usr/share/syslinux'
9
+ grub2 = ['/var/lib/tftpboot/grub2'].find { |p| File.exist?(p) } || '/var/lib/foreman/bootdisk'
12
10
  templates = -> { Hash[ProvisioningTemplate.where(template_kind: TemplateKind.where(name: 'Bootdisk')).map { |temp| [temp[:name], temp[:name]] }] }
13
11
 
14
- Setting.transaction do
15
- [
16
- set('bootdisk_ipxe_dir', N_('Path to directory containing iPXE images'), ipxe, N_('iPXE directory')),
17
- set('bootdisk_isolinux_dir', N_('Path to directory containing isolinux images'), isolinux, N_('ISOLINUX directory')),
18
- set('bootdisk_syslinux_dir', N_('Path to directory containing syslinux images'), syslinux, N_('SYSLINUX directory')),
19
- set('bootdisk_host_template', N_('iPXE template to use for host-specific boot disks'),
20
- 'Boot disk iPXE - host', N_('Host image template'), nil, collection: templates),
21
- set('bootdisk_generic_host_template', N_('iPXE template to use for generic host boot disks'),
22
- 'Boot disk iPXE - generic host', N_('Generic image template'), nil, collection: templates),
23
- set('bootdisk_mkiso_command', N_('Command to generate ISO image, use genisoimage or mkisofs'), 'genisoimage', N_('ISO generation command')),
24
- set('bootdisk_cache_media', N_('Installation media files will be cached for full host images'), true, N_('Installation media caching'))
25
- ].compact.each { |s| create s.update(category: 'Setting::Bootdisk') }
26
- end
27
-
28
- true
12
+ [
13
+ set('bootdisk_ipxe_dir', N_('Path to directory containing iPXE images'), ipxe, N_('iPXE directory')),
14
+ set('bootdisk_isolinux_dir', N_('Path to directory containing isolinux images'), isolinux, N_('ISOLINUX directory')),
15
+ set('bootdisk_syslinux_dir', N_('Path to directory containing syslinux images'), syslinux, N_('SYSLINUX directory')),
16
+ set('bootdisk_grub2_dir', N_('Path to directory containing grubx64.efi and shimx64.efi'), grub2, N_('Grub2 directory')),
17
+ set('bootdisk_host_template', N_('iPXE template to use for host-specific boot disks'),
18
+ 'Boot disk iPXE - host', N_('Host image template'), nil, collection: templates),
19
+ set('bootdisk_generic_host_template', N_('iPXE template to use for generic host boot disks'),
20
+ 'Boot disk iPXE - generic host', N_('Generic image template'), nil, collection: templates),
21
+ set('bootdisk_generic_efi_host_template', N_('Grub2 template to use for generic EFI host boot disks'),
22
+ 'Boot disk Grub2 EFI - generic host', N_('Generic Grub2 EFI image template'), nil, collection: templates),
23
+ set('bootdisk_mkiso_command', N_('Command to generate ISO image, use genisoimage or mkisofs'), 'genisoimage', N_('ISO generation command')),
24
+ set('bootdisk_cache_media', N_('Installation media files will be cached for full host images'), true, N_('Installation media caching'))
25
+ ]
29
26
  end
30
27
 
31
28
  def self.humanized_category
@@ -11,9 +11,10 @@ require 'uri'
11
11
  module ForemanBootdisk
12
12
  class ISOGenerator
13
13
  def self.generate_full_host(host, opts = {}, &block)
14
- raise Foreman::Exception, N_('Host is not in build mode, so the template cannot be rendered') unless host.build?
14
+ raise Foreman::Exception.new(N_('Host is not in build mode, so the template cannot be rendered')) unless host.build?
15
15
 
16
- tmpl = render_pxelinux_template(host)
16
+ isolinux_template = render_template(host, :PXELinux, ForemanBootdisk::Scope::FullHostBootdisk)
17
+ grub_template = render_template(host, :PXEGrub2, ForemanBootdisk::Scope::FullHostBootdiskEfi)
17
18
 
18
19
  # pxe_files and filename conversion is utterly bizarre
19
20
  # aim to convert filenames to something usable under ISO 9660, to match as rendered in the template
@@ -24,84 +25,145 @@ module ForemanBootdisk
24
25
  hash[iso_filename] = host.url_for_boot(type)
25
26
  end
26
27
 
27
- generate(opts.merge(isolinux: tmpl, files: files), &block)
28
+ generate(opts.merge(isolinux: isolinux_template, grub: grub_template, files: files), &block)
29
+ rescue ::Foreman::Exception => e
30
+ if e.code == 'ERF42-0067'
31
+ ForemanBootdisk.logger.warn e
32
+ else
33
+ raise e
34
+ end
28
35
  end
29
36
 
30
- def self.render_pxelinux_template(host)
31
- pxelinux_template = host.provisioning_template(kind: :PXELinux)
37
+ def self.render_template(host, kind, scope_class)
38
+ template = host.provisioning_template(kind: kind)
32
39
 
33
- raise Foreman::Exception, N_('Unable to generate disk template, PXELinux template not found.') unless pxelinux_template
40
+ raise Foreman::Exception.new(N_('Unable to generate disk template, %{kind} template not found.'), kind: kind) unless template
34
41
 
35
42
  template = ForemanBootdisk::Renderer.new.render_template(
36
- template: pxelinux_template,
43
+ template: template,
37
44
  host: host,
38
- scope_class: ForemanBootdisk::Scope::FullHostBootdisk
45
+ scope_class: scope_class
39
46
  )
40
47
 
41
48
  unless template
42
49
  err = host.errors.full_messages.to_sentence
43
- raise ::Foreman::Exception.new(N_('Unable to generate disk PXELinux template: %s'), err)
50
+ raise Foreman::Exception.new(N_('Unable to generate disk %{kind} template: %{error}'), kind: kind, error: err)
44
51
  end
45
52
 
46
53
  template
47
54
  end
48
55
 
56
+ def self.system_or_exception(command, error)
57
+ unless system(command)
58
+ ForemanBootdisk.logger.debug "Bootdisk command failed: #{command}"
59
+ raise Foreman::Exception.new(N_(error))
60
+ end
61
+ end
62
+
49
63
  def self.generate(opts = {})
50
- opts[:isolinux] = <<~ISOLINUX if opts[:isolinux].nil? && opts[:ipxe]
51
- default ipxe
52
- label ipxe
53
- kernel /ipxe
54
- initrd /script
55
- ISOLINUX
64
+ # isolinux template not provided for generic BIOS bootdisks
65
+ if opts[:isolinux].nil? && opts[:ipxe]
66
+ opts[:isolinux] = <<~EOT
67
+ default ipxe
68
+ label ipxe
69
+ kernel /ipxe
70
+ initrd /script
71
+ EOT
72
+ end
56
73
 
57
- Dir.mktmpdir('bootdisk') do |wd|
58
- Dir.mkdir(File.join(wd, 'build'))
74
+ if opts[:grub].nil? && opts[:ipxe]
75
+ opts[:grub] = <<~EOT
76
+ echo "Grub2 template not associated and/or unsupported bootdisk type."
77
+ echo "The following bootdisks are supported for EFI systems:"
78
+ echo " * FULL HOST BOOTDISK"
79
+ echo " * SUBNET GENERIC BOOTDISK"
80
+ echo "The system will poweroff in few minutes..."
81
+ sleep 500
82
+ poweroff
83
+ EOT
84
+ end
59
85
 
60
- if opts[:isolinux]
61
- isolinux_source_file = File.join(Setting[:bootdisk_isolinux_dir], 'isolinux.bin')
62
- raise Foreman::Exception, N_('Please ensure the isolinux/syslinux package(s) are installed.') unless File.exist?(isolinux_source_file)
86
+ # Temporary directory cannot be cleaned inprocess due to send_file offloaded to web server
87
+ wd = Dir.mktmpdir('bootdisk-iso-')
88
+ Dir.mkdir(File.join(wd, 'build'))
63
89
 
64
- FileUtils.cp(isolinux_source_file, File.join(wd, 'build', 'isolinux.bin'))
90
+ if opts[:isolinux]
91
+ isolinux_source_file = File.join(Setting[:bootdisk_isolinux_dir], 'isolinux.bin')
92
+ raise Foreman::Exception.new(N_('Please ensure the isolinux/syslinux package(s) are installed.')) unless File.exist?(isolinux_source_file)
65
93
 
66
- ldlinux_source_file = File.join(Setting[:bootdisk_syslinux_dir], 'ldlinux.c32')
67
- FileUtils.cp(ldlinux_source_file, File.join(wd, 'build', 'ldlinux.c32')) if File.exist?(ldlinux_source_file)
94
+ FileUtils.cp(isolinux_source_file, File.join(wd, 'build', 'isolinux.bin'))
68
95
 
69
- File.open(File.join(wd, 'build', 'isolinux.cfg'), 'w') do |file|
70
- file.write(opts[:isolinux])
71
- end
96
+ source_files = ['ldlinux.c32', 'menu.c32', 'libutil.c32']
97
+ source_files.each do |source_file|
98
+ full_path = File.join(Setting[:bootdisk_syslinux_dir], source_file)
99
+ FileUtils.cp(full_path, File.join(wd, 'build', source_file)) if File.exist?(full_path)
100
+ end
101
+
102
+ File.open(File.join(wd, 'build', 'isolinux.cfg'), 'w') do |file|
103
+ file.write(opts[:isolinux])
72
104
  end
105
+ end
106
+
107
+ if opts[:ipxe]
108
+ ipxe_source_file = File.join(Setting[:bootdisk_ipxe_dir], 'ipxe.lkrn')
109
+ raise Foreman::Exception.new(N_('Please ensure the ipxe-bootimgs package is installed.')) unless File.exist?(ipxe_source_file)
73
110
 
74
- if opts[:ipxe]
75
- ipxe_source_file = File.join(Setting[:bootdisk_ipxe_dir], 'ipxe.lkrn')
76
- raise Foreman::Exception, N_('Please ensure the ipxe-bootimgs package is installed.') unless File.exist?(ipxe_source_file)
111
+ FileUtils.cp(ipxe_source_file, File.join(wd, 'build', 'ipxe'))
112
+ File.open(File.join(wd, 'build', 'script'), 'w') { |file| file.write(opts[:ipxe]) }
113
+ end
77
114
 
78
- FileUtils.cp(ipxe_source_file, File.join(wd, 'build', 'ipxe'))
79
- File.open(File.join(wd, 'build', 'script'), 'w') { |file| file.write(opts[:ipxe]) }
115
+ if opts[:grub]
116
+ FileUtils.mkdir_p(File.join(wd, 'build', 'EFI', 'BOOT'))
117
+ # a copy when using CDROM directly
118
+ File.open(File.join(wd, 'build', 'EFI', 'BOOT', 'grub.cfg'), 'w') { |file| file.write(opts[:grub]) }
119
+ # a copy when using USB/HDD (copied into EFI image as well)
120
+ File.open(File.join(wd, 'build', 'grub-hdd.cfg'), 'w') do |f|
121
+ # set root to (cd0) or similar when booting via disk
122
+ f.write("search --file /ISOLINUX.BIN --set root\n")
123
+ f.write(opts[:grub])
124
+ end
125
+ efibootimg = File.join(wd, 'build', 'efiboot.img')
126
+ system_or_exception("truncate -s 4M #{efibootimg}", N_('Creating new image failed, install truncate utility'))
127
+ system_or_exception("mkfs.msdos #{efibootimg}", N_('Failed to format the ESP image via mkfs.msdos'))
128
+ system_or_exception("mmd -i #{efibootimg} '::/EFI'", N_('Failed to create a directory within the ESP image'))
129
+ system_or_exception("mmd -i #{efibootimg} '::/EFI/BOOT'", N_('Failed to create a directory within the ESP image'))
130
+ {
131
+ File.join(wd, 'build', 'grub-hdd.cfg').to_s => 'GRUB.CFG',
132
+ File.join(Setting[:bootdisk_grub2_dir], 'grubx64.efi').to_s => 'GRUBX64.EFI',
133
+ File.join(Setting[:bootdisk_grub2_dir], 'shimx64.efi').to_s => 'BOOTX64.EFI',
134
+ }.each do |src, dest|
135
+ raise(Foreman::Exception.new(N_('Ensure %{path} contains grubx64.efi and shimx64.efi: install TFTP module or copy those files into /var/lib/foreman/bootdisk (Grub2 directory setting)'), path: src)) unless File.exist?(src)
136
+ raise(Foreman::Exception.new(N_('Unable to mcopy %{path}'), path: src)) unless system("mcopy -m -i #{efibootimg} '#{src}' '#{"::/EFI/BOOT/#{dest}"}'")
80
137
  end
138
+ mkiso_args = "-eltorito-alt-boot -e efiboot.img -no-emul-boot"
139
+ isohybrod_args = ["--uefi"]
140
+ else
141
+ mkiso_args = ''
142
+ isohybrod_args = []
143
+ end
81
144
 
82
- if opts[:files]
83
- if opts[:files].respond_to?(:each)
84
- opts[:files].each do |file, source|
85
- fetch(File.join(wd, 'build', file), source)
86
- end
145
+ if opts[:files]
146
+ if opts[:files].respond_to?(:each)
147
+ opts[:files].each do |file, source|
148
+ fetch(File.join(wd, 'build', file), source)
87
149
  end
88
150
  end
151
+ end
89
152
 
90
- iso = if opts[:dir]
91
- Tempfile.new(['bootdisk', '.iso'], opts[:dir]).path
92
- else
93
- File.join(wd, 'output.iso')
94
- end
95
- raise Foreman::Exception, N_('ISO build failed') unless system(build_mkiso_command(output_file: iso, source_directory: File.join(wd, 'build')))
153
+ iso = if opts[:dir]
154
+ Tempfile.new(['bootdisk', '.iso'], opts[:dir]).path
155
+ else
156
+ File.join(wd, 'output.iso')
157
+ end
158
+ raise Foreman::Exception.new(N_('ISO build failed')) unless system(build_mkiso_command(output_file: iso, extra_commands: mkiso_args, source_directory: File.join(wd, 'build')))
96
159
 
97
- # Make the ISO bootable as a HDD/USB disk too
98
- raise Foreman::Exception, N_('ISO hybrid conversion failed') unless system('isohybrid', iso)
160
+ # Make the ISO bootable as a HDD/USB disk too
161
+ raise Foreman::Exception.new(N_('ISO hybrid conversion failed: %s'), $?) unless system(*["isohybrid", isohybrod_args, iso].flatten.compact)
99
162
 
100
- yield iso
101
- end
163
+ yield iso
102
164
  end
103
165
 
104
- def self.build_mkiso_command(output_file:, source_directory:)
166
+ def self.build_mkiso_command(output_file:, source_directory:, extra_commands:)
105
167
  arguments = [
106
168
  "-o #{output_file}",
107
169
  '-iso-level 2',
@@ -111,6 +173,7 @@ module ForemanBootdisk
111
173
  '-boot-load-size 4',
112
174
  '-boot-info-table'
113
175
  ]
176
+ arguments.push(extra_commands) unless extra_commands.nil?
114
177
  [Setting[:bootdisk_mkiso_command], arguments, source_directory].flatten.join(' ')
115
178
  end
116
179
 
@@ -121,7 +184,7 @@ module ForemanBootdisk
121
184
  '_' + expiry.strftime('%Y%m%d_%H%M')
122
185
  end
123
186
 
124
- def self.fetch(path, uri)
187
+ def self.fetch(path, uri, limit = 10)
125
188
  dir = File.dirname(path)
126
189
  FileUtils.mkdir_p(dir) unless File.exist?(dir)
127
190
 
@@ -138,11 +201,22 @@ module ForemanBootdisk
138
201
  write_cache = use_cache
139
202
  uri = URI(uri)
140
203
  Net::HTTP.start(uri.host, uri.port) do |http|
141
- request = Net::HTTP::Get.new(uri.request_uri)
204
+ request = Net::HTTP::Get.new(uri.request_uri, 'Accept-Encoding' => 'plain')
142
205
 
143
206
  http.request(request) do |response|
144
- response.read_body do |chunk|
145
- file.write chunk
207
+ case response
208
+ when Net::HTTPSuccess then
209
+ response.read_body do |chunk|
210
+ file.write chunk
211
+ end
212
+ when Net::HTTPRedirection then
213
+ raise("Too many HTTP redirects when downloading #{uri}") if limit <= 0
214
+
215
+ fetch(path, response['location'], limit - 1)
216
+ # prevent multiple writes to the cache
217
+ write_cache = false
218
+ else
219
+ raise ::Foreman::Exception, N_(format("Unable to download boot file %{uri}, HTTP return code %{code}", uri: uri, code: response.code))
146
220
  end
147
221
  end
148
222
  end
@@ -151,8 +225,11 @@ module ForemanBootdisk
151
225
 
152
226
  return unless write_cache
153
227
 
228
+ contents = File.read(path)
229
+ return if contents.empty?
230
+
154
231
  ForemanBootdisk.logger.debug("Caching contents of #{uri}")
155
- Rails.cache.write(uri, File.read(path), raw: true)
232
+ Rails.cache.write(uri, contents, raw: true)
156
233
  end
157
234
 
158
235
  # isolinux supports up to ISO 9660 level 2 filenames
@@ -4,35 +4,46 @@ require 'uri'
4
4
 
5
5
  module ForemanBootdisk
6
6
  class Renderer
7
- def generic_template_render(subnet = nil)
8
- host = if subnet.present?
9
- # rendering a subnet-level bootdisk requires tricking the renderer into thinking it has a
10
- # valid host, without a token, but with a tftp proxy
11
- Struct.new(:token, :provision_interface).new(
12
- nil,
13
- Struct.new(:subnet).new(subnet)
14
- )
15
- else
16
- Struct.new(:token, :subnet).new(nil, nil)
17
- end
18
-
19
- render_template(template: generic_host_template, host: host)
20
- end
21
-
22
- def render_template(template:, host:, scope_class: renderer_scope)
7
+ def render_template(template:, host:, scope_class: renderer_scope, subnet: nil)
23
8
  source = Foreman::Renderer.get_source(
24
9
  template: template,
25
- host: host
26
10
  )
27
11
  scope = Foreman::Renderer.get_scope(
28
12
  host: host,
29
- klass: scope_class
13
+ variables: { subnet: subnet, bootdisk: true },
14
+ klass: scope_class,
30
15
  )
31
16
  Foreman::Renderer.render(source, scope)
32
17
  end
33
18
 
19
+ def generic_template_render(subnet = nil)
20
+ render_template(template: generic_host_template, host: stub_host(subnet), subnet: subnet)
21
+ end
22
+
23
+ def generic_efi_template_render(subnet)
24
+ if subnet.httpboot?
25
+ render_template(template: generic_efi_host_template, host: stub_host(subnet), subnet: subnet)
26
+ else
27
+ ForemanBootdisk.logger.warn('HTTPBOOT feature is not enabled for subnet %s, UEFI may not be available for bootdisk' % subnet.name)
28
+ end
29
+ end
30
+
34
31
  private
35
32
 
33
+ def stub_host(subnet)
34
+ if subnet.present?
35
+ # rendering a subnet-level bootdisk requires tricking the renderer into thinking it has a
36
+ # valid host, without a token, but with a tftp proxy
37
+ Struct.new(:token, :provision_interface, :content_source).new(
38
+ nil,
39
+ Struct.new(:subnet).new(subnet),
40
+ nil
41
+ )
42
+ else
43
+ Struct.new(:token, :subnet, :content_source).new(nil, nil, nil)
44
+ end
45
+ end
46
+
36
47
  def renderer_scope
37
48
  ForemanBootdisk::Scope::Bootdisk
38
49
  end
@@ -43,5 +54,12 @@ module ForemanBootdisk
43
54
 
44
55
  template
45
56
  end
57
+
58
+ def generic_efi_host_template
59
+ template = ProvisioningTemplate.unscoped.find_by(name: Setting[:bootdisk_generic_efi_host_template])
60
+ raise ::Foreman::Exception.new(N_('Unable to find template specified by %s setting'), 'bootdisk_generic_efi_host_template') unless template
61
+
62
+ template
63
+ end
46
64
  end
47
65
  end
@@ -0,0 +1,64 @@
1
+ #
2
+ # Boot disk Grub2 EFI - generic host
3
+ #
4
+ #set debug="http,efinet,net"
5
+ #set debug=all
6
+ echo "Foreman Bootdisk: 'Boot disk Grub2 EFI - generic host' template"
7
+ echo
8
+ echo "********************************************************"
9
+ echo " REQUIREMENTS:"
10
+ echo " * SUBNET GENERIC IMAGE ONLY (host image not supported)"
11
+ echo " * HOST PARAM default_grub_install_entry set to efi_http"
12
+ echo " * PROXY WITH HTTPBOOT FEATURE"
13
+ echo " * HTTP UEFI BOOT ONLY (Legacy/PXE not supported)"
14
+ echo " * IPv4 ONLY (IPv6 not tested, change the template)"
15
+ echo " * HTTP ONLY (change the template for HTTPS)"
16
+ echo " * ISC DHCP (other servers not tested)"
17
+ echo " * GRUB FROM RHEL 8.3+/7.9+ (when generating the image)"
18
+ echo " * EFI HTTP or HTTPS grub entry must be selected in menu"
19
+ echo " * DNS must resolve proxy hostname via DNS proxy if set"
20
+ echo "*******************************************************"
21
+ sleep 5
22
+ <%
23
+ # possible values are: "http" or "https"
24
+ proxy_proto = "http"
25
+
26
+ @subnet || bootdisk_raise("Generic disk not supported for EFI, use subnet disk")
27
+ @subnet.template? || bootdisk_raise("Requires a proxy with template feature")
28
+ proxy_port = if proxy_proto == "http"
29
+ @subnet.httpboot.httpboot_http_port
30
+ else
31
+ @subnet.httpboot.httpboot_https_port
32
+ end
33
+ # Workaround for "no DNS server configured" https://bugzilla.redhat.com/show_bug.cgi?id=1842509
34
+ proxy_httpboot_host = dns_lookup(@subnet.httpboot.hostname)
35
+ proxy_template_host = dns_lookup(@subnet.template.hostname)
36
+ -%>
37
+ echo
38
+ net_ls_cards
39
+ echo "Configuring ALL cards via BOOTP/IPv4"
40
+ net_bootp
41
+ # uncomment here for IPv6 support (not tested)
42
+ #echo "Configuring ALL cards via BOOTP/IPv6"
43
+ #net_ipv6_autoconf
44
+ net_ls_addr
45
+ net_ls_routes
46
+ net_ls_dns
47
+ sleep 5
48
+ set root=(<%= proxy_proto %>,<%= proxy_httpboot_host %>:<%= proxy_port %>)
49
+ # The variable will not survive configfile fetch, therefore absolute path
50
+ # must be used in the chainloaded template.
51
+ # https://bugzilla.redhat.com/show_bug.cgi?id=1842893
52
+ set http_path=/httpboot/
53
+ set default=efi_<%= proxy_proto %>
54
+ <% (0..32).each do |i| -%>
55
+ echo "Trying efinet<%= i %> via <%= proxy_proto %>://<%= proxy_template_host %>:<%= proxy_port %>/unattended/PXEGrub2?mac=$net_efinet<%= i %>_dhcp_mac"
56
+ set net_default_mac=$net_efinet<%= i %>_dhcp_mac
57
+ sleep 5
58
+ configfile (<%= proxy_proto %>,<%= proxy_template_host %>:<%= proxy_port %>)/unattended/PXEGrub2?mac=$net_efinet<%= i %>_dhcp_mac
59
+ <% end -%>
60
+
61
+ echo "Could not find a host with PXEGrub2 template and one of the MAC addresses!"
62
+ echo "The system will poweroff in few minutes..."
63
+ sleep 500
64
+ poweroff
@@ -0,0 +1,34 @@
1
+ #!ipxe
2
+ #
3
+ # Generic host template with interactive static IP configuration. Tokens
4
+ # must be disabled in order to access templates via MAC addresses.
5
+ #
6
+
7
+ <% (0..32).each do |i| -%>
8
+ :net<%= i %>
9
+ isset ${net<%= i -%>/mac} || goto configure
10
+ echo Found ${net<%= i -%>/mac} as net<%= i -%> on a ${net<%= i -%>/chip}
11
+ goto net<%= i+1 %>
12
+ <% end -%>
13
+
14
+ :configure
15
+ echo -n Interface (e.g. net0): && read interface
16
+ iseq ${interface} n && goto reboot ||
17
+ iseq ${interface} N && goto reboot ||
18
+
19
+ ifopen ${interface}
20
+ echo Please enter IPv4 details for ${interface}
21
+ echo
22
+
23
+ echo -n IP address: && read ${interface}/ip
24
+ echo -n Subnet mask: && read ${interface}/netmask
25
+ echo -n Default gateway: && read ${interface}/gateway
26
+ echo -n DNS server: && read dns
27
+
28
+ chain <%= bootdisk_chain_url %>${${interface}/mac} || goto reboot
29
+ exit 0
30
+
31
+ :reboot
32
+ echo Unable to continue, rebooting...
33
+ sleep 30
34
+ exit 1