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