foreman_bootdisk 14.0.0 → 17.0.2

Sign up to get free protection for your applications and to get access to all the features.
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