Skip to content

Commit 59ff59c

Browse files
committed
squash:new implementation
1 parent c211365 commit 59ff59c

File tree

8 files changed

+77
-55
lines changed

8 files changed

+77
-55
lines changed

tmt/steps/finish/__init__.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -226,7 +226,7 @@ def go(self, force: bool = False) -> None:
226226

227227
# Stop and remove provisioned guests
228228
for guest in self.plan.provision.guests():
229-
guest.fetch_logs(lognames=guest.lognames)
229+
guest.fetch_logs(guest_logs=guest.guest_logs)
230230
guest.stop()
231231
guest.remove()
232232

tmt/steps/provision/__init__.py

+52-13
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
import re
99
import secrets
1010
import shlex
11+
import shutil
1112
import signal as _signal
1213
import string
1314
import subprocess
@@ -56,6 +57,7 @@
5657
ProvisionError,
5758
ShellScript,
5859
configure_constant,
60+
create_directory,
5961
effective_workdir_root,
6062
)
6163

@@ -952,6 +954,19 @@ def show(
952954
logger.info(key_to_option(key).replace('-', ' '), printable_value, color='green')
953955

954956

957+
@container
958+
class GuestLog:
959+
name: str
960+
961+
def fetch(self) -> Optional[str]:
962+
"""
963+
Fetch and return content of a log.
964+
965+
:returns: content of the log, or ``None`` if the log cannot be retrieved.
966+
"""
967+
raise NotImplementedError
968+
969+
955970
class Guest(tmt.utils.Common):
956971
"""
957972
Guest provisioned for test execution
@@ -993,6 +1008,8 @@ def get_data_class(cls) -> type[GuestData]:
9931008
#: Guest topology hostname or IP address for guest/guest communication.
9941009
topology_address: Optional[str] = None
9951010

1011+
guest_logs: list[GuestLog] = []
1012+
9961013
become: bool
9971014

9981015
hardware: Optional[tmt.hardware.Hardware]
@@ -1090,12 +1107,6 @@ def scripts_path(self) -> Path:
10901107
else tmt.steps.execute.DEFAULT_SCRIPTS_DEST_DIR
10911108
)
10921109

1093-
@property
1094-
def lognames(self) -> list[str]:
1095-
"""Return name list of logs the guest could provide."""
1096-
1097-
return []
1098-
10991110
@classmethod
11001111
def options(cls, how: Optional[str] = None) -> list[tmt.options.ClickOptionDecoratorType]:
11011112
"""
@@ -1681,7 +1692,7 @@ def store_log(self, path: Path, content: str, logname: Optional[str] = None) ->
16811692
raise tmt.utils.GeneralError('Log path is a directory but log name is not defined.')
16821693

16831694
def fetch_logs(
1684-
self, dirpath: Optional[Path] = None, lognames: Optional[list[str]] = None
1695+
self, dirpath: Optional[Path] = None, guest_logs: Optional[list[GuestLog]] = None
16851696
) -> None:
16861697
"""
16871698
Get log content and save it to a directory.
@@ -1691,14 +1702,20 @@ def fetch_logs(
16911702
:param lognames: name list of logs need to be handled. If not set, all guest logs
16921703
would be collected, as reported by :py:attr:`lognames`.
16931704
"""
1694-
lognames = lognames or self.lognames
1695-
dirpath = dirpath or self.workdir or Path.cwd()
1696-
for logname in lognames:
1697-
content = self.acquire_log(logname)
1705+
1706+
guest_logs = guest_logs or self.guest_logs or []
1707+
1708+
dirpath = dirpath or (self.workdir / 'logs' if self.workdir else None) or Path.cwd()
1709+
if self.workdir and dirpath == self.workdir / 'logs':
1710+
create_directory(
1711+
path=self.workdir / 'logs', name='logs workdir', quiet=True, logger=self._logger
1712+
)
1713+
for log in guest_logs:
1714+
content = log.fetch()
16981715
if content:
1699-
self.store_log(dirpath, content, logname)
1716+
self.store_log(dirpath, content, log.name)
17001717
else:
1701-
self.store_log(dirpath, '', logname)
1718+
self.store_log(dirpath, '', log.name)
17021719

17031720

17041721
@container
@@ -2696,6 +2713,28 @@ def show(self, keys: Optional[list[str]] = None) -> None:
26962713
if hardware:
26972714
echo(tmt.utils.format('hardware', tmt.utils.dict_to_yaml(hardware.to_spec())))
26982715

2716+
def prune(self, logger: tmt.log.Logger) -> None:
2717+
"""Do not prune logs"""
2718+
if self.workdir is None:
2719+
return
2720+
2721+
logs_dir = self.workdir / 'logs'
2722+
if logs_dir.exists():
2723+
for member in self.workdir.iterdir():
2724+
if member.name == "logs":
2725+
logger.debug(f"Preserve '{member.relative_to(self.workdir)}'.", level=3)
2726+
continue
2727+
logger.debug(f"Remove '{member}'.", level=3)
2728+
try:
2729+
if member.is_file() or member.is_symlink():
2730+
member.unlink()
2731+
else:
2732+
shutil.rmtree(member)
2733+
except OSError as error:
2734+
logger.warning(f"Unable to remove '{member}': {error}")
2735+
else:
2736+
super().prune(logger)
2737+
26992738

27002739
@container
27012740
class ProvisionTask(tmt.queue.GuestlessTask[None]):

tmt/steps/provision/artemis.py

-5
Original file line numberDiff line numberDiff line change
@@ -493,11 +493,6 @@ def is_ready(self) -> bool:
493493
# return True if self.guest is not None
494494
return self.primary_address is not None
495495

496-
@property
497-
def lognames(self) -> list[str]:
498-
"""Return name list of logs the guest could provide."""
499-
return []
500-
501496
def _create(self) -> None:
502497
environment: dict[str, Any] = {
503498
'hw': {'arch': self.arch},

tmt/steps/provision/connect.py

-5
Original file line numberDiff line numberDiff line change
@@ -85,11 +85,6 @@ class GuestConnect(tmt.steps.provision.GuestSsh):
8585
soft_reboot: Optional[ShellScript]
8686
hard_reboot: Optional[ShellScript]
8787

88-
@property
89-
def lognames(self) -> list[str]:
90-
"""Return name list of logs the guest could provide."""
91-
return []
92-
9388
def reboot(
9489
self,
9590
hard: bool = False,

tmt/steps/provision/local.py

-16
Original file line numberDiff line numberDiff line change
@@ -32,11 +32,6 @@ def is_ready(self) -> bool:
3232

3333
return True
3434

35-
@property
36-
def lognames(self) -> list[str]:
37-
"""Return name list of logs the guest could provide."""
38-
return []
39-
4035
def _run_ansible(
4136
self,
4237
playbook: tmt.steps.provision.AnsibleApplicable,
@@ -185,17 +180,6 @@ def pull(
185180
Nothing to be done to pull workdir
186181
"""
187182

188-
def acquire_log(self, logname: str) -> Optional[str]:
189-
"""
190-
Fetch and return content of a log.
191-
192-
:param logname: name of the log.
193-
:returns: content of the log, or ``None`` if the log cannot be retrieved.
194-
"""
195-
if logname == 'dmesg':
196-
return self.execute(Command('dmesg')).stdout
197-
return None
198-
199183

200184
@tmt.steps.provides_method('local')
201185
class ProvisionLocal(tmt.steps.provision.ProvisionPlugin[ProvisionLocalData]):

tmt/steps/provision/mrack.py

+24-5
Original file line numberDiff line numberDiff line change
@@ -1186,11 +1186,6 @@ def is_ready(self) -> bool:
11861186
except mrack.errors.MrackError:
11871187
return False
11881188

1189-
@property
1190-
def lognames(self) -> list[str]:
1191-
"""Return name list of logs the guest could provide."""
1192-
return []
1193-
11941189
def _create(self, tmt_name: str) -> None:
11951190
"""
11961191
Create beaker job xml request and submit it to Beaker hub
@@ -1290,6 +1285,11 @@ def get_new_state() -> GuestInspectType:
12901285
raise ProvisionError('Failed to create, provisioning failed.')
12911286

12921287
if state == 'Reserved':
1288+
for key in response["logs"]:
1289+
self.guest_logs.append(
1290+
GuestLogBeaker(self, key.replace('.log', ''), response["logs"][key])
1291+
)
1292+
self.guest_logs.append(GuestLogBeaker(self, 'dmesg'))
12931293
return current
12941294

12951295
raise tmt.utils.WaitingIncompleteError
@@ -1463,3 +1463,22 @@ def guest(self) -> Optional[GuestBeaker]:
14631463
"""
14641464

14651465
return self._guest
1466+
1467+
1468+
@container
1469+
class GuestLogBeaker(tmt.steps.provision.GuestLog):
1470+
def __init__(self, guest: GuestBeaker, name: str, url: Optional[str] = None) -> None:
1471+
self.name = name
1472+
self.url = url
1473+
self.guest = guest
1474+
1475+
def fetch(self) -> Optional[str]:
1476+
"""
1477+
Fetch and return content of a log.
1478+
1479+
:returns: content of the log, or ``None`` if the log cannot be retrieved.
1480+
"""
1481+
1482+
if self.name == 'dmesg':
1483+
return self.guest.execute(Command('dmesg')).stdout
1484+
return tmt.utils.get_url_content(self.url) if self.url else None

tmt/steps/provision/podman.py

-5
Original file line numberDiff line numberDiff line change
@@ -128,11 +128,6 @@ def is_ready(self) -> bool:
128128
)
129129
return str(cmd_output.stdout).strip() == 'true'
130130

131-
@property
132-
def lognames(self) -> list[str]:
133-
"""Return name list of logs the guest could provide."""
134-
return []
135-
136131
def wake(self) -> None:
137132
"""
138133
Wake up the guest

tmt/steps/provision/testcloud.py

-5
Original file line numberDiff line numberDiff line change
@@ -715,11 +715,6 @@ def is_coreos(self) -> bool:
715715
# Is this a CoreOS?
716716
return bool(re.search('coreos|rhcos', self.image.lower()))
717717

718-
@property
719-
def lognames(self) -> list[str]:
720-
"""Return name list of logs the guest could provide."""
721-
return []
722-
723718
def _get_url(self, url: str, message: str) -> requests.Response:
724719
"""
725720
Get url, retry when fails, return response

0 commit comments

Comments
 (0)