foreman_bootdisk 15.0.0 → 17.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/workflows/rubocop.yaml +19 -0
- data/.tx/config +1 -1
- data/AUTHORS +11 -0
- data/README.md +25 -0
- data/app/controllers/concerns/allowed_actions.rb +16 -0
- data/app/controllers/foreman_bootdisk/api/v2/disks_controller.rb +9 -7
- data/app/controllers/foreman_bootdisk/api/v2/subnet_disks_controller.rb +16 -4
- data/app/controllers/foreman_bootdisk/disks_controller.rb +14 -10
- data/app/helpers/concerns/foreman_bootdisk/hosts_helper_ext.rb +71 -45
- data/app/lib/foreman_bootdisk/scope/bootdisk.rb +28 -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 +13 -6
- data/app/models/concerns/foreman_bootdisk/orchestration/compute.rb +38 -22
- data/app/models/setting/bootdisk.rb +25 -19
- data/app/services/foreman_bootdisk/iso_generator.rb +132 -51
- data/app/services/foreman_bootdisk/renderer.rb +36 -19
- data/app/views/foreman_bootdisk/generic_efi_host.erb +68 -0
- data/app/views/foreman_bootdisk/generic_static_host.erb +34 -0
- data/app/views/foreman_bootdisk/host.erb +29 -8
- data/db/seeds.d/50-bootdisk_templates.rb +15 -31
- data/lib/foreman_bootdisk/engine.rb +9 -6
- data/lib/foreman_bootdisk/version.rb +1 -1
- data/lib/tasks/bootdisk.rake +10 -24
- data/locale/action_names.rb +7 -0
- data/locale/ca/LC_MESSAGES/foreman_bootdisk.mo +0 -0
- data/locale/ca/foreman_bootdisk.po +58 -7
- data/locale/de/LC_MESSAGES/foreman_bootdisk.mo +0 -0
- data/locale/de/foreman_bootdisk.po +62 -11
- data/locale/en/LC_MESSAGES/foreman_bootdisk.mo +0 -0
- data/locale/en/foreman_bootdisk.po +56 -5
- data/locale/en_GB/LC_MESSAGES/foreman_bootdisk.mo +0 -0
- data/locale/en_GB/foreman_bootdisk.po +62 -11
- data/locale/es/LC_MESSAGES/foreman_bootdisk.mo +0 -0
- data/locale/es/foreman_bootdisk.po +62 -11
- data/locale/foreman_bootdisk.pot +108 -36
- data/locale/fr/LC_MESSAGES/foreman_bootdisk.mo +0 -0
- data/locale/fr/foreman_bootdisk.po +61 -10
- data/locale/it/LC_MESSAGES/foreman_bootdisk.mo +0 -0
- data/locale/it/foreman_bootdisk.po +60 -9
- data/locale/ja/LC_MESSAGES/foreman_bootdisk.mo +0 -0
- data/locale/ja/foreman_bootdisk.po +61 -10
- data/locale/ko/LC_MESSAGES/foreman_bootdisk.mo +0 -0
- data/locale/ko/foreman_bootdisk.po +60 -9
- data/locale/pt_BR/LC_MESSAGES/foreman_bootdisk.mo +0 -0
- data/locale/pt_BR/foreman_bootdisk.po +60 -9
- data/locale/ru/LC_MESSAGES/foreman_bootdisk.mo +0 -0
- data/locale/ru/foreman_bootdisk.po +60 -9
- data/locale/sv_SE/LC_MESSAGES/foreman_bootdisk.mo +0 -0
- data/locale/sv_SE/foreman_bootdisk.po +59 -8
- data/locale/zh_CN/LC_MESSAGES/foreman_bootdisk.mo +0 -0
- data/locale/zh_CN/foreman_bootdisk.po +60 -9
- data/locale/zh_TW/LC_MESSAGES/foreman_bootdisk.mo +0 -0
- data/locale/zh_TW/foreman_bootdisk.po +60 -9
- data/release-gem +2 -0
- 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 +12 -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 +14 -9
@@ -3,10 +3,23 @@
|
|
3
3
|
module ForemanBootdisk
|
4
4
|
module Scope
|
5
5
|
class Bootdisk < ::Foreman::Renderer::Scope::Provisioning
|
6
|
+
extend ApipieDSL::Class
|
7
|
+
|
8
|
+
apipie :class, 'Macros related to provisioning via boot disk' do
|
9
|
+
name 'Bootdisk'
|
10
|
+
sections only: %w[all provisioning]
|
11
|
+
end
|
12
|
+
|
13
|
+
apipie :method, 'Generates URL for boot chain' do
|
14
|
+
optional :mac, String, 'MAC address of the host', default: 'MAC address of the current host'
|
15
|
+
optional :action, String, 'Bootloader to use', default: 'iPXE'
|
16
|
+
returns String, desc: 'URL for boot chain'
|
17
|
+
example 'bootdisk_chain_url #=> "http://foreman.some.host.fqdn/unattended/iPXE?mac=00%3A11%3A22%3A33%3A44%3A55"'
|
18
|
+
example 'bootdisk_chain_url("00:11:22:33:44:55") #=> "http://foreman.some.host.fqdn/unattended/iPXE?mac=00%3A11%3A22%3A33%3A44%3A55"'
|
19
|
+
end
|
6
20
|
def bootdisk_chain_url(mac = host.try(:mac), action = 'iPXE')
|
7
21
|
url = foreman_url(action)
|
8
22
|
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
23
|
new_query_data = URI.decode_www_form(u.query || '') << ['mac', mac || '']
|
11
24
|
new_querystring = URI.encode_www_form(new_query_data)
|
12
25
|
u.query = nil
|
@@ -15,9 +28,23 @@ module ForemanBootdisk
|
|
15
28
|
u.to_s
|
16
29
|
end
|
17
30
|
|
31
|
+
apipie :method, 'Always raises an error with a description provided as an argument' do
|
32
|
+
desc 'This method is useful for aborting script execution if some of the conditions are not met'
|
33
|
+
list :args, desc: 'Description for the error'
|
34
|
+
raises error: ::Foreman::Exception, desc: 'The error is always being raised'
|
35
|
+
returns nil, desc: "Doesn't return anything"
|
36
|
+
example "<%
|
37
|
+
interface = @host.provision_interface #=> interface = nil
|
38
|
+
bootdisk_raise(N_('Host has no provisioning interface defined')) unless interface #=> Foreman::Exception is raised and the execution of the script is aborted
|
39
|
+
%>"
|
40
|
+
end
|
18
41
|
def bootdisk_raise(*args)
|
19
42
|
raise ::Foreman::Exception.new(*args)
|
20
43
|
end
|
44
|
+
|
45
|
+
def template_name
|
46
|
+
"Foreman Bootdisk"
|
47
|
+
end
|
21
48
|
end
|
22
49
|
end
|
23
50
|
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
|
@@ -4,15 +4,14 @@ require 'uri'
|
|
4
4
|
|
5
5
|
module ForemanBootdisk
|
6
6
|
module HostExt
|
7
|
+
extend ApipieDSL::Extension
|
8
|
+
|
7
9
|
def bootdisk_template
|
8
|
-
template = ProvisioningTemplate.unscoped.find_by(
|
9
|
-
name: Setting[:bootdisk_host_template]
|
10
|
-
)
|
10
|
+
template = ProvisioningTemplate.unscoped.find_by(name: Setting[:bootdisk_host_template])
|
11
11
|
unless template
|
12
|
-
raise ::Foreman::Exception.new(
|
13
|
-
N_('Unable to find template specified by %s setting'), 'bootdisk_host_template'
|
14
|
-
)
|
12
|
+
raise ::Foreman::Exception.new(N_('Unable to find template specified by %s setting'), 'bootdisk_host_template')
|
15
13
|
end
|
14
|
+
|
16
15
|
template
|
17
16
|
end
|
18
17
|
|
@@ -39,5 +38,13 @@ module ForemanBootdisk
|
|
39
38
|
def can_be_built?
|
40
39
|
super || (managed? && SETTINGS[:unattended] && bootdisk_build? && !build?)
|
41
40
|
end
|
41
|
+
|
42
|
+
apipie_update :class do
|
43
|
+
property :bootdisk_build?, one_of: [true, false], desc: 'Returns true if provision method for this host is bootdisk, false otherwise'
|
44
|
+
end
|
42
45
|
end
|
43
46
|
end
|
47
|
+
|
48
|
+
class Host::Managed::Jail < Safemode::Jail
|
49
|
+
allow :bootdisk_build?
|
50
|
+
end
|
@@ -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,20 +20,38 @@ 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
|
36
|
-
|
37
|
-
|
45
|
+
if self.build? && self.provisioning_template(kind: :PXELinux) && self.provisioning_template(kind: :PXEGrub2)
|
46
|
+
logger.info format('Generating FULL HOST ISO image for %s', name)
|
47
|
+
ForemanBootdisk::ISOGenerator.generate_full_host(self, dir: Dir.tmpdir) do |image|
|
48
|
+
FileUtils.mv image, bootdisk_isofile
|
49
|
+
end
|
50
|
+
else
|
51
|
+
logger.info format('Generating HOST ISO image for %s', name)
|
52
|
+
ForemanBootdisk::ISOGenerator.generate(ipxe: bootdisk_template_render, dir: Dir.tmpdir) do |image|
|
53
|
+
FileUtils.mv image, bootdisk_isofile
|
54
|
+
end
|
38
55
|
end
|
39
56
|
end
|
40
57
|
|
@@ -46,8 +63,11 @@ module ForemanBootdisk
|
|
46
63
|
compute_resource.iso_attach(File.basename(bootdisk_isofile), uuid)
|
47
64
|
end
|
48
65
|
|
66
|
+
def bootdisk_detach_iso
|
67
|
+
compute_resource.iso_detach(uuid)
|
68
|
+
end
|
69
|
+
|
49
70
|
def setGenerateIsoImage
|
50
|
-
logger.info format('Generating ISO image for %s', name)
|
51
71
|
bootdisk_generate_iso_image
|
52
72
|
rescue StandardError => e
|
53
73
|
failure format(_('Failed to generate ISO image for instance %{name}: %{message}'), name: name, message: e.message), e
|
@@ -73,18 +93,14 @@ module ForemanBootdisk
|
|
73
93
|
|
74
94
|
def delAttachIsoImage; end
|
75
95
|
|
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
|
96
|
+
def setDetachIsoImage
|
97
|
+
logger.info format('Detaching ISO image from CDROM drive for %s', name)
|
98
|
+
bootdisk_detach_iso
|
99
|
+
rescue StandardError => e
|
100
|
+
failure format(_('Failed to detach ISO image from CDROM drive of instance %{name}: %{message}'), name: name, message: e.message), e
|
87
101
|
end
|
102
|
+
|
103
|
+
def delDetachIsoImage; end
|
88
104
|
end
|
89
105
|
end
|
90
106
|
end
|
@@ -2,34 +2,40 @@
|
|
2
2
|
|
3
3
|
class Setting
|
4
4
|
class Bootdisk < ::Setting
|
5
|
-
def self.
|
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
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
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
|
+
set('bootdisk_allowed_types', N_('List of allowed bootdisk types, remove type to disable it'), Setting::Bootdisk.bootdisk_types, N_('Allowed bootdisk types'))
|
26
|
+
]
|
29
27
|
end
|
30
28
|
|
31
29
|
def self.humanized_category
|
32
30
|
N_('Boot disk')
|
33
31
|
end
|
32
|
+
|
33
|
+
def self.bootdisk_types
|
34
|
+
%w(generic host full_host subnet)
|
35
|
+
end
|
36
|
+
|
37
|
+
def self.allowed_types
|
38
|
+
Setting['bootdisk_allowed_types']
|
39
|
+
end
|
34
40
|
end
|
35
41
|
end
|
@@ -10,10 +10,13 @@ require 'uri'
|
|
10
10
|
# requires syslinux, ipxe/ipxe-bootimgs, genisoimage, isohybrid
|
11
11
|
module ForemanBootdisk
|
12
12
|
class ISOGenerator
|
13
|
+
extend Foreman::HTTPProxy
|
14
|
+
|
13
15
|
def self.generate_full_host(host, opts = {}, &block)
|
14
|
-
raise Foreman::Exception
|
16
|
+
raise Foreman::Exception.new(N_('Host is not in build mode, so the template cannot be rendered')) unless host.build?
|
15
17
|
|
16
|
-
|
18
|
+
isolinux_template = render_template(host, :PXELinux, ForemanBootdisk::Scope::FullHostBootdisk)
|
19
|
+
grub_template = render_template(host, :PXEGrub2, ForemanBootdisk::Scope::FullHostBootdiskEfi)
|
17
20
|
|
18
21
|
# pxe_files and filename conversion is utterly bizarre
|
19
22
|
# aim to convert filenames to something usable under ISO 9660, to match as rendered in the template
|
@@ -24,84 +27,154 @@ module ForemanBootdisk
|
|
24
27
|
hash[iso_filename] = host.url_for_boot(type)
|
25
28
|
end
|
26
29
|
|
27
|
-
generate(opts.merge(isolinux:
|
30
|
+
generate(opts.merge(isolinux: isolinux_template, grub: grub_template, files: files), &block)
|
31
|
+
rescue ::Foreman::Exception => e
|
32
|
+
if e.code == 'ERF42-0067'
|
33
|
+
ForemanBootdisk.logger.warn e
|
34
|
+
else
|
35
|
+
raise e
|
36
|
+
end
|
28
37
|
end
|
29
38
|
|
30
|
-
def self.
|
31
|
-
|
39
|
+
def self.render_template(host, kind, scope_class)
|
40
|
+
template = host.provisioning_template(kind: kind)
|
32
41
|
|
33
|
-
raise Foreman::Exception
|
42
|
+
raise Foreman::Exception.new(N_('Unable to generate disk template, %{kind} template not found.'), kind: kind) unless template
|
34
43
|
|
35
44
|
template = ForemanBootdisk::Renderer.new.render_template(
|
36
|
-
template:
|
45
|
+
template: template,
|
37
46
|
host: host,
|
38
|
-
scope_class:
|
47
|
+
scope_class: scope_class
|
39
48
|
)
|
40
49
|
|
41
50
|
unless template
|
42
51
|
err = host.errors.full_messages.to_sentence
|
43
|
-
raise
|
52
|
+
raise Foreman::Exception.new(N_('Unable to generate disk %{kind} template: %{error}'), kind: kind, error: err)
|
44
53
|
end
|
45
54
|
|
46
55
|
template
|
47
56
|
end
|
48
57
|
|
58
|
+
def self.system_or_exception(command, error)
|
59
|
+
unless system(command)
|
60
|
+
ForemanBootdisk.logger.debug "Bootdisk command failed: #{command}"
|
61
|
+
raise Foreman::Exception.new(N_(error))
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
49
65
|
def self.generate(opts = {})
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
66
|
+
# isolinux template not provided for generic BIOS bootdisks
|
67
|
+
if opts[:isolinux].nil? && opts[:ipxe]
|
68
|
+
opts[:isolinux] = <<~EOT
|
69
|
+
default ipxe
|
70
|
+
label ipxe
|
71
|
+
kernel /ipxe
|
72
|
+
initrd /script
|
73
|
+
EOT
|
74
|
+
end
|
56
75
|
|
57
|
-
|
58
|
-
|
76
|
+
if opts[:grub].nil? && opts[:ipxe]
|
77
|
+
opts[:grub] = <<~EOT
|
78
|
+
echo "Grub2 template not associated and/or unsupported bootdisk type."
|
79
|
+
echo "The following bootdisks are supported for EFI systems:"
|
80
|
+
echo " * FULL HOST BOOTDISK"
|
81
|
+
echo " * SUBNET GENERIC BOOTDISK"
|
82
|
+
echo "The system will poweroff in few minutes..."
|
83
|
+
sleep 500
|
84
|
+
poweroff
|
85
|
+
EOT
|
86
|
+
end
|
59
87
|
|
60
|
-
|
61
|
-
|
62
|
-
|
88
|
+
# And create new temporary directory:
|
89
|
+
wd = Dir.mktmpdir('bootdisk-iso-', Rails.root.join('tmp'))
|
90
|
+
Dir.mkdir(File.join(wd, 'build'))
|
63
91
|
|
64
|
-
|
92
|
+
if opts[:isolinux]
|
93
|
+
isolinux_source_file = File.join(Setting[:bootdisk_isolinux_dir], 'isolinux.bin')
|
94
|
+
raise Foreman::Exception.new(N_('Please ensure the isolinux/syslinux package(s) are installed.')) unless File.exist?(isolinux_source_file)
|
65
95
|
|
66
|
-
|
67
|
-
FileUtils.cp(ldlinux_source_file, File.join(wd, 'build', 'ldlinux.c32')) if File.exist?(ldlinux_source_file)
|
96
|
+
FileUtils.cp(isolinux_source_file, File.join(wd, 'build', 'isolinux.bin'))
|
68
97
|
|
69
|
-
|
70
|
-
|
71
|
-
|
98
|
+
source_files = ['ldlinux.c32', 'menu.c32', 'libutil.c32']
|
99
|
+
source_files.each do |source_file|
|
100
|
+
full_path = File.join(Setting[:bootdisk_syslinux_dir], source_file)
|
101
|
+
FileUtils.cp(full_path, File.join(wd, 'build', source_file)) if File.exist?(full_path)
|
72
102
|
end
|
73
103
|
|
74
|
-
|
75
|
-
|
76
|
-
raise Foreman::Exception, N_('Please ensure the ipxe-bootimgs package is installed.') unless File.exist?(ipxe_source_file)
|
77
|
-
|
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]) }
|
104
|
+
File.open(File.join(wd, 'build', 'isolinux.cfg'), 'w') do |file|
|
105
|
+
file.write(opts[:isolinux])
|
80
106
|
end
|
107
|
+
end
|
81
108
|
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
fetch(File.join(wd, 'build', file), source)
|
86
|
-
end
|
87
|
-
end
|
88
|
-
end
|
109
|
+
if opts[:ipxe]
|
110
|
+
ipxe_source_file = File.join(Setting[:bootdisk_ipxe_dir], 'ipxe.lkrn')
|
111
|
+
raise Foreman::Exception.new(N_('Please ensure the ipxe-bootimgs package is installed.')) unless File.exist?(ipxe_source_file)
|
89
112
|
|
90
|
-
|
91
|
-
|
92
|
-
|
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')))
|
113
|
+
FileUtils.cp(ipxe_source_file, File.join(wd, 'build', 'ipxe'))
|
114
|
+
File.open(File.join(wd, 'build', 'script'), 'w') { |file| file.write(opts[:ipxe]) }
|
115
|
+
end
|
96
116
|
|
97
|
-
|
98
|
-
|
117
|
+
if opts[:grub]
|
118
|
+
FileUtils.mkdir_p(File.join(wd, 'build', 'EFI', 'BOOT'))
|
119
|
+
# a copy when using CDROM directly
|
120
|
+
File.open(File.join(wd, 'build', 'EFI', 'BOOT', 'grub.cfg'), 'w') { |file| file.write(opts[:grub]) }
|
121
|
+
# a copy when using USB/HDD (copied into EFI image as well)
|
122
|
+
File.open(File.join(wd, 'build', 'grub-hdd.cfg'), 'w') do |f|
|
123
|
+
# set root to (cd0) or similar when booting via disk
|
124
|
+
f.write("search --file /ISOLINUX.BIN --set root\n")
|
125
|
+
f.write(opts[:grub])
|
126
|
+
end
|
127
|
+
efibootimg = File.join(wd, 'build', 'efiboot.img')
|
128
|
+
system_or_exception("truncate -s 4M #{efibootimg}", N_('Creating new image failed, install truncate utility'))
|
129
|
+
system_or_exception("mkfs.msdos #{efibootimg}", N_('Failed to format the ESP image via mkfs.msdos'))
|
130
|
+
system_or_exception("mmd -i #{efibootimg} '::/EFI'", N_('Failed to create a directory within the ESP image'))
|
131
|
+
system_or_exception("mmd -i #{efibootimg} '::/EFI/BOOT'", N_('Failed to create a directory within the ESP image'))
|
132
|
+
{
|
133
|
+
File.join(wd, 'build', 'grub-hdd.cfg').to_s => 'GRUB.CFG',
|
134
|
+
File.join(Setting[:bootdisk_grub2_dir], 'grubx64.efi').to_s => 'GRUBX64.EFI',
|
135
|
+
File.join(Setting[:bootdisk_grub2_dir], 'shimx64.efi').to_s => 'BOOTX64.EFI',
|
136
|
+
}.each do |src, dest|
|
137
|
+
raise(Foreman::Exception.new(N_('Ensure %{file} is readable (or update "Grub2 directory" setting)'), file: src)) unless File.exist?(src)
|
138
|
+
raise(Foreman::Exception.new(N_('Unable to mcopy %{file}'), file: src)) unless system("mcopy -m -i #{efibootimg} '#{src}' '#{"::/EFI/BOOT/#{dest}"}'")
|
139
|
+
end
|
140
|
+
mkiso_args = "-eltorito-alt-boot -e efiboot.img -no-emul-boot"
|
141
|
+
isohybrod_args = ["--uefi"]
|
142
|
+
else
|
143
|
+
mkiso_args = ''
|
144
|
+
isohybrod_args = []
|
145
|
+
end
|
99
146
|
|
100
|
-
|
147
|
+
if opts[:files]
|
148
|
+
if opts[:files].respond_to?(:each)
|
149
|
+
opts[:files].each do |file, source|
|
150
|
+
fetch(File.join(wd, 'build', file), source)
|
151
|
+
end
|
152
|
+
end
|
101
153
|
end
|
154
|
+
|
155
|
+
iso = if opts[:dir]
|
156
|
+
Tempfile.new(['bootdisk', '.iso'], opts[:dir]).path
|
157
|
+
else
|
158
|
+
File.join(wd, 'output.iso')
|
159
|
+
end
|
160
|
+
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')))
|
161
|
+
|
162
|
+
# Make the ISO bootable as a HDD/USB disk too
|
163
|
+
raise Foreman::Exception.new(N_('ISO hybrid conversion failed: %s'), $?) unless system(*["isohybrid", isohybrod_args, iso].flatten.compact)
|
164
|
+
|
165
|
+
yield iso
|
166
|
+
ensure
|
167
|
+
# Clean the working directory (not the ISO file itself)
|
168
|
+
FileUtils.rm_f(File.join(wd, 'build'))
|
169
|
+
# Temporary directory cannot be cleaned in-process due to asynchronous send_file call.
|
170
|
+
# Also we cannot rely on systemd-tmpfiles-clean as private temporary files are not subject
|
171
|
+
# of scheduled cleanups. Let's clean bootdisks from prevous requests manually by finding
|
172
|
+
# and deleting all directories created 30 minutes ago.
|
173
|
+
delete_older_than = Time.now.to_i - (60 * 30)
|
174
|
+
Rails.root.glob('tmp/bootdisk-iso-*').select { |f| File.ctime(f) < delete_older_than }.each { |f| FileUtils.rm_f(f) }
|
102
175
|
end
|
103
176
|
|
104
|
-
def self.build_mkiso_command(output_file:, source_directory:)
|
177
|
+
def self.build_mkiso_command(output_file:, source_directory:, extra_commands:)
|
105
178
|
arguments = [
|
106
179
|
"-o #{output_file}",
|
107
180
|
'-iso-level 2',
|
@@ -111,6 +184,7 @@ module ForemanBootdisk
|
|
111
184
|
'-boot-load-size 4',
|
112
185
|
'-boot-info-table'
|
113
186
|
]
|
187
|
+
arguments.push(extra_commands) unless extra_commands.nil?
|
114
188
|
[Setting[:bootdisk_mkiso_command], arguments, source_directory].flatten.join(' ')
|
115
189
|
end
|
116
190
|
|
@@ -137,7 +211,14 @@ module ForemanBootdisk
|
|
137
211
|
ForemanBootdisk.logger.info("Fetching #{uri}")
|
138
212
|
write_cache = use_cache
|
139
213
|
uri = URI(uri)
|
140
|
-
|
214
|
+
|
215
|
+
if proxy_http_request?(nil, uri.host, uri.scheme)
|
216
|
+
proxy_uri = URI.parse(http_proxy)
|
217
|
+
http_object = Net::HTTP::Proxy(proxy_uri.host, proxy_uri.port, proxy_uri.user, proxy_uri.password)
|
218
|
+
else
|
219
|
+
http_object = Net::HTTP
|
220
|
+
end
|
221
|
+
http_object.start(uri.host, uri.port) do |http|
|
141
222
|
request = Net::HTTP::Get.new(uri.request_uri, 'Accept-Encoding' => 'plain')
|
142
223
|
|
143
224
|
http.request(request) do |response|
|
@@ -153,7 +234,7 @@ module ForemanBootdisk
|
|
153
234
|
# prevent multiple writes to the cache
|
154
235
|
write_cache = false
|
155
236
|
else
|
156
|
-
response.
|
237
|
+
raise ::Foreman::Exception, N_(format("Unable to download boot file %{uri}, HTTP return code %{code}", uri: uri, code: response.code))
|
157
238
|
end
|
158
239
|
end
|
159
240
|
end
|