foreman_bootdisk 16.1.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.
- checksums.yaml +4 -4
- data/AUTHORS +1 -0
- data/README.md +9 -0
- data/app/controllers/foreman_bootdisk/api/v2/disks_controller.rb +5 -7
- data/app/controllers/foreman_bootdisk/api/v2/subnet_disks_controller.rb +9 -4
- data/app/controllers/foreman_bootdisk/disks_controller.rb +11 -10
- data/app/helpers/concerns/foreman_bootdisk/hosts_helper_ext.rb +1 -1
- data/app/lib/foreman_bootdisk/scope/bootdisk.rb +4 -1
- data/app/lib/foreman_bootdisk/scope/full_host_bootdisk_efi.rb +15 -0
- data/app/models/concerns/foreman_bootdisk/compute_resources/vmware.rb +10 -1
- data/app/models/concerns/foreman_bootdisk/host_ext.rb +3 -6
- data/app/models/concerns/foreman_bootdisk/orchestration/compute.rb +28 -19
- data/app/models/setting/bootdisk.rb +4 -0
- data/app/services/foreman_bootdisk/iso_generator.rb +114 -54
- data/app/services/foreman_bootdisk/renderer.rb +36 -19
- data/app/views/foreman_bootdisk/generic_efi_host.erb +64 -0
- data/db/seeds.d/50-bootdisk_templates.rb +1 -0
- data/lib/foreman_bootdisk/engine.rb +1 -1
- data/lib/foreman_bootdisk/version.rb +1 -1
- data/lib/tasks/bootdisk.rake +10 -6
- data/locale/action_names.rb +5 -0
- data/locale/ca/LC_MESSAGES/foreman_bootdisk.mo +0 -0
- data/locale/ca/foreman_bootdisk.po +37 -4
- data/locale/de/LC_MESSAGES/foreman_bootdisk.mo +0 -0
- data/locale/de/foreman_bootdisk.po +40 -7
- data/locale/en/LC_MESSAGES/foreman_bootdisk.mo +0 -0
- data/locale/en/foreman_bootdisk.po +37 -4
- data/locale/en_GB/LC_MESSAGES/foreman_bootdisk.mo +0 -0
- data/locale/en_GB/foreman_bootdisk.po +38 -5
- data/locale/es/LC_MESSAGES/foreman_bootdisk.mo +0 -0
- data/locale/es/foreman_bootdisk.po +40 -7
- data/locale/foreman_bootdisk.pot +87 -39
- data/locale/fr/LC_MESSAGES/foreman_bootdisk.mo +0 -0
- data/locale/fr/foreman_bootdisk.po +38 -5
- data/locale/it/LC_MESSAGES/foreman_bootdisk.mo +0 -0
- data/locale/it/foreman_bootdisk.po +38 -5
- data/locale/ja/LC_MESSAGES/foreman_bootdisk.mo +0 -0
- data/locale/ja/foreman_bootdisk.po +38 -5
- data/locale/ko/LC_MESSAGES/foreman_bootdisk.mo +0 -0
- data/locale/ko/foreman_bootdisk.po +38 -5
- data/locale/pt_BR/LC_MESSAGES/foreman_bootdisk.mo +0 -0
- data/locale/pt_BR/foreman_bootdisk.po +38 -5
- data/locale/ru/LC_MESSAGES/foreman_bootdisk.mo +0 -0
- data/locale/ru/foreman_bootdisk.po +38 -5
- data/locale/sv_SE/LC_MESSAGES/foreman_bootdisk.mo +0 -0
- data/locale/sv_SE/foreman_bootdisk.po +38 -5
- data/locale/zh_CN/LC_MESSAGES/foreman_bootdisk.mo +0 -0
- data/locale/zh_CN/foreman_bootdisk.po +38 -5
- data/locale/zh_TW/LC_MESSAGES/foreman_bootdisk.mo +0 -0
- data/locale/zh_TW/foreman_bootdisk.po +38 -5
- data/test/functional/foreman_bootdisk/api/v2/disks_controller_test.rb +51 -17
- data/test/functional/foreman_bootdisk/api/v2/subnet_disks_controller_test.rb +23 -10
- data/test/functional/foreman_bootdisk/disks_controller_test.rb +65 -25
- data/test/test_plugin_helper.rb +21 -3
- data/test/unit/concerns/host_test.rb +1 -1
- data/test/unit/concerns/orchestration/compute_test.rb +32 -13
- data/test/unit/foreman_bootdisk/renderer_test.rb +1 -1
- data/test/unit/iso_generator_test.rb +6 -7
- metadata +19 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: cc43113465fd89f890f9606fb04af3e948b96ac1bd6c1943211a452e38c4cb2a
|
4
|
+
data.tar.gz: 9db1069085f706df776576fc0df25f49a7d09ae71555ea15397d57656ddd0da5
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 115bddba362f7c53b78d06ca78a1f120babae76cc2f922447cd0e05b1f322f189e30b186e6cb0718b7d9a24bfca67bf2287283fd9495eba4193a9b6bb560ec29
|
7
|
+
data.tar.gz: 1cd764fdc0a939d45934070d6b34e2619870c8c206afd4915cae93f68f7553cfcc9b18d3c04770aba9f4b6f45d62bbd779563798b7175595ee8c2b991f03b660
|
data/AUTHORS
CHANGED
data/README.md
CHANGED
@@ -50,6 +50,10 @@ gPXE images are unsupported due to lack of initrd support.
|
|
50
50
|
| >= 1.19 | ~> 13.0 |
|
51
51
|
| >= 1.20 | ~> 14.0 |
|
52
52
|
| >= 1.21 | ~> 15.0 |
|
53
|
+
| >= 1.23 | ~> 16.0 |
|
54
|
+
| >= 1.24 | ~> 16.1 |
|
55
|
+
| >= 2.0 | ~> 16.1 |
|
56
|
+
| >= 2.1 | ~> 17.0 |
|
53
57
|
|
54
58
|
# Usage
|
55
59
|
|
@@ -119,6 +123,7 @@ TFTP settings are needed.
|
|
119
123
|
<th>DHCP reservation</th>
|
120
124
|
<th>Pre-register host</th>
|
121
125
|
<th>OS-specific</th>
|
126
|
+
<th>EFI supported</th>
|
122
127
|
</tr>
|
123
128
|
<tr>
|
124
129
|
<td>Per-host image</td>
|
@@ -127,6 +132,7 @@ TFTP settings are needed.
|
|
127
132
|
<td>No</td>
|
128
133
|
<td>Yes</td>
|
129
134
|
<td>No</td>
|
135
|
+
<td>No</td>
|
130
136
|
</tr>
|
131
137
|
<tr>
|
132
138
|
<td>Full host image</td>
|
@@ -135,6 +141,7 @@ TFTP settings are needed.
|
|
135
141
|
<td>No</td>
|
136
142
|
<td>Yes</td>
|
137
143
|
<td>Yes</td>
|
144
|
+
<td>Yes</td>
|
138
145
|
</tr>
|
139
146
|
<tr>
|
140
147
|
<td>Generic image</td>
|
@@ -143,6 +150,7 @@ TFTP settings are needed.
|
|
143
150
|
<td>No</td>
|
144
151
|
<td>Yes</td>
|
145
152
|
<td>No</td>
|
153
|
+
<td>No</td>
|
146
154
|
</tr>
|
147
155
|
<tr>
|
148
156
|
<td>Subnet image</td>
|
@@ -151,6 +159,7 @@ TFTP settings are needed.
|
|
151
159
|
<td>No</td>
|
152
160
|
<td>Yes</td>
|
153
161
|
<td>No</td>
|
162
|
+
<td>Yes</td>
|
154
163
|
</tr>
|
155
164
|
</table>
|
156
165
|
|
@@ -21,9 +21,10 @@ module ForemanBootdisk
|
|
21
21
|
|
22
22
|
api :GET, '/generic', N_('Download generic image')
|
23
23
|
def generic
|
24
|
+
# EFI not supported for iPXE generic bootdisk
|
24
25
|
tmpl = ForemanBootdisk::Renderer.new.generic_template_render
|
25
26
|
ForemanBootdisk::ISOGenerator.generate(ipxe: tmpl) do |iso|
|
26
|
-
|
27
|
+
send_file(iso, filename: "bootdisk_#{URI.parse(Setting[:foreman_url]).host}.iso")
|
27
28
|
end
|
28
29
|
end
|
29
30
|
|
@@ -34,22 +35,19 @@ module ForemanBootdisk
|
|
34
35
|
host = @disk
|
35
36
|
if params[:full]
|
36
37
|
ForemanBootdisk::ISOGenerator.generate_full_host(host) do |iso|
|
37
|
-
|
38
|
+
send_file(iso, filename: "#{host.name}#{ForemanBootdisk::ISOGenerator.token_expiry(host)}.iso")
|
38
39
|
end
|
39
40
|
else
|
41
|
+
# EFI not supported for iPXE host bootdisk
|
40
42
|
tmpl = host.bootdisk_template_render
|
41
43
|
ForemanBootdisk::ISOGenerator.generate(ipxe: tmpl) do |iso|
|
42
|
-
|
44
|
+
send_file(iso, filename: "#{host.name}.iso")
|
43
45
|
end
|
44
46
|
end
|
45
47
|
end
|
46
48
|
|
47
49
|
private
|
48
50
|
|
49
|
-
def read_file(filename)
|
50
|
-
File.read(filename)
|
51
|
-
end
|
52
|
-
|
53
51
|
def resource_scope
|
54
52
|
Host::Managed.authorized('view_hosts')
|
55
53
|
end
|
@@ -25,10 +25,15 @@ module ForemanBootdisk
|
|
25
25
|
def subnet
|
26
26
|
subnet = @subnet_disk
|
27
27
|
subnet.tftp || raise(::Foreman::Exception.new(N_('TFTP feature not enabled for subnet %s'), subnet.name))
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
28
|
+
tmpl_bios = ForemanBootdisk::Renderer.new.generic_template_render(subnet)
|
29
|
+
tmpl_efi = nil
|
30
|
+
if subnet.httpboot
|
31
|
+
tmpl_efi = ForemanBootdisk::Renderer.new.generic_efi_template_render(subnet)
|
32
|
+
else
|
33
|
+
ForemanBootdisk.logger.warn('HTTPBOOT feature is not enabled for subnet %s, UEFI may not be available for bootdisk' % subnet.name)
|
34
|
+
end
|
35
|
+
ForemanBootdisk::ISOGenerator.generate(ipxe: tmpl_bios, grub: tmpl_efi) do |temp_iso_filename|
|
36
|
+
send_file(temp_iso_filename, filename: subnet.name)
|
32
37
|
end
|
33
38
|
end
|
34
39
|
|
@@ -12,6 +12,7 @@ module ForemanBootdisk
|
|
12
12
|
|
13
13
|
def generic
|
14
14
|
begin
|
15
|
+
# EFI not supported for iPXE generic bootdisk
|
15
16
|
tmpl = ForemanBootdisk::Renderer.new.generic_template_render
|
16
17
|
rescue StandardError => e
|
17
18
|
error_rendering(e)
|
@@ -20,13 +21,14 @@ module ForemanBootdisk
|
|
20
21
|
end
|
21
22
|
|
22
23
|
ForemanBootdisk::ISOGenerator.generate(ipxe: tmpl) do |iso|
|
23
|
-
|
24
|
+
send_file(iso, filename: "bootdisk_#{URI.parse(Setting[:foreman_url]).host}.iso")
|
24
25
|
end
|
25
26
|
end
|
26
27
|
|
27
28
|
def host
|
28
29
|
host = @disk
|
29
30
|
begin
|
31
|
+
# EFI not supported for iPXE host bootdisk
|
30
32
|
tmpl = host.bootdisk_template_render
|
31
33
|
rescue StandardError => e
|
32
34
|
error_rendering(e)
|
@@ -35,14 +37,14 @@ module ForemanBootdisk
|
|
35
37
|
end
|
36
38
|
|
37
39
|
ForemanBootdisk::ISOGenerator.generate(ipxe: tmpl) do |iso|
|
38
|
-
|
40
|
+
send_file(iso, filename: "#{host.name}.iso")
|
39
41
|
end
|
40
42
|
end
|
41
43
|
|
42
44
|
def full_host
|
43
45
|
host = @disk
|
44
46
|
ForemanBootdisk::ISOGenerator.generate_full_host(host) do |iso|
|
45
|
-
|
47
|
+
send_file(iso, filename: "#{host.name}#{ForemanBootdisk::ISOGenerator.token_expiry(host)}.iso")
|
46
48
|
end
|
47
49
|
end
|
48
50
|
|
@@ -51,15 +53,18 @@ module ForemanBootdisk
|
|
51
53
|
begin
|
52
54
|
subnet = host.try(:subnet) || raise(::Foreman::Exception.new(N_('Subnet is not assigned to the host %s'), host.name))
|
53
55
|
subnet.tftp || raise(::Foreman::Exception.new(N_('TFTP feature not enabled for subnet %s'), subnet.name))
|
54
|
-
|
56
|
+
subnet.httpboot || ForemanBootdisk.logger.warn('HTTPBOOT feature is not enabled for subnet %s, UEFI may not be available for bootdisk' % subnet.name)
|
57
|
+
|
58
|
+
tmpl_bios = ForemanBootdisk::Renderer.new.generic_template_render(subnet)
|
59
|
+
tmpl_efi = ForemanBootdisk::Renderer.new.generic_efi_template_render(subnet)
|
55
60
|
rescue StandardError => e
|
56
61
|
error_rendering(e)
|
57
62
|
redirect_back(fallback_location: '/')
|
58
63
|
return
|
59
64
|
end
|
60
65
|
|
61
|
-
ForemanBootdisk::ISOGenerator.generate(ipxe:
|
62
|
-
|
66
|
+
ForemanBootdisk::ISOGenerator.generate(ipxe: tmpl_bios, grub: tmpl_efi) do |iso|
|
67
|
+
send_file(iso, filename: "bootdisk_subnet_#{subnet.name}.iso")
|
63
68
|
end
|
64
69
|
end
|
65
70
|
|
@@ -67,10 +72,6 @@ module ForemanBootdisk
|
|
67
72
|
|
68
73
|
private
|
69
74
|
|
70
|
-
def read_file(filename)
|
71
|
-
File.read(filename)
|
72
|
-
end
|
73
|
-
|
74
75
|
def resource_scope(_controller = controller_name)
|
75
76
|
Host::Managed.authorized(:view_hosts)
|
76
77
|
end
|
@@ -65,7 +65,7 @@ module ForemanBootdisk
|
|
65
65
|
|
66
66
|
# need to wrap this one in a test for template proxy presence
|
67
67
|
def display_bootdisk_for_subnet(host)
|
68
|
-
if (proxy = host.try(:subnet).try(:tftp)) && proxy.has_feature?('Templates')
|
68
|
+
if (proxy = host.try(:subnet).try(:tftp) || host.try(:subnet).try(:httpboot)) && proxy.has_feature?('Templates')
|
69
69
|
display_bootdisk_link_if_authorized(
|
70
70
|
_("Subnet '%s' generic image") % host.subnet.name, {
|
71
71
|
controller: 'foreman_bootdisk/disks',
|
@@ -6,7 +6,6 @@ module ForemanBootdisk
|
|
6
6
|
def bootdisk_chain_url(mac = host.try(:mac), action = 'iPXE')
|
7
7
|
url = foreman_url(action)
|
8
8
|
u = URI.parse(url)
|
9
|
-
ForemanBootdisk.logger.warn("Foreman or proxy is configured with HTTPS, probably not supported by iPXE: #{u}") if u.scheme == 'https'
|
10
9
|
new_query_data = URI.decode_www_form(u.query || '') << ['mac', mac || '']
|
11
10
|
new_querystring = URI.encode_www_form(new_query_data)
|
12
11
|
u.query = nil
|
@@ -18,6 +17,10 @@ module ForemanBootdisk
|
|
18
17
|
def bootdisk_raise(*args)
|
19
18
|
raise ::Foreman::Exception.new(*args)
|
20
19
|
end
|
20
|
+
|
21
|
+
def template_name
|
22
|
+
"Foreman Bootdisk"
|
23
|
+
end
|
21
24
|
end
|
22
25
|
end
|
23
26
|
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ForemanBootdisk
|
4
|
+
module Scope
|
5
|
+
class FullHostBootdiskEfi < Bootdisk
|
6
|
+
def kernel(medium_provider)
|
7
|
+
'/' + ForemanBootdisk::ISOGenerator.iso9660_filename(super)
|
8
|
+
end
|
9
|
+
|
10
|
+
def initrd(medium_provider)
|
11
|
+
'/' + ForemanBootdisk::ISOGenerator.iso9660_filename(super)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -36,12 +36,21 @@ module ForemanBootdisk
|
|
36
36
|
'instance_uuid' => vm_uuid,
|
37
37
|
'iso' => "foreman_isos/#{iso}",
|
38
38
|
'datastore' => bootdisk_datastore(vm_uuid),
|
39
|
-
'start_connected' =>
|
39
|
+
'start_connected' => true,
|
40
40
|
'connected' => true,
|
41
41
|
'allow_guest_control' => true
|
42
42
|
}
|
43
43
|
client.vm_reconfig_cdrom options
|
44
44
|
end
|
45
|
+
|
46
|
+
def iso_detach(vm_uuid)
|
47
|
+
options = {
|
48
|
+
'instance_uuid' => vm_uuid,
|
49
|
+
'start_connected' => false,
|
50
|
+
'connected' => false,
|
51
|
+
}
|
52
|
+
client.vm_reconfig_cdrom options
|
53
|
+
end
|
45
54
|
end
|
46
55
|
end
|
47
56
|
end
|
@@ -5,14 +5,11 @@ require 'uri'
|
|
5
5
|
module ForemanBootdisk
|
6
6
|
module HostExt
|
7
7
|
def bootdisk_template
|
8
|
-
template = ProvisioningTemplate.unscoped.find_by(
|
9
|
-
name: Setting[:bootdisk_host_template]
|
10
|
-
)
|
8
|
+
template = ProvisioningTemplate.unscoped.find_by(name: Setting[:bootdisk_host_template])
|
11
9
|
unless template
|
12
|
-
raise ::Foreman::Exception.new(
|
13
|
-
N_('Unable to find template specified by %s setting'), 'bootdisk_host_template'
|
14
|
-
)
|
10
|
+
raise ::Foreman::Exception.new(N_('Unable to find template specified by %s setting'), 'bootdisk_host_template')
|
15
11
|
end
|
12
|
+
|
16
13
|
template
|
17
14
|
end
|
18
15
|
|
@@ -9,7 +9,6 @@ module ForemanBootdisk
|
|
9
9
|
|
10
10
|
included do
|
11
11
|
after_validation :queue_bootdisk_compute
|
12
|
-
after_build :rebuild_with_bootdisk
|
13
12
|
end
|
14
13
|
|
15
14
|
def bootdisk_isodir
|
@@ -21,15 +20,25 @@ module ForemanBootdisk
|
|
21
20
|
end
|
22
21
|
|
23
22
|
def queue_bootdisk_compute
|
24
|
-
return unless compute? && errors.empty?
|
23
|
+
return unless compute? && errors.empty?
|
25
24
|
return unless provision_method == 'bootdisk'
|
26
25
|
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
26
|
+
# We want to add our queue jobs only if really necessary:
|
27
|
+
# - in case of starting to create a new host
|
28
|
+
# - in case of a rebuild
|
29
|
+
if (old.nil? || !old.build?) && build?
|
30
|
+
queue.create(name: _('Generating ISO image for %s') % self, priority: 5,
|
31
|
+
action: [self, :setGenerateIsoImage])
|
32
|
+
queue.create(name: _('Upload ISO image to datastore for %s') % self, priority: 6,
|
33
|
+
action: [self, :setIsoImage])
|
34
|
+
queue.create(name: _('Attach ISO image to CDROM drive for %s') % self, priority: 1001,
|
35
|
+
action: [self, :setAttachIsoImage])
|
36
|
+
# Detach ISO image when host sends 'built' HTTP POST request
|
37
|
+
elsif old&.build? && !build?
|
38
|
+
queue.create(name: _('Detach ISO image from CDROM drive for %s') % self, priority: 52,
|
39
|
+
action: [self, :setDetachIsoImage])
|
40
|
+
end
|
41
|
+
true
|
33
42
|
end
|
34
43
|
|
35
44
|
def bootdisk_generate_iso_image
|
@@ -46,6 +55,10 @@ module ForemanBootdisk
|
|
46
55
|
compute_resource.iso_attach(File.basename(bootdisk_isofile), uuid)
|
47
56
|
end
|
48
57
|
|
58
|
+
def bootdisk_detach_iso
|
59
|
+
compute_resource.iso_detach(uuid)
|
60
|
+
end
|
61
|
+
|
49
62
|
def setGenerateIsoImage
|
50
63
|
logger.info format('Generating ISO image for %s', name)
|
51
64
|
bootdisk_generate_iso_image
|
@@ -73,18 +86,14 @@ module ForemanBootdisk
|
|
73
86
|
|
74
87
|
def delAttachIsoImage; end
|
75
88
|
|
76
|
-
def
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
bootdisk_upload_iso
|
82
|
-
bootdisk_attach_iso
|
83
|
-
rescue StandardError => e
|
84
|
-
Foreman::Logging.exception "Failed to rebuild Bootdisk image for #{name}", e, level: :error
|
85
|
-
return false
|
86
|
-
end
|
89
|
+
def setDetachIsoImage
|
90
|
+
logger.info format('Detaching ISO image from CDROM drive for %s', name)
|
91
|
+
bootdisk_detach_iso
|
92
|
+
rescue StandardError => e
|
93
|
+
failure format(_('Failed to detach ISO image from CDROM drive of instance %{name}: %{message}'), name: name, message: e.message), e
|
87
94
|
end
|
95
|
+
|
96
|
+
def delDetachIsoImage; end
|
88
97
|
end
|
89
98
|
end
|
90
99
|
end
|
@@ -6,16 +6,20 @@ class Setting
|
|
6
6
|
ipxe = ['/usr/lib/ipxe'].find { |p| File.exist?(p) } || '/usr/share/ipxe'
|
7
7
|
isolinux = ['/usr/lib/ISOLINUX'].find { |p| File.exist?(p) } || '/usr/share/syslinux'
|
8
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'
|
9
10
|
templates = -> { Hash[ProvisioningTemplate.where(template_kind: TemplateKind.where(name: 'Bootdisk')).map { |temp| [temp[:name], temp[:name]] }] }
|
10
11
|
|
11
12
|
[
|
12
13
|
set('bootdisk_ipxe_dir', N_('Path to directory containing iPXE images'), ipxe, N_('iPXE directory')),
|
13
14
|
set('bootdisk_isolinux_dir', N_('Path to directory containing isolinux images'), isolinux, N_('ISOLINUX directory')),
|
14
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')),
|
15
17
|
set('bootdisk_host_template', N_('iPXE template to use for host-specific boot disks'),
|
16
18
|
'Boot disk iPXE - host', N_('Host image template'), nil, collection: templates),
|
17
19
|
set('bootdisk_generic_host_template', N_('iPXE template to use for generic host boot disks'),
|
18
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),
|
19
23
|
set('bootdisk_mkiso_command', N_('Command to generate ISO image, use genisoimage or mkisofs'), 'genisoimage', N_('ISO generation command')),
|
20
24
|
set('bootdisk_cache_media', N_('Installation media files will be cached for full host images'), true, N_('Installation media caching'))
|
21
25
|
]
|
@@ -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
|
14
|
+
raise Foreman::Exception.new(N_('Host is not in build mode, so the template cannot be rendered')) unless host.build?
|
15
15
|
|
16
|
-
|
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,87 +25,145 @@ module ForemanBootdisk
|
|
24
25
|
hash[iso_filename] = host.url_for_boot(type)
|
25
26
|
end
|
26
27
|
|
27
|
-
generate(opts.merge(isolinux:
|
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.
|
31
|
-
|
37
|
+
def self.render_template(host, kind, scope_class)
|
38
|
+
template = host.provisioning_template(kind: kind)
|
32
39
|
|
33
|
-
raise
|
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:
|
43
|
+
template: template,
|
37
44
|
host: host,
|
38
|
-
scope_class:
|
45
|
+
scope_class: scope_class
|
39
46
|
)
|
40
47
|
|
41
48
|
unless template
|
42
49
|
err = host.errors.full_messages.to_sentence
|
43
|
-
raise
|
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
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
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)
|
63
|
-
|
64
|
-
FileUtils.cp(isolinux_source_file, File.join(wd, 'build', 'isolinux.bin'))
|
65
|
-
|
66
|
-
source_files = ['ldlinux.c32', 'menu.c32']
|
67
|
-
source_files.each do |source_file|
|
68
|
-
full_path = File.join(Setting[:bootdisk_syslinux_dir], source_file)
|
69
|
-
FileUtils.cp(full_path, File.join(wd, 'build', source_file)) if File.exist?(full_path)
|
70
|
-
end
|
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
|
71
73
|
|
72
|
-
|
73
|
-
|
74
|
-
|
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
|
85
|
+
|
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'))
|
89
|
+
|
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)
|
93
|
+
|
94
|
+
FileUtils.cp(isolinux_source_file, File.join(wd, 'build', 'isolinux.bin'))
|
95
|
+
|
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)
|
75
100
|
end
|
76
101
|
|
77
|
-
|
78
|
-
|
79
|
-
|
102
|
+
File.open(File.join(wd, 'build', 'isolinux.cfg'), 'w') do |file|
|
103
|
+
file.write(opts[:isolinux])
|
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)
|
110
|
+
|
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
|
80
114
|
|
81
|
-
|
82
|
-
|
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}"}'")
|
83
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
|
84
144
|
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
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)
|
90
149
|
end
|
91
150
|
end
|
151
|
+
end
|
92
152
|
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
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')))
|
99
159
|
|
100
|
-
|
101
|
-
|
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)
|
102
162
|
|
103
|
-
|
104
|
-
end
|
163
|
+
yield iso
|
105
164
|
end
|
106
165
|
|
107
|
-
def self.build_mkiso_command(output_file:, source_directory:)
|
166
|
+
def self.build_mkiso_command(output_file:, source_directory:, extra_commands:)
|
108
167
|
arguments = [
|
109
168
|
"-o #{output_file}",
|
110
169
|
'-iso-level 2',
|
@@ -114,6 +173,7 @@ module ForemanBootdisk
|
|
114
173
|
'-boot-load-size 4',
|
115
174
|
'-boot-info-table'
|
116
175
|
]
|
176
|
+
arguments.push(extra_commands) unless extra_commands.nil?
|
117
177
|
[Setting[:bootdisk_mkiso_command], arguments, source_directory].flatten.join(' ')
|
118
178
|
end
|
119
179
|
|