Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
105 changes: 93 additions & 12 deletions virttest/libvirt_vm.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,9 @@
import shutil
import string
import tempfile
import threading
import time
from functools import partial

import aexpect
from aexpect import remote
Expand All @@ -38,6 +40,7 @@
virt_vm,
xml_utils,
)
from virttest.utils_test import libvirt

Comment thread
coderabbitai[bot] marked this conversation as resolved.
# Using as lower capital is not the best way to do, but this is just a
# workaround to avoid changing the entire file.
Expand Down Expand Up @@ -2716,37 +2719,115 @@ def deactivate_nic(self, nic_index_or_name):

@error_context.context_aware
def reboot(
self, session=None, method="shell", nic_index=0, timeout=240, serial=False
self,
session=None,
method="shell",
nic_index=0,
timeout=virt_vm.BaseVM.REBOOT_TIMEOUT,
serial=False,
):
"""
Comment thread
YongxueHong marked this conversation as resolved.
Reboot the VM and wait for it to come back up by trying to log in until
timeout expires.

:param session: A shell session object or None.
:param method: Reboot method. Can be "shell" (send a shell reboot
command).
:param method: Reboot method. Can be "shell" (send a shell reboot
command) or "libvirt_reset" (use virsh reset).
:param nic_index: Index of NIC to access in the VM, when logging in
after rebooting.
:param timeout: Time to wait for login to succeed (after rebooting).
:param serial: Just use to unify api in virt_vm module.
:param serial: Whether to use serial console for login.
:return: A new shell session object.
"""
:raises VMRebootError: If reboot fails or guest doesn't respond.
"""

def _check_serial_console_down(console, timeout):
"""Check if serial console indicates system is going down."""
reboot_patterns = [
# Linux reboot patterns
r".*[Rr]ebooting.*",
Comment thread
YongxueHong marked this conversation as resolved.
r".*[Rr]estarting system.*",
r".*[Mm]achine restart.*",
r".*Linux version.*",
# Windows reboot patterns
r".*Microsoft Windows \[Version.*",
r".*Microsoft Corporation\. All rights reserved\.*",
]
try:
if console.read_until_any_line_matches(
reboot_patterns, timeout=timeout
):
LOG.debug("Reboot pattern detected in serial console")
return True
LOG.debug(f"No reboot patterns detected within timeout {timeout} sec")
return False
except Exception as e:
LOG.debug(f"Unexpected error during serial console reboot check: {e}")
return False
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Comment thread
YongxueHong marked this conversation as resolved.

def _execute_shell_reboot(session):
"""Execute reboot command via shell session."""
reboot_cmd = self.params.get("reboot_command")
session.cmd(reboot_cmd, ignore_all_errors=True)

def _check_system_event_reboot(timeout):
"""Check if system is reboot via libvirt events."""
try:
result = virsh.event(
domain=self.name, event="reboot", event_timeout=timeout
)
libvirt.check_exit_status(result)
LOG.debug("Detected libvirt reboot event")
return True
except Exception as e:
LOG.debug(f"Libvirt reboot event check failed: {e}")
return False
Comment thread
YongxueHong marked this conversation as resolved.

def _execute_libvirt_reset():
"""Execute libvirt reset via virsh."""
reset_thread = threading.Thread(
target=virsh.reset, args=(self.name,), name=f"reset-{self.name}"
)
Comment thread
YongxueHong marked this conversation as resolved.
reset_thread.daemon = True
reset_thread.start()

error_context.base_context("rebooting '%s'" % self.name, LOG.info)
error_context.context("before reboot")
session = session or self.login(timeout=timeout)
error_context.context()

serial_console = None
if method == "shell":
session.sendline(self.params.get("reboot_command"))
if not session:
if not serial:
session = self.wait_for_login(nic_index=nic_index, timeout=timeout)
else:
session = self.wait_for_serial_login(timeout=timeout)

if isinstance(session, remote.RemoteSession):
serial_console = self.wait_for_serial_login(timeout=timeout)

_reboot = partial(_execute_shell_reboot, session)
_check_go_down = partial(
_check_serial_console_down,
serial_console if serial_console else session,
30,
)
Comment thread
YongxueHong marked this conversation as resolved.
elif method == "libvirt_reset":
_reboot = partial(_execute_libvirt_reset)
_check_go_down = partial(_check_system_event_reboot, 30)
else:
raise virt_vm.VMRebootError("Unknown reboot method: %s" % method)

error_context.context("waiting for guest to go down", LOG.info)
if not utils_misc.wait_for(
lambda: not session.is_responsive(timeout=30), 120, 0, 1
):
raise virt_vm.VMRebootError("Guest refuses to go down")
session.close()
try:
_reboot()
if not utils_misc.wait_for(_check_go_down, timeout=timeout):
Comment thread
YongxueHong marked this conversation as resolved.
raise virt_vm.VMRebootError("Guest refuses to go down")
finally:
if session:
session.close()
if serial_console:
serial_console.close()

error_context.context("logging in after reboot", LOG.info)
if serial:
Expand Down
Loading