Skip to content

Commit e6a3d8b

Browse files
author
YongxueHong
authored
Merge pull request #4312 from YongxueHong/LIBVIRTAT-22270
libvirt_vm: expand reboot methods and improve state detection
2 parents 2dfb027 + 1e87c50 commit e6a3d8b

1 file changed

Lines changed: 93 additions & 12 deletions

File tree

virttest/libvirt_vm.py

Lines changed: 93 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,9 @@
1515
import shutil
1616
import string
1717
import tempfile
18+
import threading
1819
import time
20+
from functools import partial
1921

2022
import aexpect
2123
from aexpect import remote
@@ -39,6 +41,7 @@
3941
virt_vm,
4042
xml_utils,
4143
)
44+
from virttest.utils_test import libvirt
4245

4346
# Using as lower capital is not the best way to do, but this is just a
4447
# workaround to avoid changing the entire file.
@@ -2726,37 +2729,115 @@ def deactivate_nic(self, nic_index_or_name):
27262729

27272730
@error_context.context_aware
27282731
def reboot(
2729-
self, session=None, method="shell", nic_index=0, timeout=240, serial=False
2732+
self,
2733+
session=None,
2734+
method="shell",
2735+
nic_index=0,
2736+
timeout=virt_vm.BaseVM.REBOOT_TIMEOUT,
2737+
serial=False,
27302738
):
27312739
"""
27322740
Reboot the VM and wait for it to come back up by trying to log in until
27332741
timeout expires.
27342742
27352743
:param session: A shell session object or None.
2736-
:param method: Reboot method. Can be "shell" (send a shell reboot
2737-
command).
2744+
:param method: Reboot method. Can be "shell" (send a shell reboot
2745+
command) or "libvirt_reset" (use virsh reset).
27382746
:param nic_index: Index of NIC to access in the VM, when logging in
27392747
after rebooting.
27402748
:param timeout: Time to wait for login to succeed (after rebooting).
2741-
:param serial: Just use to unify api in virt_vm module.
2749+
:param serial: Whether to use serial console for login.
27422750
:return: A new shell session object.
2743-
"""
2751+
:raises VMRebootError: If reboot fails or guest doesn't respond.
2752+
"""
2753+
2754+
def _check_serial_console_down(console, timeout):
2755+
"""Check if serial console indicates system is going down."""
2756+
reboot_patterns = [
2757+
# Linux reboot patterns
2758+
r".*[Rr]ebooting.*",
2759+
r".*[Rr]estarting system.*",
2760+
r".*[Mm]achine restart.*",
2761+
r".*Linux version.*",
2762+
# Windows reboot patterns
2763+
r".*Microsoft Windows \[Version.*",
2764+
r".*Microsoft Corporation\. All rights reserved\.*",
2765+
]
2766+
try:
2767+
if console.read_until_any_line_matches(
2768+
reboot_patterns, timeout=timeout
2769+
):
2770+
LOG.debug("Reboot pattern detected in serial console")
2771+
return True
2772+
LOG.debug(f"No reboot patterns detected within timeout {timeout} sec")
2773+
return False
2774+
except Exception as e:
2775+
LOG.debug(f"Unexpected error during serial console reboot check: {e}")
2776+
return False
2777+
2778+
def _execute_shell_reboot(session):
2779+
"""Execute reboot command via shell session."""
2780+
reboot_cmd = self.params.get("reboot_command")
2781+
session.cmd(reboot_cmd, ignore_all_errors=True)
2782+
2783+
def _check_system_event_reboot(timeout):
2784+
"""Check if system is reboot via libvirt events."""
2785+
try:
2786+
result = virsh.event(
2787+
domain=self.name, event="reboot", event_timeout=timeout
2788+
)
2789+
libvirt.check_exit_status(result)
2790+
LOG.debug("Detected libvirt reboot event")
2791+
return True
2792+
except Exception as e:
2793+
LOG.debug(f"Libvirt reboot event check failed: {e}")
2794+
return False
2795+
2796+
def _execute_libvirt_reset():
2797+
"""Execute libvirt reset via virsh."""
2798+
reset_thread = threading.Thread(
2799+
target=virsh.reset, args=(self.name,), name=f"reset-{self.name}"
2800+
)
2801+
reset_thread.daemon = True
2802+
reset_thread.start()
2803+
27442804
error_context.base_context("rebooting '%s'" % self.name, LOG.info)
27452805
error_context.context("before reboot")
2746-
session = session or self.login(timeout=timeout)
27472806
error_context.context()
27482807

2808+
serial_console = None
27492809
if method == "shell":
2750-
session.sendline(self.params.get("reboot_command"))
2810+
if not session:
2811+
if not serial:
2812+
session = self.wait_for_login(nic_index=nic_index, timeout=timeout)
2813+
else:
2814+
session = self.wait_for_serial_login(timeout=timeout)
2815+
2816+
if isinstance(session, remote.RemoteSession):
2817+
serial_console = self.wait_for_serial_login(timeout=timeout)
2818+
2819+
_reboot = partial(_execute_shell_reboot, session)
2820+
_check_go_down = partial(
2821+
_check_serial_console_down,
2822+
serial_console if serial_console else session,
2823+
30,
2824+
)
2825+
elif method == "libvirt_reset":
2826+
_reboot = partial(_execute_libvirt_reset)
2827+
_check_go_down = partial(_check_system_event_reboot, 30)
27512828
else:
27522829
raise virt_vm.VMRebootError("Unknown reboot method: %s" % method)
27532830

27542831
error_context.context("waiting for guest to go down", LOG.info)
2755-
if not utils_misc.wait_for(
2756-
lambda: not session.is_responsive(timeout=30), 120, 0, 1
2757-
):
2758-
raise virt_vm.VMRebootError("Guest refuses to go down")
2759-
session.close()
2832+
try:
2833+
_reboot()
2834+
if not utils_misc.wait_for(_check_go_down, timeout=timeout):
2835+
raise virt_vm.VMRebootError("Guest refuses to go down")
2836+
finally:
2837+
if session:
2838+
session.close()
2839+
if serial_console:
2840+
serial_console.close()
27602841

27612842
error_context.context("logging in after reboot", LOG.info)
27622843
if serial:

0 commit comments

Comments
 (0)