Skip to content

Commit 54e17e5

Browse files
author
xianglongfei
committed
Fixed the issue in sysinfo where the dmidecode and fdisk - l commands were executed.
1 parent 74b7379 commit 54e17e5

File tree

4 files changed

+162
-5
lines changed

4 files changed

+162
-5
lines changed

avocado/core/sysinfo.py

Lines changed: 63 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
import logging
1616
import os
1717
import time
18+
import configparser
1819

1920
from avocado.core import output
2021
from avocado.core.settings import settings
@@ -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+
config = configparser.ConfigParser()
96+
config.read(raw_value)
97+
return config.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: 71 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,15 @@
1818
import shlex
1919
import subprocess
2020
import tempfile
21-
from abc import ABC, abstractmethod
21+
import platform
22+
import logging
2223

24+
from abc import ABC, abstractmethod
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,16 @@ 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
141149

142150
def __repr__(self):
143151
r = "Command(%r, %r)"
@@ -168,6 +176,14 @@ def collect(self):
168176
# but the avocado.utils.process APIs define no timeouts as "None"
169177
if int(self.timeout) <= 0:
170178
self.timeout = None
179+
180+
# Determine whether to run with sudo (do not mutate the command string)
181+
sudo_flag = False
182+
if self.sudo_commands and self.sudo_distros:
183+
sysinfo_cmd = SysinfoCommand(self.sudo_commands, self.sudo_distros)
184+
sudo_flag = sysinfo_cmd.use_sudo() and sysinfo_cmd.is_sudo_cmd(self.cmd)
185+
log.info("Executing Command%s: %s", " (sudo)" if sudo_flag else "", self.cmd)
186+
171187
try:
172188
result = process.run(
173189
self.cmd,
@@ -176,6 +192,7 @@ def collect(self):
176192
ignore_status=True,
177193
shell=True,
178194
env=env,
195+
sudo=sudo_flag,
179196
)
180197
yield result.stdout
181198
except FileNotFoundError as exc_fnf:
@@ -394,3 +411,55 @@ def collect(self):
394411
raise CollectibleException(
395412
f"Not logging {self.path} " f"(lack of permissions)"
396413
) from exc
414+
415+
416+
class SysinfoCommand:
417+
def __init__(self, sudo_commands=None, sudo_distros=None):
418+
self.sudo_cmds = sudo_commands if sudo_commands else set()
419+
self.sudo_distros = sudo_distros if sudo_distros else set()
420+
self.sudo_available = False
421+
# Only attempt sudo capability detection on Linux, where it is relevant.
422+
if platform.system().lower() == "linux":
423+
self.sudo_available = can_sudo()
424+
425+
def use_sudo(self):
426+
"""
427+
Determine if 'sudo' should be used based on the system type.
428+
429+
Returns:
430+
bool: True if 'sudo' should be used, False otherwise.
431+
"""
432+
if not self.sudo_available:
433+
return False
434+
system_name = platform.system().lower()
435+
if system_name == "linux":
436+
if hasattr(os, "geteuid") and not os.geteuid():
437+
return False
438+
try:
439+
with open("/etc/os-release", encoding="utf-8") as f:
440+
for line in f:
441+
if line.startswith("ID="):
442+
os_id = line.strip().split("=")[1].strip('"')
443+
return os_id.lower() in self.sudo_distros
444+
except FileNotFoundError:
445+
log.debug("/etc/os-release not found.")
446+
return False
447+
return False
448+
return False
449+
450+
def is_sudo_cmd(self, cmd):
451+
"""
452+
Determine if 'sudo' should be used for a specific command based on the configuration.
453+
454+
Args:
455+
cmd (str): The command to check.
456+
457+
Returns:
458+
bool: True if 'sudo' should be used, False otherwise.
459+
"""
460+
try:
461+
first = shlex.split(cmd or "")[0]
462+
except (ValueError, IndexError):
463+
return False
464+
base = os.path.basename(first).lower()
465+
return base in self.sudo_cmds

0 commit comments

Comments
 (0)