Skip to content

Commit 219a3bc

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

File tree

6 files changed

+264
-4
lines changed

6 files changed

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