Skip to content

Commit b11d1aa

Browse files
author
xianglongfei
committed
Fixed the issue in sysinfo where the dmidecode and fdisk -l commands were executed. Also added sysinfo.conf to RPM spec file to resolve packaging error.
Signed-off-by: xianglongfei <[email protected]>
1 parent 74b7379 commit b11d1aa

File tree

5 files changed

+168
-4
lines changed

5 files changed

+168
-4
lines changed

avocado/core/sysinfo.py

Lines changed: 63 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
# This code was inspired in the autotest project,
1212
# client/shared/settings.py
1313
# Author: John Admanski <[email protected]>
14+
import configparser
1415
import filecmp
1516
import logging
1617
import os
@@ -68,6 +69,47 @@ def __init__(self, basedir=None, log_packages=None, profiler=None):
6869
"""
6970
self.config = settings.as_dict()
7071

72+
# Retrieve the configured paths for sudo commands and distros from the settings dictionary
73+
sudo_commands_conf = self.config.get("sysinfo.sudo_commands", "")
74+
sudo_distros_conf = self.config.get("sysinfo.sudo_distros", "")
75+
76+
if sudo_commands_conf:
77+
log.info("sudo_commands loaded from config: %s", sudo_commands_conf)
78+
else:
79+
log.debug("sudo_commands config is empty or missing")
80+
81+
if sudo_distros_conf:
82+
log.info("sudo_distros loaded from config: %s", sudo_distros_conf)
83+
else:
84+
log.debug("sudo_distros config is empty or missing")
85+
86+
def _load_sudo_list(raw_value, key):
87+
# pylint: disable=wrong-spelling-in-docstring
88+
"""
89+
If `raw_value` is a path to an INI file, read `[sysinfo] / key`
90+
from it; otherwise, treat `raw_value` itself as a CSV list.
91+
"""
92+
if not raw_value:
93+
return ""
94+
if os.path.isfile(raw_value):
95+
parser = configparser.ConfigParser()
96+
parser.read(raw_value)
97+
return parser.get("sysinfo", key, fallback="")
98+
return raw_value
99+
100+
# Retrieve the actual sudo commands and distros values from the config files,
101+
# falling back to empty string if the keys are missing
102+
sudo_commands_value = _load_sudo_list(sudo_commands_conf, "sudo_commands")
103+
sudo_distros_value = _load_sudo_list(sudo_distros_conf, "sudo_distros")
104+
105+
self.sudo_commands = {
106+
cmd.strip().lower() for cmd in sudo_commands_value.split(",") if cmd.strip()
107+
}
108+
109+
self.sudo_distros = {
110+
dst.strip().lower() for dst in sudo_distros_value.split(",") if dst.strip()
111+
}
112+
71113
if basedir is None:
72114
basedir = utils_path.init_dir("sysinfo")
73115
self.basedir = basedir
@@ -136,15 +178,33 @@ def _set_collectibles(self):
136178

137179
for cmd in self.sysinfo_files["commands"]:
138180
self.start_collectibles.add(
139-
sysinfo.Command(cmd, timeout=timeout, locale=locale)
181+
sysinfo.Command(
182+
cmd,
183+
timeout=timeout,
184+
locale=locale,
185+
sudo_commands=self.sudo_commands,
186+
sudo_distros=self.sudo_distros,
187+
)
140188
)
141189
self.end_collectibles.add(
142-
sysinfo.Command(cmd, timeout=timeout, locale=locale)
190+
sysinfo.Command(
191+
cmd,
192+
timeout=timeout,
193+
locale=locale,
194+
sudo_commands=self.sudo_commands,
195+
sudo_distros=self.sudo_distros,
196+
)
143197
)
144198

145199
for fail_cmd in self.sysinfo_files["fail_commands"]:
146200
self.end_fail_collectibles.add(
147-
sysinfo.Command(fail_cmd, timeout=timeout, locale=locale)
201+
sysinfo.Command(
202+
fail_cmd,
203+
timeout=timeout,
204+
locale=locale,
205+
sudo_commands=self.sudo_commands,
206+
sudo_distros=self.sudo_distros,
207+
)
148208
)
149209

150210
for filename in self.sysinfo_files["files"]:

avocado/etc/avocado/sysinfo.conf

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
[sysinfo]
2+
sudo_commands = dmidecode,fdisk
3+
# Add any other commands that require sudo here, separated by commas.
4+
sudo_distros = uos,deepin
5+
# Add any other operating system that require sudo here, separated by commas.
6+
# Values of sudo_distros must match the ID= field from /etc/os-release (e.g. uos, deepin).

avocado/plugins/sysinfo.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,28 @@ def initialize(self):
169169
help_msg=help_msg,
170170
)
171171

172+
help_msg = "File with list of commands that require sudo"
173+
default = system_wide_or_base_path("etc/avocado/sysinfo.conf")
174+
settings.register_option(
175+
section="sysinfo",
176+
key="sudo_commands",
177+
key_type=prepend_base_path,
178+
default=default,
179+
help_msg=help_msg,
180+
)
181+
182+
help_msg = (
183+
"File with list of distributions (values matching ID= in /etc/os-release) "
184+
"that require sudo"
185+
)
186+
settings.register_option(
187+
section="sysinfo",
188+
key="sudo_distros",
189+
key_type=prepend_base_path,
190+
default=default,
191+
help_msg=help_msg,
192+
)
193+
172194

173195
class SysInfoJob(JobPreTests, JobPostTests):
174196

avocado/utils/sysinfo.py

Lines changed: 76 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,15 +14,19 @@
1414
# John Admanski <[email protected]>
1515

1616
import json
17+
import logging
1718
import os
19+
import platform
1820
import shlex
1921
import subprocess
2022
import tempfile
2123
from abc import ABC, abstractmethod
2224

2325
from avocado.utils import astring, process
26+
from avocado.utils.process import can_sudo
2427

2528
DATA_SIZE = 200000
29+
log = logging.getLogger("avocado.sysinfo")
2630

2731

2832
class CollectibleException(Exception):
@@ -132,12 +136,23 @@ class Command(Collectible):
132136
:param locale: Force LANG for sysinfo collection
133137
"""
134138

135-
def __init__(self, cmd, timeout=-1, locale="C"):
139+
def __init__(
140+
self, cmd, timeout=-1, locale="C", sudo_commands=None, sudo_distros=None
141+
): # pylint: disable=R0913
136142
super().__init__(cmd)
137143
self._name = self.log_path
138144
self.cmd = cmd
139145
self.timeout = timeout
140146
self.locale = locale
147+
self.sudo_commands = sudo_commands
148+
self.sudo_distros = sudo_distros
149+
self._sysinfo_cmd = None
150+
151+
@property
152+
def _sudoer(self):
153+
if self._sysinfo_cmd is None and self.sudo_commands and self.sudo_distros:
154+
self._sysinfo_cmd = SysinfoCommand(self.sudo_commands, self.sudo_distros)
155+
return self._sysinfo_cmd
141156

142157
def __repr__(self):
143158
r = "Command(%r, %r)"
@@ -168,6 +183,13 @@ def collect(self):
168183
# but the avocado.utils.process APIs define no timeouts as "None"
169184
if int(self.timeout) <= 0:
170185
self.timeout = None
186+
187+
# Determine whether to run with sudo (do not mutate the command string)
188+
sudo_flag = False
189+
if self._sudoer:
190+
sudo_flag = self._sudoer.use_sudo() and self._sudoer.is_sudo_cmd(self.cmd)
191+
log.info("Executing Command%s: %s", " (sudo)" if sudo_flag else "", self.cmd)
192+
171193
try:
172194
result = process.run(
173195
self.cmd,
@@ -176,6 +198,7 @@ def collect(self):
176198
ignore_status=True,
177199
shell=True,
178200
env=env,
201+
sudo=sudo_flag,
179202
)
180203
yield result.stdout
181204
except FileNotFoundError as exc_fnf:
@@ -394,3 +417,55 @@ def collect(self):
394417
raise CollectibleException(
395418
f"Not logging {self.path} " f"(lack of permissions)"
396419
) from exc
420+
421+
422+
class SysinfoCommand:
423+
def __init__(self, sudo_commands=None, sudo_distros=None):
424+
self.sudo_cmds = sudo_commands if sudo_commands else set()
425+
self.sudo_distros = sudo_distros if sudo_distros else set()
426+
self.sudo_available = False
427+
# Only attempt sudo capability detection on Linux, where it is relevant.
428+
if platform.system().lower() == "linux":
429+
self.sudo_available = can_sudo()
430+
431+
def use_sudo(self):
432+
"""
433+
Determine if 'sudo' should be used based on the system type.
434+
435+
Returns:
436+
bool: True if 'sudo' should be used, False otherwise.
437+
"""
438+
if not self.sudo_available:
439+
return False
440+
system_name = platform.system().lower()
441+
if system_name == "linux":
442+
if hasattr(os, "geteuid") and not os.geteuid():
443+
return False
444+
try:
445+
with open("/etc/os-release", encoding="utf-8") as f:
446+
for line in f:
447+
if line.startswith("ID="):
448+
os_id = line.strip().split("=")[1].strip('"')
449+
return os_id.lower() in self.sudo_distros
450+
except FileNotFoundError:
451+
log.debug("/etc/os-release not found.")
452+
return False
453+
return False
454+
return False
455+
456+
def is_sudo_cmd(self, cmd):
457+
"""
458+
Determine if 'sudo' should be used for a specific command based on the configuration.
459+
460+
Args:
461+
cmd (str): The command to check.
462+
463+
Returns:
464+
bool: True if 'sudo' should be used, False otherwise.
465+
"""
466+
try:
467+
first = shlex.split(cmd or "")[0]
468+
except (ValueError, IndexError):
469+
return False
470+
base = os.path.basename(first).lower()
471+
return base in self.sudo_cmds

python-avocado.spec

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -290,6 +290,7 @@ Common files (such as configuration) for the Avocado Testing Framework.
290290
%dir %{_datarootdir}/avocado/schemas
291291
%{_datarootdir}/avocado/schemas/*
292292
%config(noreplace)%{_sysconfdir}/avocado/sysinfo/commands
293+
%config(noreplace)%{_sysconfdir}/avocado/sysinfo.conf
293294
%config(noreplace)%{_sysconfdir}/avocado/sysinfo/files
294295
%config(noreplace)%{_sysconfdir}/avocado/sysinfo/profilers
295296
%config(noreplace)%{_sysconfdir}/avocado/scripts/job/pre.d/README

0 commit comments

Comments
 (0)