Skip to content

Commit 3a40fce

Browse files
author
Bastian Schmidt
committed
Enable boot image download for iso images
* Implement fetch and extract boot image * Apply correct file permissions * Introduce and add tests for fetch_boot_image * Implement class for file extraction
1 parent 9268440 commit 3a40fce

6 files changed

Lines changed: 73 additions & 4 deletions

File tree

lib/proxy/archive_extract.rb

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
module Proxy
2+
class ArchiveExtract < Proxy::Util::CommandTask
3+
include Util
4+
5+
def initialize(image_path, file_in_image, dst_path)
6+
args = [
7+
which('isoinfo'),
8+
# Filename to read ISO-9660 image from
9+
'-i', image_path.to_s,
10+
# Extract specified file to stdout
11+
'-x', file_in_image.to_s,
12+
]
13+
14+
super(args, input=nil, output=dst_path)
15+
end
16+
17+
def start
18+
lock = Proxy::FileLock.try_locking(File.join(File.dirname(@output), ".#{File.basename(@output)}.lock"))
19+
if lock.nil?
20+
false
21+
else
22+
super do
23+
Proxy::FileLock.unlock(lock)
24+
File.unlink(lock)
25+
end
26+
end
27+
end
28+
end
29+
end

lib/proxy/util.rb

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,10 @@ class CommandTask
1313
# stderr is redirected to proxy error log, stdout to proxy debug log
1414
# command can be either string or array (command + arguments)
1515
# input is passed into STDIN and must be string
16-
def initialize(command, input = nil)
16+
def initialize(command, input = nil, output = nil)
1717
@command = command
1818
@input = input
19+
@output = output
1920
end
2021

2122
def start(&ensured_block)
@@ -27,8 +28,13 @@ def start(&ensured_block)
2728
logger.info "[#{thr.pid}] Started task #{cmdline_string}"
2829
stdin.write(input) if input
2930
stdin.close
30-
stdout.each do |line|
31-
logger.debug "[#{thr.pid}] #{line}"
31+
unless @output.nil?
32+
File.binwrite(@output, stdout.read)
33+
stdout.close
34+
else
35+
stdout.each do |line|
36+
logger.debug "[#{thr.pid}] #{line}"
37+
end
3238
end
3339
stderr.each do |line|
3440
logger.warn "[#{thr.pid}] #{line}"

lib/smart_proxy_main.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
require 'proxy/dependency_injection'
1515
require 'proxy/util'
1616
require 'proxy/http_download'
17+
require 'proxy/archive_extract'
1718
require 'proxy/helpers'
1819
require 'proxy/memory_store'
1920
require 'proxy/plugin_validators'

modules/tftp/server.rb

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,29 @@ def pxeconfig_file(mac)
150150
end
151151
end
152152

153+
def self.fetch_boot_image(image_dst, url, files)
154+
# Verify image_dst does not contain ".." (switch folder)
155+
raise "TFTP image file extraction error: #{file_path}" unless image_dst.split("..").size <= 1
156+
157+
base_path = Pathname.new("/var/www/html/pub/installation_media")
158+
image_path = Pathname.new(File.expand_path(image_dst, base_path)).cleanpath
159+
extr_image_dir = Pathname.new(image_path.to_s.delete_suffix(".iso"))
160+
161+
FileUtils.mkdir_p image_path.parent
162+
choose_protocol_and_fetch(url, image_path).join unless File.exist? image_path
163+
164+
files.each do |file|
165+
file_path = Pathname.new file
166+
extr_file_path = Pathname.new(File.join(extr_image_dir, file_path)).cleanpath
167+
168+
# Create destination directory
169+
FileUtils.mkdir_p extr_file_path.parent
170+
# extract iso
171+
extract_task = ::Proxy::ArchiveExtract.new(image_path, file_path, extr_file_path).start unless File.exist? extr_file_path
172+
raise "TFTP image file extraction error: #{file_path}" unless extract_task.join == 0
173+
end
174+
end
175+
153176
def self.fetch_boot_file(dst, src)
154177
filename = boot_filename(dst, src)
155178
destination = Pathname.new(File.expand_path(filename, Proxy::TFTP::Plugin.settings.tftproot)).cleanpath

modules/tftp/tftp_api.rb

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,10 @@ def create_default(variant)
3434
end
3535
end
3636

37+
post "/fetch_boot_image" do
38+
log_halt(400, "TFTP: Failed to fetch boot file: ") { Proxy::TFTP.fetch_boot_image(params[:path], params[:url], params[:files]) }
39+
end
40+
3741
post "/fetch_boot_file" do
3842
log_halt(400, "TFTP: Failed to fetch boot file: ") { Proxy::TFTP.fetch_boot_file(params[:prefix], params[:path]) }
3943
end

test/tftp/tftp_api_test.rb

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,13 @@ def test_api_can_fetch_boot_file
111111
assert last_response.ok?
112112
end
113113

114-
def test_api_can_get_servername
114+
def test_api_can_fetch_boot_image
115+
Proxy::TFTP.expects(:fetch_boot_image).with('some/image.iso', 'http://localhost/file.iso').returns(true)
116+
post "/fetch_boot_image", :path => 'some/image.iso', :url => 'http://localhost/file.iso'
117+
assert last_response.ok?
118+
end
119+
120+
def test_api_can_get_servername
115121
Proxy::TFTP::Plugin.settings.stubs(:tftp_servername).returns("servername")
116122
result = get "/serverName"
117123
assert_match /servername/, result.body

0 commit comments

Comments
 (0)