Skip to content

Commit

Permalink
Fixes #35269 - Support system image download for installation media
Browse files Browse the repository at this point in the history
* Include proxy.fetch_system_image
* Add system_image_path variable for template reference
* Adapt PXELinux template
* Add tftp_http_port setting
  • Loading branch information
bastian-src committed Dec 2, 2022
1 parent 699b7a3 commit 6004445
Show file tree
Hide file tree
Showing 7 changed files with 104 additions and 9 deletions.
51 changes: 47 additions & 4 deletions app/models/concerns/orchestration/tftp.rb
Original file line number Diff line number Diff line change
Expand Up @@ -111,10 +111,25 @@ def setTFTPBootFiles
logger.info "Fetching required TFTP boot files for #{host.name}"
valid = []

host.operatingsystem.pxe_files(host.medium_provider).each do |bootfile_info|
bootfile_info.each do |prefix, path|
valid << each_unique_feasible_tftp_proxy do |proxy|
proxy.fetch_boot_file(:prefix => prefix.to_s, :path => path)
# Check host.medium_provider path for iso image
is_image_path = File.extname(host.medium_uri.to_s).downcase.end_with?(".iso")

valid << each_unique_feasible_tftp_proxy do |proxy|
bootfiles = host.operatingsystem.pxe_files(host.medium_provider)
# fetch boot files and iso image
if is_image_path
max_request_timeout = Setting[:proxy_request_timeout]
image_url = host.medium_uri.to_s
tftp_files = pxe_files_from_url(bootfiles, image_url)
tftp_dst = pxe_dst_from_bootfiles(bootfiles)
image_dst = host.operatingsystem.system_image_path(host.medium_provider, host, true, false)
image_status = poll_fetch_system_image(image_url, image_dst, tftp_files, tftp_dst, max_request_timeout)
logger.error "Timeout fetching system image #{image_url}. See smart proxy log for details." unless image_status
else
bootfiles.each do |bootfile_info|
bootfile_info.each do |prefix, path|
proxy.fetch_boot_file(:prefix => prefix.to_s, :path => path)
end
end
end
end
Expand Down Expand Up @@ -197,4 +212,32 @@ def each_unique_feasible_tftp_proxy
end
results.all?
end

# Extract pxe files from bootfiles
def pxe_files_from_url(bootfiles, image_url)
pxe_files = []
bootfiles.each { |pxe_url| pxe_files.append(pxe_url.values.first.delete_prefix(image_url)) }
pxe_files
end

# Extract pxe destination file name from bootfiles
def pxe_dst_from_bootfiles(bootfiles)
bootfiles.first.keys.first
end

def poll_fetch_system_image(image_url, image_dst, tftp_files, tftp_dst, max_request_time)
retries = poll_system_image_retries
pause_time = max_request_time / poll_system_image_retries
request_status = proxy.fetch_system_image(:url => image_url, :path => image_dst, :files => tftp_files, :tftp_path => tftp_dst)
until retries <= 0 || request_status == 200
sleep(pause_time)
request_status = proxy.fetch_system_image(:url => image_url, :path => image_dst, :files => tftp_files, :tftp_path => tftp_dst)
retries -= 1
end
image_status == 200
end

def poll_system_image_retries
10
end
end
21 changes: 20 additions & 1 deletion app/models/operatingsystem.rb
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ class Operatingsystem < ApplicationRecord
property :password_hash, String, desc: 'Encrypted hash of the operating system password'
end
class Jail < Safemode::Jail
allow :id, :name, :major, :minor, :family, :to_s, :==, :release, :release_name, :kernel, :initrd, :pxe_type, :boot_files_uri, :password_hash, :mediumpath, :bootfile
allow :id, :name, :major, :minor, :family, :to_s, :==, :release, :release_name, :kernel, :initrd, :pxe_type, :boot_files_uri, :password_hash, :mediumpath, :bootfile, :system_image_path
end

def self.title_name
Expand Down Expand Up @@ -236,6 +236,25 @@ def bootfile(medium_provider, type)
pxe_prefix(medium_provider) + "-" + pxe_file_names(medium_provider)[type.to_sym]
end

apipie :method, 'Returns path to boot image based on given medium provider and (optional) host' do
required :medium_provider, 'MediumProviders::Provider', 'Medium provider responsible to provide location of installation medium for a given entity (host or host group)'
returns String, 'Path to the boot image file'
end
def system_image_path(medium_provider, include_suffix = true, include_base_path = true)
unless medium_provider.is_a? MediumProviders::Provider
raise Foreman::Exception.new(N_('Please provide a medium provider. It can be found as @medium_provider in templates, or Foreman::Plugin.medium_providers_registry.find_provider(host)'))
end
include_base_path ? base_path = system_image_base_path : base_path = ""
include_suffix ? suffix = ".iso" : suffix = ""

"#{base_path}#{name.downcase}/#{medium_provider.unique_id}#{suffix}"
end

# Base path for system_image url
def system_image_base_path
"/tftp/system_image/"
end

# Does this OS family support a build variant that is constructed from a prebuilt archive
def supports_image
false
Expand Down
12 changes: 11 additions & 1 deletion app/models/smart_proxy.rb
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,14 @@ def setting(feature, setting)
smart_proxy_feature_by_name(feature).try(:settings).try(:[], setting)
end

def tftp_http_port
setting(:TFTP, 'http_port')
end

def tftp_http_port!
tftp_http_port || raise(::Foreman::Exception.new(N_("HTTP boot requires proxy with httpboot feature and http_port exposed setting")))
end

def httpboot_http_port
setting(:HTTPBoot, 'http_port')
end
Expand Down Expand Up @@ -202,12 +210,14 @@ def get_features
sections only: %w[all additional]
prop_group :basic_model_props, ApplicationRecord, meta: { friendly_name: 'Smart Proxy' }
property :hostname, String, desc: 'Returns name of the host with proxy'
property :tftp_http_port, Integer, desc: 'Returns proxy port for TFTP boot images'
property :tftp_http_port!, Integer, desc: 'Same as tftp_http_port, but raises Foreman::Exception if no port is set'
property :httpboot_http_port, Integer, desc: 'Returns proxy port for HTTP boot'
property :httpboot_http_port!, Integer, desc: 'Same as httpboot_http_port, but raises Foreman::Exception if no port is set'
property :httpboot_https_port, Integer, desc: 'Returns proxy port for HTTPS boot'
property :httpboot_https_port!, Integer, desc: 'Same as httpboot_https_port, but raises Foreman::Exception if no port is set'
end
class Jail < ::Safemode::Jail
allow :id, :name, :hostname, :httpboot_http_port, :httpboot_https_port, :httpboot_http_port!, :httpboot_https_port!, :url
allow :id, :name, :hostname, :tftp_http_port, :httpboot_http_port, :httpboot_https_port, :tftp_http_port!, :httpboot_http_port!, :httpboot_https_port!, :url
end
end
1 change: 1 addition & 0 deletions app/services/foreman/renderer/configuration.rb
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@ class Configuration
:static,
:template_name,
:xen,
:system_image_path,
]

DEFAULT_ALLOWED_GLOBAL_SETTINGS = [
Expand Down
3 changes: 2 additions & 1 deletion app/services/foreman/renderer/scope/variables/base.rb
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ def self.included(base)
delegate :diskLayout, :disk_layout_source, :medium, :architecture, :ptable, :use_image, :arch,
:image_file, :default_image_file, to: :host, allow_nil: true
delegate :mediumpath, :additional_media, :supports_image, :major, :preseed_path, :preseed_server,
:xen, :kernel, :initrd, to: :operatingsystem, allow_nil: true
:xen, :kernel, :initrd, :system_image_path, to: :operatingsystem, allow_nil: true
delegate :name, to: :architecture, allow_nil: true, prefix: true
delegate :content, to: :disk_layout_source, allow_nil: true, prefix: true

Expand Down Expand Up @@ -97,6 +97,7 @@ def xenserver_attributes

def pxe_config
return unless @medium_provider
@system_image_path = system_image_path(@medium_provider, host)
@kernel = kernel(@medium_provider)
@initrd = initrd(@medium_provider)
@kernel_uri, @initrd_uri = operatingsystem.boot_files_uri(@medium_provider)
Expand Down
14 changes: 14 additions & 0 deletions app/services/proxy_api/tftp.rb
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,20 @@ def fetch_boot_file(args)
raise ProxyException.new(url, e, N_("Unable to fetch TFTP boot file"))
end

# Requests that the proxy downloads and extracts an image from the media's source
# [+args+] : Hash containing
# :path => String containing the location on the smart proxy to store the image
# :url => String containing the URL of the image to download
# Returns : Integer response status
def fetch_system_image(args)
response = post(args, "fetch_system_image")
response.code
rescue RestClient::Locked
423
rescue => e
raise ProxyException.new(url, e, N_("Unable to fetch and extract TFTP system image"))
end

# returns the TFTP boot server for this proxy
def bootServer
if (response = parse(get("serverName"))) && response["serverName"].present?
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,14 @@ test_on:

options << "locale=#{host_param('lang') || 'en_US'}"
options = options.join(' ')
image_path = @preseed_path.sub(/\/?$/, '.iso')
if @preseed_path.downcase.end_with?('.iso')
image_path = @system_image_path
tftp = @host.subnet.tftp
image_host = "#{tftp}:#{tftp.tftp_http_port}"
else
image_path = @preseed_path.sub(/\/?$/, '.iso')
image_host = foreman_request_addr.split(':').first
end
-%>
#
# WARNING
Expand All @@ -55,6 +62,6 @@ DEFAULT linux cloud-init autoinstall
LABEL linux cloud-init autoinstall
KERNEL <%= @kernel %>
INITRD <%= @initrd %>
APPEND url=http://<%= @preseed_server %><%= image_path %> autoinstall ds=nocloud-net;s=http://<%= foreman_request_addr %>/userdata/ root=/dev/ram0 ramdisk_size=1500000 fsck.mode=skip <%= options %>
APPEND url=http://<%= image_host %><%= image_path %> autoinstall ds=nocloud-net;s=http://<%= foreman_request_addr %>/userdata/ root=/dev/ram0 ramdisk_size=1500000 fsck.mode=skip <%= options %>
<%= snippet_if_exists(template_name + " custom menu") %>

0 comments on commit 6004445

Please sign in to comment.