Skip to content
Open
Show file tree
Hide file tree
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
3 changes: 3 additions & 0 deletions provider/v2v/defaults.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@
The first matching ESX profile is applied by utils_v2v.set_defaults.
"""

FIRSTBOOT_TIMEOUT_LINUX = 600
FIRSTBOOT_TIMEOUT_WINDOWS = 2400

esx_70_vmname_regex = r".*esx7\.0.*"
esx_80_vmname_regex = r".*esx8\.0.*"
esx_90_vmname_regex = r".*esx9\.0.*"
Expand Down
46 changes: 43 additions & 3 deletions provider/v2v/utils_v2v.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import glob
import hashlib
import logging
import math
import os
import pwd
import random
Expand Down Expand Up @@ -82,6 +83,16 @@ def set_defaults(test, params, _env):
if V2V_EXEC is None:
test.error("Missing command: virt-v2v")

params.setdefault("os_type", "linux")
if not params["os_type"] in ["windows", "linux"]:
test.error("Unsupported os_type=%s" % params["os_type"])

if params["os_type"] == "windows":
default_firstboot_timeout = defaults.FIRSTBOOT_TIMEOUT_WINDOWS
else:
default_firstboot_timeout = defaults.FIRSTBOOT_TIMEOUT_LINUX
params.setdefault("v2v_firstboot_timeout", default_firstboot_timeout)
Comment thread
crobinso marked this conversation as resolved.
Resolved

_set_default_vmware(test, params)


Expand Down Expand Up @@ -545,6 +556,7 @@ def __init__(self, test, params, env):
self.windows_root = params.get("windows_root", r"C:\WINDOWS")
# Need create session after create the instance
self.session = None
self._firstboot_deadline = None

if self.name is None:
LOG.error("vm name not exist")
Expand All @@ -558,12 +570,40 @@ def __init__(self, test, params, env):
else:
raise ValueError("Doesn't support %s target now" % self.target)

def create_session(self, timeout=None):
if timeout is None:
timeout = int(self.params.get('v2v_login_timeout', 900))
def remaining_firstboot_timeout(self):
"""Start the firstboot budget on first use and return whole seconds left."""
now = time.time()
if self._firstboot_deadline is None:
timeout = int(self.params['v2v_firstboot_timeout'])
self._firstboot_deadline = now + timeout
LOG.info("Starting firstboot timeout of %ds", timeout)
remaining = self._firstboot_deadline - now
if remaining < 0:
raise exceptions.TestFail("Firstboot timeout expired")
return math.ceil(remaining)

def firstboot_wait_for(self, func, description, step=5):
"""Poll within the shared firstboot budget or fail on timeout.

Return the callback's truthy result. Callback exceptions propagate so
shell failures can be handled by the caller's reconnect logic.
"""
timeout = self.remaining_firstboot_timeout()
LOG.info("Waiting up to %ds for %s", timeout, description)
result = utils_misc.wait_for(func, timeout, step=step)
if not result:
raise exceptions.TestFail(
"Firstboot timeout expired waiting for %s" % description)
# The callback may have finished after the deadline, or the rounded
# timeout may have allowed an extra fraction of a second.
self.remaining_firstboot_timeout()
return result

def create_session(self):
if self.session:
LOG.debug("vm session %s exists", self.session)
return
timeout = self.remaining_firstboot_timeout()
self.session = self.vm.wait_for_login(
nic_index=self.nic_index,
timeout=timeout,
Expand Down
148 changes: 82 additions & 66 deletions provider/v2v/v2v_vmcheck_helper.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import glob
import json
import logging
import os
import re
Expand All @@ -25,9 +26,6 @@

RETRY_TIMES = 10

# max time we expect a single windows boot+configure cycle to take.
WIN_SINGLE_BOOT_MAX_TIME = 300


def detect_source_efi(source_xml):
"""
Expand Down Expand Up @@ -644,29 +642,6 @@ def _reconnect_session(self):
self.checker.session = None
self.checker.create_session()

def _wait_for_firstboot_complete(self):
"""
Wait for the firstboot 'complete' marker file to appear.

Returns True if firstboot completed, False on timeout.
"""
total_timeout = WIN_SINGLE_BOOT_MAX_TIME
step = 5
complete_path = r'"C:\Program Files\Guestfs\Firstboot\complete"'
cmd = 'type %s' % complete_path
deadline = time.time() + total_timeout

LOG.info("Waiting up to %ds for %s", total_timeout, complete_path)
while time.time() < deadline:
status, _ = self.checker.run_cmd(cmd)
if status == 0:
LOG.info("Found: %s", complete_path)
return True
time.sleep(step)

LOG.warning("Not found after %ds: %s", total_timeout, complete_path)
return False

def _check_firstboot_failures(self):
"""
Check if any firstboot scripts failed and dump the log.
Expand All @@ -678,7 +653,8 @@ def _check_firstboot_failures(self):
"""
log_path = r'"C:\Program Files\Guestfs\Firstboot\log.txt"'
try:
status, log_content = self.checker.run_cmd('type %s' % log_path)
status, log_content = self.checker.run_cmd(
'type %s' % log_path, debug=False)
except Exception as e:
LOG.warning("Could not read firstboot log.txt: %s", e)
return
Expand Down Expand Up @@ -728,20 +704,6 @@ def _check_firstboot_failures(self):
LOG.info("All firstboot scripts completed successfully")

def _perform_windows_checks(self):
# in virt-v2v, qemu-guest-agent is installed after virtio-win
# drivers. so if qemu-guest-agent shows up we know we are
# passed the point of driver install.
LOG.info("Checking qemu-guest-agent service")
res = utils_misc.wait_for(
lambda: re.search(
'running',
self.checker.get_service_info('qemu-ga'),
re.I),
WIN_SINGLE_BOOT_MAX_TIME,
step=5)
if not res:
self.log_err("qemu-guest-agent service is not running")

# Check boottype of the guest
self.check_vm_boottype()

Expand Down Expand Up @@ -780,46 +742,100 @@ def _perform_windows_checks(self):
# Check remaining VM XML properties
self.check_vm_xml()

def check_windows_vm(self):
"""
Wait for firstboot, then run Windows postboot checks. These might be
interrupted by a reboot, in which case we retry the checks.
"""
# create_session() returns after getting to the network `C:\` prompt.
# it seems to tolerate windows VMs multiple reboots
self.checker.create_session()
def _wait_for_guest_agent(self):
"""Wait for the guest agent to reply to guest-ping."""
def agent_ready():
result = virsh.qemu_agent_command(
self.vm_name, '{"execute":"guest-ping"}',
uri=self.virsh_session.get("uri"),
ignore_status=True, debug=False)
return (result.exit_status == 0 and
'return' in json.loads(result.stdout_text))

self.checker.firstboot_wait_for(
agent_ready, "guest-agent reply to guest-ping", step=10)
LOG.info("Guest-agent replied to guest-ping, continuing")

reboot_count = self._reboot_watcher.reboot_count
def _wait_for_firstboot_complete(self):
"""Wait for the firstboot completion marker through the guest agent."""
complete_path = r'C:\Program Files\Guestfs\Firstboot\complete'
command = json.dumps({
'execute': 'guest-file-open',
'arguments': {'path': complete_path, 'mode': 'r'}})

def marker_exists():
result = virsh.qemu_agent_command(
self.vm_name, command,
uri=self.virsh_session.get("uri"),
ignore_status=True, debug=False)
if result.exit_status != 0:
return False
reply = json.loads(result.stdout_text)
if 'return' not in reply:
return False

def _did_reboot():
# Shell may have been closed but VM hasn't officially reported
# it is rebooting yet.
# Wait 60 seconds, checking every second.
return utils_misc.wait_for(
lambda: self._reboot_watcher.reboot_count != reboot_count,
60, step=1)
# The marker is empty; opening it is enough. Close the returned
# handle before reporting completion, including handle 0.
close_cmd = json.dumps({
'execute': 'guest-file-close',
'arguments': {'handle': reply['return']}})
result = virsh.qemu_agent_command(
self.vm_name, close_cmd,
uri=self.virsh_session.get("uri"),
ignore_status=True, debug=False)
return (result.exit_status == 0 and
'return' in json.loads(result.stdout_text))

self.checker.firstboot_wait_for(
marker_exists, "firstboot completion marker %s" % complete_path,
step=10)
LOG.info("Firstboot completion marker found: %s, continuing",
complete_path)

while True:
def check_windows_vm(self):
"""
Wait for firstboot, then run Windows postboot checks.
Reconnect and retry shell failures within the firstboot timeout.
"""
# As of Nov 2026, virt-v2v's firstboot scripts run in this order
#
# + Install virtio drivers
# + Configure networking
# + Wait for PnP to settle.
# + Install QEMU guest agent.
# + Install balloon server.
# + Uninstall Xen PV, Parallels tools, then VMware Tools.
# + Bring additional virtio disks online.
#
firstboot_complete = False

while self.checker.remaining_firstboot_timeout():
error_count = len(self.errors)
try:
if utils_v2v.supports_firstboot_sentinel():
if not self._wait_for_firstboot_complete():
raise exceptions.TestFail(
"Firstboot did not complete within timeout")
self._wait_for_firstboot_complete()
firstboot_complete = True
else:
self._wait_for_guest_agent()

# this returns after getting to the network `C:\` prompt.
# it seems to tolerate windows VMs multiple reboots
self._reconnect_session()

if firstboot_complete:
self._check_firstboot_failures()

self._perform_windows_checks()
return
except ShellError:
if not _did_reboot():
LOG.info("windows checks threw error not caused by reboot. raising")
if firstboot_complete:
LOG.info("firstboot completion file found, but we still hit Shell error. raising")
raise

LOG.info("Guest rebooted during Windows validation, retrying",
LOG.info("Shell error during Windows validation, reconnecting and retrying",
exc_info=True)
del self.errors[error_count:]
self._reconnect_session()
reboot_count = self._reboot_watcher.reboot_count
time.sleep(10)

def check_genid(self):
"""
Expand Down
1 change: 0 additions & 1 deletion v2v/tests/cfg/convert_from_file.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
start_vm = 'no'
take_regular_screendumps = no
v2v_timeout = '7200'
v2v_login_timeout = 900
v2v_debug = on

# Regular kvm guest parameters
Expand Down
1 change: 0 additions & 1 deletion v2v/tests/cfg/convert_vm_to_libvirt.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
remote_preprocess = "no"
only dest_libvirt
v2v_debug = on
v2v_login_timeout = 900
enable_vsock_check = yes

username = "root"
Expand Down
1 change: 0 additions & 1 deletion v2v/tests/cfg/function_test_esx.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
start_vm = 'no'
take_regular_screendumps = no
v2v_timeout = '7200'
v2v_login_timeout = 900
default_output_format = 'qcow2'
vpx_passwd_file = "/tmp/v2v_vpx_passwd"
v2v_debug = on
Expand Down
1 change: 0 additions & 1 deletion v2v/tests/cfg/specific_kvm.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
start_vm = 'no'
take_regular_screendumps = no
v2v_timeout = '7200'
v2v_login_timeout = 900
os_type = 'linux'
username = 'root'
password = GENERAL_GUEST_PASSWORD
Expand Down
1 change: 0 additions & 1 deletion v2v/tests/cfg/v2v_options.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@
password = GENERAL_GUEST_PASSWORD
# v2v cmd running time
v2v_timeout = 10800
v2v_login_timeout = 900
vms = ''
variants:
- it_vddk:
Expand Down
Loading