Skip to content

Commit fdf992f

Browse files
committed
Add qemu and swtpm package info to report html
Signed-off-by: Leidong Wang <leidwang@redhat.com>
1 parent 27cbcad commit fdf992f

File tree

6 files changed

+262
-4
lines changed

6 files changed

+262
-4
lines changed

lib/all.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ module AutoHCK
5858
autoload_relative :MultiLogger, 'auxiliary/multi_logger'
5959
autoload_relative :NotImplementedError, 'exceptions'
6060
autoload_relative :OpenJsonError, 'exceptions'
61+
autoload_relative :PackageManager, 'auxiliary/package_manager'
6162
autoload_relative :Pgroup, 'auxiliary/pgroup'
6263
autoload_relative :PhysHCK, 'setupmanagers/physhck/physhck'
6364
autoload_relative :Playlist, 'engines/hcktest/playlist'

lib/auxiliary/package_manager.rb

Lines changed: 205 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,205 @@
1+
# typed: true
2+
# frozen_string_literal: true
3+
4+
# AutoHCK module
5+
module AutoHCK
6+
# PackageManager class for cross-platform package queries
7+
class PackageManager
8+
extend T::Sig
9+
10+
class UnsupportedPlatformError < StandardError; end
11+
12+
SUPPORTED_DISTROS = {
13+
'ubuntu' => :debian,
14+
'debian' => :debian,
15+
'centos' => :rhel,
16+
'rhel' => :rhel,
17+
'rocky' => :rhel,
18+
'almalinux' => :rhel,
19+
'fedora' => :fedora,
20+
'arch' => :arch,
21+
'manjaro' => :arch
22+
}.freeze
23+
24+
sig { void }
25+
def initialize
26+
@logger = nil
27+
@distro_info = detect_distro
28+
end
29+
30+
sig { params(logger: T.untyped).returns(T.untyped) }
31+
attr_writer :logger
32+
33+
sig { params(binary_path: String).returns(T.nilable(String)) }
34+
def query_package(binary_path)
35+
return nil if binary_path.empty?
36+
37+
query_method = distro_query_method(@distro_info[:family])
38+
return unsupported_platform_warning unless query_method
39+
40+
send(query_method, binary_path)
41+
rescue StandardError => e
42+
log_warning("Failed to query package for #{binary_path}: #{e.message}")
43+
nil
44+
end
45+
46+
private
47+
48+
sig { params(family: Symbol).returns(T.nilable(Symbol)) }
49+
def distro_query_method(family)
50+
{
51+
debian: :query_debian_package,
52+
rhel: :query_rhel_package,
53+
fedora: :query_fedora_package,
54+
arch: :query_arch_package
55+
}[family]
56+
end
57+
58+
sig { returns(T.nilable(String)) }
59+
def unsupported_platform_warning
60+
log_warning("Unsupported platform: #{@distro_info[:name]} (#{@distro_info[:family]})")
61+
nil
62+
end
63+
64+
sig { returns(T::Hash[Symbol, T.untyped]) }
65+
def detect_distro
66+
# Check for common distribution identification files
67+
if File.exist?('/etc/os-release')
68+
parse_os_release
69+
elsif File.exist?('/etc/lsb-release')
70+
parse_lsb_release
71+
elsif File.exist?('/etc/redhat-release')
72+
parse_redhat_release
73+
else
74+
{ name: 'unknown', family: :unknown }
75+
end
76+
end
77+
78+
sig { returns(T::Hash[Symbol, T.untyped]) }
79+
def parse_os_release
80+
content = File.read('/etc/os-release')
81+
id = content[/^ID=(.+)/, 1]&.gsub(/["']/, '')&.downcase
82+
83+
return { name: 'unknown', family: :unknown } unless id
84+
85+
return parse_fedora_release(content, id) if id == 'fedora'
86+
87+
family = SUPPORTED_DISTROS[id] || :unknown
88+
{ name: id, family: family }
89+
end
90+
91+
sig { params(content: String, id: String).returns(T::Hash[Symbol, T.untyped]) }
92+
def parse_fedora_release(content, id)
93+
# Check if it's Fedora Silverblue/Kinoite
94+
variant = content[/^VARIANT_ID=(.+)/, 1]&.gsub(/["']/, '')&.downcase
95+
family = fedora_immutable_variant?(variant) ? :fedora_immutable : :fedora
96+
{ name: id, family: family, variant: variant }
97+
end
98+
99+
sig { params(variant: T.nilable(String)).returns(T::Boolean) }
100+
def fedora_immutable_variant?(variant)
101+
return false unless variant
102+
103+
variant.include?('silverblue') || variant.include?('kinoite')
104+
end
105+
106+
sig { returns(T::Hash[Symbol, T.untyped]) }
107+
def parse_lsb_release
108+
content = File.read('/etc/lsb-release')
109+
id = content[/^DISTRIB_ID=(.+)/, 1]&.gsub(/["']/, '')&.downcase
110+
111+
family = id ? SUPPORTED_DISTROS[id] || :unknown : :unknown
112+
{ name: id || 'unknown', family: family }
113+
end
114+
115+
sig { returns(T::Hash[Symbol, T.untyped]) }
116+
def parse_redhat_release
117+
content = File.read('/etc/redhat-release').downcase
118+
119+
if content.include?('centos')
120+
{ name: 'centos', family: :rhel }
121+
elsif content.include?('red hat')
122+
{ name: 'rhel', family: :rhel }
123+
elsif content.include?('fedora')
124+
{ name: 'fedora', family: :fedora }
125+
else
126+
{ name: 'unknown', family: :unknown }
127+
end
128+
end
129+
130+
sig { params(binary_path: String).returns(T.nilable(String)) }
131+
def query_debian_package(binary_path)
132+
# Use dpkg -S to find which package owns the file
133+
result = `dpkg -S "#{binary_path}" 2>/dev/null`
134+
return nil if $CHILD_STATUS.exitstatus != 0 || result.empty?
135+
136+
# dpkg -S output format: "package: /path/to/file"
137+
package = result.split(':').first&.strip
138+
return nil unless package
139+
140+
# Get the full package version
141+
version_result = `dpkg -l "#{package}" 2>/dev/null | tail -1`
142+
return package if version_result.empty?
143+
144+
# dpkg -l output format: "ii package version arch description"
145+
parts = version_result.split
146+
return package unless parts.length >= 3
147+
148+
"#{package}_#{parts[2]}_#{parts[3]}"
149+
end
150+
151+
sig { params(binary_path: String).returns(T.nilable(String)) }
152+
def query_rhel_package(binary_path)
153+
# Use rpm -qf for RHEL/CentOS
154+
result = `rpm -qf "#{binary_path}" 2>/dev/null`
155+
return nil if $CHILD_STATUS.exitstatus != 0 || result.empty?
156+
157+
result.strip
158+
end
159+
160+
sig { params(binary_path: String).returns(T.nilable(String)) }
161+
def query_fedora_package(binary_path)
162+
# For regular Fedora, use rpm -qf
163+
# For Fedora Silverblue/immutable, also use rpm -qf (rpm-ostree manages the rpm db)
164+
if @distro_info[:family] == :fedora_immutable
165+
log_info("Detected Fedora immutable variant (#{@distro_info[:variant]}), using rpm query")
166+
end
167+
168+
result = `rpm -qf "#{binary_path}" 2>/dev/null`
169+
return nil if $CHILD_STATUS.exitstatus != 0 || result.empty?
170+
171+
result.strip
172+
end
173+
174+
sig { params(binary_path: String).returns(T.nilable(String)) }
175+
def query_arch_package(binary_path)
176+
# Use pacman -Qo for Arch Linux
177+
result = `pacman -Qo "#{binary_path}" 2>/dev/null`
178+
return nil if $CHILD_STATUS.exitstatus != 0 || result.empty?
179+
180+
# pacman -Qo output format: "/path/to/file is owned by package version"
181+
match = result.match(/is owned by (.+)/)
182+
return nil unless match
183+
184+
T.must(match[1]).strip
185+
end
186+
187+
sig { params(message: String).void }
188+
def log_info(message)
189+
if @logger
190+
@logger.info(message)
191+
else
192+
puts "INFO: #{message}"
193+
end
194+
end
195+
196+
sig { params(message: String).void }
197+
def log_warning(message)
198+
if @logger
199+
@logger.warn(message)
200+
else
201+
puts "WARNING: #{message}"
202+
end
203+
end
204+
end
205+
end

lib/engines/hcktest/tests.rb

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -398,16 +398,31 @@ def archive_test_results(test, test_result)
398398
@logger.info('Test results uploaded via the result uploader')
399399
end
400400

401+
def build_system_info_data
402+
data = { 'guest' => @clients_system_info }
403+
404+
if @project.setup_manager
405+
host_info = {}
406+
qemu_version = @project.setup_manager.hypervisor_package_info
407+
swtpm_version = @project.setup_manager.hypervisor_dependencies_package_info
408+
409+
host_info['qemu_version'] = qemu_version unless qemu_version.nil? || qemu_version.empty?
410+
host_info['swtpm_version'] = swtpm_version unless swtpm_version.nil? || swtpm_version.empty?
411+
412+
data['host'] = host_info unless host_info.empty?
413+
end
414+
415+
data
416+
end
417+
401418
def report_data
402419
{
403420
'tag' => @tag,
404421
'test_stats' => tests_stats,
405422
'rejected_test' => @playlist.rejected_test,
406423
'tests' => @tests,
407424
'url' => @project.result_uploader.url,
408-
'system_info' => {
409-
'guest' => @clients_system_info
410-
},
425+
'system_info' => build_system_info_data,
411426
'sections' => RESULTS_REPORT_SECTIONS - @project.options.test.reject_report_sections
412427
}
413428
end

lib/setupmanagers/physhck/physhck.rb

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,16 @@ def hypervisor_info
4949
@logger.info('Hypervisor info is currently not supported for physical machines')
5050
end
5151

52+
def hypervisor_package_info
53+
@logger.info('Hypervisor package info is currently not supported for physical machines')
54+
nil
55+
end
56+
57+
def hypervisor_dependencies_package_info
58+
@logger.info('swtpm package info is currently not supported for physical machines')
59+
nil
60+
end
61+
5262
def host_info
5363
@logger.info('Host info is currently not supported for physical machines')
5464
end

lib/setupmanagers/qemuhck/qemuhck.rb

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ def initialize(project)
2020
initialize_project project
2121

2222
@clients_vm = {}
23+
@package_manager = PackageManager.new
24+
@package_manager.logger = @logger
2325
initialize_studio_vm
2426
initialize_clients_vm
2527
create_qemuhck_log_file
@@ -134,13 +136,27 @@ def hypervisor_info
134136
`#{@studio_vm.config['qemu_bin']} --version`.lines.first.strip
135137
end
136138

139+
def hypervisor_package_info
140+
binary_path = @studio_vm.config['qemu_bin']
141+
@package_manager.query_package(binary_path) || "Unknown package for #{binary_path}"
142+
end
143+
144+
def hypervisor_dependencies_package_info
145+
binary_path = `which #{@studio_vm.config['swtpm_bin']} 2>/dev/null`.strip
146+
return 'swtpm binary not found' if binary_path.empty?
147+
148+
@package_manager.query_package(binary_path) || "Unknown package for #{binary_path}"
149+
end
150+
137151
def host_info
138152
`uname -a`.strip
139153
end
140154

141155
def append_host_info(logs)
142156
logs << <<~HOST_INFO
143157
QEMU version: #{hypervisor_info}
158+
QEMU package: #{hypervisor_package_info}
159+
swtpm package: #{hypervisor_dependencies_package_info}
144160
System information: #{host_info}
145161
146162
HOST_INFO

lib/templates/report.html.erb

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
</div>
2222
<% end %>
2323
<% if sections.include? 'guest_info' %>
24-
<div class="col col-8">
24+
<div class="col col-4">
2525
<p class="text-center">Guest system information</p>
2626
<% system_info['guest'].each do |_, guest_info| %>
2727
<% guest_info.each do |k, v| %>
@@ -30,6 +30,17 @@
3030
<% end %>
3131
</div>
3232
<% end %>
33+
<% if system_info['host'] %>
34+
<div class="col col-4">
35+
<p class="text-center">Host system information</p>
36+
<% if system_info['host']['qemu_version'] %>
37+
<p>QEMU version: <%= system_info['host']['qemu_version'] %></p>
38+
<% end %>
39+
<% if system_info['host']['swtpm_version'] %>
40+
<p>swtpm version: <%= system_info['host']['swtpm_version'] %></p>
41+
<% end %>
42+
</div>
43+
<% end %>
3344
</div>
3445
<div class="row pb-3">
3546
<p>Report generation time: <%= Time.now %></p>

0 commit comments

Comments
 (0)