foreman_bootdisk 15.0.0 → 17.1.0
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/.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
|