Skip to content

Commit

Permalink
Fixes #35269 - Enable boot image download of installation media
Browse files Browse the repository at this point in the history
* Include proxy.fetch_boot_image
* Add bootimage_path variable for template reference
* Adapt PXELinux template
* Add custom timeout for tftp requests
  • Loading branch information
bastian-src committed Sep 9, 2022
1 parent 124ebd2 commit 31764aa
Show file tree
Hide file tree
Showing 7 changed files with 77 additions and 17 deletions.
24 changes: 21 additions & 3 deletions app/models/concerns/orchestration/tftp.rb
Original file line number Diff line number Diff line change
Expand Up @@ -111,13 +111,31 @@ 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|
# Check host.medium_provider path for iso image
prefetch_image = 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 iso image if given
if prefetch_image
raw_files = []
bootfiles.each { |info| raw_files.append(info.values.first.delete_prefix(host.medium_uri.to_s)) }
proxy.fetch_boot_image(:url => host.medium_uri.to_s, :path => host.operatingsystem.bootimage_path(host.medium_provider, host, true, false), :files => raw_files)
end

host.operatingsystem.pxe_files(host.medium_provider).each do |bootfile_info|
bootfile_info.each do |prefix, path|
# change path in case of iso download
if prefetch_image
proxy_path = host.operatingsystem.bootimage_path(host.medium_provider, host, false)
proxy_url = "http://#{URI.parse(proxy.url).host}/#{proxy_path}"
path.sub!(host.medium_uri.to_s, proxy_url)
end
proxy.fetch_boot_file(:prefix => prefix.to_s, :path => path)
end
end
end

failure _("Failed to fetch boot files") unless valid.all?
valid.all?
end
Expand Down
23 changes: 22 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, :bootimage_path
end

def self.title_name
Expand Down Expand Up @@ -236,6 +236,27 @@ 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)'
optional :host, 'Host::Managed', 'A specific host which can set custom a boot image path'
returns String, 'Path to the boot image file'
end
def bootimage_path(medium_provider, host = nil, 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 = "/pub/installation_media/" : base_path = ""
include_suffix ? suffix = ".iso" : suffix = ""
unless host.nil?
if host.host_params.has_key? "custom_bootimage_path"
base_path = host.host_params["custom_bootimage_path"]
base_path += "/" unless base_path[-1] == '/'
end
end

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

# Does this OS family support a build variant that is constructed from a prebuilt archive
def supports_image
false
Expand Down
1 change: 1 addition & 0 deletions app/services/foreman/renderer/configuration.rb
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@ class Configuration
:static,
:template_name,
:xen,
:bootimage_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, :bootimage_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
@bootimage_path = bootimage_path(@medium_provider, host)
@kernel = kernel(@medium_provider)
@initrd = initrd(@medium_provider)
@kernel_uri, @initrd_uri = operatingsystem.boot_files_uri(@medium_provider)
Expand Down
24 changes: 14 additions & 10 deletions app/services/proxy_api/resource.rb
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,12 @@ def initialize(args)

attr_reader :connect_params

def resource
def resource(timeout = nil)
# Required in order to ability to mock the resource
unless timeout.nil?
custom_params = connect_params.merge(timeout: timeout)
return RestClient::Resource.new(url, custom_params)
end
@resource ||= RestClient::Resource.new(url, connect_params)
end

Expand Down Expand Up @@ -65,7 +69,7 @@ def parse(response)
end

# Perform GET operation on the supplied path
def get(path = nil, payload = {})
def get(path = nil, payload = {}, timeout = nil)
query = payload.delete(:query)
Foreman::Deprecation.deprecation_warning("3.3", "passing additional headers to ProxyApi resource GET action") unless payload.empty?
final_uri = path || ""
Expand All @@ -78,39 +82,39 @@ def get(path = nil, payload = {})
telemetry_duration_histogram(:proxy_api_duration, :ms, method: 'get') do
# This ensures that an extra "/" is not generated
if path
resource[final_uri].get payload
resource(timeout)[final_uri].get payload
else
resource.get payload
resource(timeout).get payload
end
end
end
end

# Perform POST operation with the supplied payload on the supplied path
def post(payload, path = "")
def post(payload, path = "", timeout = nil)
logger.debug("POST request payload: #{payload}")
with_logger do
telemetry_duration_histogram(:proxy_api_duration, :ms, method: 'post') do
resource[path].post payload
resource(timeout)[path].post payload
end
end
end

# Perform PUT operation with the supplied payload on the supplied path
def put(payload, path = "")
def put(payload, path = "", timeout = nil)
logger.debug("PUT request payload: #{payload}")
with_logger do
telemetry_duration_histogram(:proxy_api_duration, :ms, method: 'put') do
resource[path].put payload
resource(timeout)[path].put payload
end
end
end

# Perform DELETE operation on the supplied path
def delete(path)
def delete(path, timeout = nil)
with_logger do
telemetry_duration_histogram(:proxy_api_duration, :ms, method: 'delete') do
resource[path].delete
resource(timeout)[path].delete
end
end
end
Expand Down
11 changes: 11 additions & 0 deletions app/services/proxy_api/tftp.rb
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,17 @@ 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 : Boolean status
def fetch_boot_image(args)
parse(post(args, "fetch_boot_image", 180)) # Set 180 seconds timeout for large image download
rescue => e
raise ProxyException.new(url, e, N_("Unable to fetch and extract TFTP boot 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,11 @@ 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 = @bootimage_path
else
image_path = @preseed_path.sub(/\/?$/, '.iso')
end
-%>
#
# WARNING
Expand All @@ -55,6 +59,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://<%= foreman_request_addr.split(':').first %><%= 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 31764aa

Please sign in to comment.