Skip to content

Commit 8ef01ae

Browse files
Add QEMU guest agent installer support
Signed-off-by: Mihaela Balutoiu <mbalutoiu@cloudbasesolutions.com>
1 parent f243272 commit 8ef01ae

2 files changed

Lines changed: 165 additions & 0 deletions

File tree

coriolis/osmorphing/windows.py

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,18 @@
7171
REQUIRED_DETECTED_WINDOWS_OS_FIELDS = [
7272
"version_number", "edition_id", "installation_type", "product_name"]
7373

74+
QEMU_GUEST_AGENT_INSTALL_SCRIPT_FORMAT = (
75+
"msiexec.exe /i '%(agent_msi_path)s' /qn /passive "
76+
"/L*V '%(agent_msi_log_dir)s/qemu-guest-agent-msiexec.log'")
77+
78+
QEMU_GUEST_AGENT_INSTALL_FROM_DIR_SCRIPT_FORMAT = (
79+
"$dst = 'C:\\Program Files\\Qemu-ga'\n"
80+
"New-Item -ItemType Directory -Force -Path $dst | Out-Null\n"
81+
"Copy-Item '%(qemu_ga_dir)s\\x64\\*' -Destination $dst -Force\n"
82+
"Copy-Item '%(qemu_ga_dir)s\\mingw64\\*' -Destination $dst -Force\n"
83+
'& "$dst\\qemu-ga.exe" --service install\n'
84+
"Start-Service QEMU-GA")
85+
7486
VIRTIO_WIN_ISO_PATH = "c:\\virtio-win.iso"
7587

7688
INTERFACES_PATH_FORMAT = (
@@ -832,6 +844,66 @@ def _install_dism_drivers(self, driver_paths):
832844
finally:
833845
self._revoke_permissions(file_repo_path, "*%s" % sid)
834846

847+
def _setup_qemu_agent_installation_local_script(
848+
self, msi_source_path=None, msi_url=None,
849+
msi_name="qemu-ga-x86_64.msi", qemu_ga_source_dir=None):
850+
"""Stages the QEMU guest agent in the Cloudbase-Init directory and
851+
registers a first-boot script which installs it.
852+
853+
Two source modes are supported:
854+
* MSI: pass 'msi_source_path' or 'msi_url'. The MSI is staged
855+
and installed via msiexec on first boot.
856+
* Directory: pass 'qemu_ga_source_dir' pointing at a 'qemu-ga'
857+
directory (e.g. on the SUSE VMDP ISO) containing 'x64' and
858+
'mingw64' subdirs.
859+
"""
860+
861+
if not any([msi_source_path, msi_url, qemu_ga_source_dir]):
862+
raise exception.CoriolisException(
863+
"No QEMU guest agent source provided: one of "
864+
"'msi_source_path', 'msi_url' or 'qemu_ga_source_dir' "
865+
"must be set.")
866+
867+
cloudbaseinit_base_dir = self._get_cbslinit_base_dir()
868+
guest_cloudbase_init_base_dir = "C%s" % cloudbaseinit_base_dir[1:]
869+
870+
self._event_manager.progress_update(
871+
"Setting up guest local script for installing the "
872+
"QEMU guest agent on first boot")
873+
874+
if qemu_ga_source_dir:
875+
qemu_ga_exe_path = "%s\\x64\\qemu-ga.exe" % qemu_ga_source_dir
876+
if not self._conn.test_path(qemu_ga_exe_path):
877+
LOG.warning(
878+
"qemu-ga is not packaged in the configured source. "
879+
"Skipping qemu-ga installation.")
880+
return
881+
# Copy qemu-ga/ to the Cloudbase-Init directory on the migrated
882+
# disk:
883+
self._conn.exec_ps_command(
884+
"Copy-Item -Recurse -Force '%s' '%s'" % (
885+
qemu_ga_source_dir, cloudbaseinit_base_dir),
886+
ignore_stdout=True)
887+
local_script = QEMU_GUEST_AGENT_INSTALL_FROM_DIR_SCRIPT_FORMAT % {
888+
"qemu_ga_dir": "%s\\qemu-ga" % guest_cloudbase_init_base_dir}
889+
else:
890+
msi_dest_path = "%s\\%s" % (cloudbaseinit_base_dir, msi_name)
891+
if msi_url:
892+
utils.retry_on_error(sleep_seconds=5)(
893+
self._conn.download_file)(msi_url, msi_dest_path)
894+
else:
895+
self._conn.exec_ps_command(
896+
"Copy-Item '%s' -Destination '%s'" % (
897+
msi_source_path, msi_dest_path))
898+
local_script = QEMU_GUEST_AGENT_INSTALL_SCRIPT_FORMAT % {
899+
"agent_msi_path": "%s\\%s" % (
900+
guest_cloudbase_init_base_dir, msi_name),
901+
"agent_msi_log_dir": guest_cloudbase_init_base_dir}
902+
903+
self.register_firstboot_script(
904+
local_script, user_provided=False,
905+
script_filename="coriolis_qemu_agent_install.ps1")
906+
835907
def get_packages(self):
836908
return [], []
837909

coriolis/tests/osmorphing/test_windows.py

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1265,3 +1265,96 @@ def test_install_dism_drivers_revokes_on_failure(
12651265
# Permissions must be revoked even if driver installation fails:
12661266
mock_revoke_permissions.assert_called_once_with(
12671267
exp_repo_path, f"*{null_sid}")
1268+
1269+
@mock.patch.object(
1270+
windows.BaseWindowsMorphingTools, "register_firstboot_script")
1271+
def test_setup_qemu_agent_installation_local_script_from_url(
1272+
self, mock_register_firstboot_script):
1273+
fake_msi_url = "fake-qemu-ga-msi-url"
1274+
1275+
self.morphing_tools._setup_qemu_agent_installation_local_script(
1276+
msi_url=fake_msi_url)
1277+
1278+
exp_msi_dest_path = "C:\\Cloudbase-Init\\qemu-ga-x86_64.msi"
1279+
self.morphing_tools._conn.download_file.assert_called_once_with(
1280+
fake_msi_url, exp_msi_dest_path)
1281+
self.morphing_tools._conn.exec_ps_command.assert_not_called()
1282+
1283+
exp_script = windows.QEMU_GUEST_AGENT_INSTALL_SCRIPT_FORMAT % {
1284+
"agent_msi_path": exp_msi_dest_path,
1285+
"agent_msi_log_dir": "C:\\Cloudbase-Init"}
1286+
mock_register_firstboot_script.assert_called_once_with(
1287+
exp_script, user_provided=False,
1288+
script_filename="coriolis_qemu_agent_install.ps1")
1289+
1290+
@mock.patch.object(
1291+
windows.BaseWindowsMorphingTools, "register_firstboot_script")
1292+
def test_setup_qemu_agent_installation_local_script_from_path(
1293+
self, mock_register_firstboot_script):
1294+
fake_msi_source_path = "e:\\guest-agent\\qemu-ga-x86_64.msi"
1295+
1296+
self.morphing_tools._setup_qemu_agent_installation_local_script(
1297+
msi_source_path=fake_msi_source_path)
1298+
1299+
exp_msi_dest_path = "C:\\Cloudbase-Init\\qemu-ga-x86_64.msi"
1300+
self.morphing_tools._conn.download_file.assert_not_called()
1301+
self.morphing_tools._conn.exec_ps_command.assert_called_once_with(
1302+
"Copy-Item '%s' -Destination '%s'" % (
1303+
fake_msi_source_path, exp_msi_dest_path))
1304+
1305+
exp_script = windows.QEMU_GUEST_AGENT_INSTALL_SCRIPT_FORMAT % {
1306+
"agent_msi_path": exp_msi_dest_path,
1307+
"agent_msi_log_dir": "C:\\Cloudbase-Init"}
1308+
mock_register_firstboot_script.assert_called_once_with(
1309+
exp_script, user_provided=False,
1310+
script_filename="coriolis_qemu_agent_install.ps1")
1311+
1312+
@mock.patch.object(
1313+
windows.BaseWindowsMorphingTools, "register_firstboot_script")
1314+
def test_setup_qemu_agent_installation_local_script_no_source(
1315+
self, mock_register_firstboot_script):
1316+
self.assertRaises(
1317+
exception.CoriolisException,
1318+
self.morphing_tools._setup_qemu_agent_installation_local_script)
1319+
1320+
self.morphing_tools._conn.download_file.assert_not_called()
1321+
self.morphing_tools._conn.exec_ps_command.assert_not_called()
1322+
mock_register_firstboot_script.assert_not_called()
1323+
1324+
@mock.patch.object(
1325+
windows.BaseWindowsMorphingTools, "register_firstboot_script")
1326+
def test_setup_qemu_agent_installation_local_script_from_qemu_ga_dir(
1327+
self, mock_register_firstboot_script):
1328+
fake_qemu_ga_source_dir = "f:\\qemu-ga"
1329+
self.morphing_tools._conn.test_path.return_value = True
1330+
1331+
self.morphing_tools._setup_qemu_agent_installation_local_script(
1332+
qemu_ga_source_dir=fake_qemu_ga_source_dir)
1333+
1334+
self.morphing_tools._conn.test_path.assert_called_once_with(
1335+
"%s\\x64\\qemu-ga.exe" % fake_qemu_ga_source_dir)
1336+
self.morphing_tools._conn.download_file.assert_not_called()
1337+
self.morphing_tools._conn.exec_ps_command.assert_called_once_with(
1338+
"Copy-Item -Recurse -Force '%s' '%s'" % (
1339+
fake_qemu_ga_source_dir, "C:\\Cloudbase-Init"),
1340+
ignore_stdout=True)
1341+
1342+
exp_script = (
1343+
windows.QEMU_GUEST_AGENT_INSTALL_FROM_DIR_SCRIPT_FORMAT % {
1344+
"qemu_ga_dir": "C:\\Cloudbase-Init\\qemu-ga"})
1345+
mock_register_firstboot_script.assert_called_once_with(
1346+
exp_script, user_provided=False,
1347+
script_filename="coriolis_qemu_agent_install.ps1")
1348+
1349+
@mock.patch.object(
1350+
windows.BaseWindowsMorphingTools, "register_firstboot_script")
1351+
def test_setup_qemu_agent_installation_local_script_qemu_ga_dir_missing(
1352+
self, mock_register_firstboot_script):
1353+
self.morphing_tools._conn.test_path.return_value = False
1354+
1355+
self.morphing_tools._setup_qemu_agent_installation_local_script(
1356+
qemu_ga_source_dir="f:\\qemu-ga")
1357+
1358+
self.morphing_tools._conn.download_file.assert_not_called()
1359+
self.morphing_tools._conn.exec_ps_command.assert_not_called()
1360+
mock_register_firstboot_script.assert_not_called()

0 commit comments

Comments
 (0)