Skip to content

Commit 80f791d

Browse files
committed
WIP install 8/n: run installer, go see inside and detect success
FIXME: - specification of answerfile contents and dom0 cmdline is tightly linked, e.g. we rely on atexit=shell - repeatedly polling with grep is junk
1 parent 499b927 commit 80f791d

File tree

3 files changed

+157
-2
lines changed

3 files changed

+157
-2
lines changed

conftest.py

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -268,20 +268,33 @@ def iso_remaster():
268268
SOURCE_ISO = "/home/user/iso/xcp-ng-8.2.1-20231130.iso" # FIXME dict in data.py
269269
ANSWERFILE_URL = "http://pxe/configs/custom/ydi/install-8.2-uefi-iso-ext.xml" # FIXME
270270

271-
from data import ISOSR_SRV, ISOSR_PATH, TOOLS
271+
from data import ISOSR_SRV, ISOSR_PATH, TEST_SSH_PUBKEY, TOOLS
272272
assert "iso-remaster" in TOOLS
273273
iso_remaster = TOOLS["iso-remaster"]
274274
assert os.access(iso_remaster, os.X_OK)
275275

276276
with tempfile.TemporaryDirectory() as isotmp:
277277
remastered_iso = os.path.join(isotmp, "image.iso")
278278
iso_patcher_script = os.path.join(isotmp, "iso-patcher")
279+
img_patcher_script = os.path.join(isotmp, "img-patcher")
279280

280281
logging.info("Remastering %s to %s", SOURCE_ISO, remastered_iso)
281282

283+
# generate install.img-patcher script
284+
with open(img_patcher_script, "xt") as patcher_fd:
285+
# FIXME insert nswerfile in there
286+
print(f"""#!/bin/bash
287+
set -ex
288+
INSTALLIMG="$1"
289+
mkdir -p "$INSTALLIMG/root/.ssh"
290+
echo "{TEST_SSH_PUBKEY}" > "$INSTALLIMG/root/.ssh/authorized_keys"
291+
""",
292+
file=patcher_fd)
293+
os.chmod(patcher_fd.fileno(), 0o755)
294+
282295
# generate iso-patcher script
283296
with open(iso_patcher_script, "xt") as patcher_fd:
284-
passwd = "passw0rd" # FIXME hash
297+
passwd = "passw0rd" # FIXME use invalid hash
285298
print(f"""#!/bin/bash
286299
set -ex
287300
ISODIR="$1"
@@ -297,6 +310,7 @@ def iso_remaster():
297310

298311
# do remaster
299312
local_cmd([iso_remaster,
313+
"--install-patcher", img_patcher_script,
300314
"--iso-patcher", iso_patcher_script,
301315
SOURCE_ISO, remastered_iso
302316
])

data.py-dist

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@
66
HOST_DEFAULT_USER = "root"
77
HOST_DEFAULT_PASSWORD = ""
88

9+
# Public key for a private key available to the test runner
10+
TEST_SSH_PUBKEY = "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDKz9uQOoxq6Q0SQ0XTzQHhDolvuo/7EyrDZsYQbRELhcPJG8MT/o5u3HyJFhIP2+HqBSXXgmqRPJUkwz9wUwb2sUwf44qZm/pyPUWOoxyVtrDXzokU/uiaNKUMhbnfaXMz6Ogovtjua63qld2+ZRXnIgrVtYKtYBeu/qKGVSnf4FTOUKl1w3uKkr59IUwwAO8ay3wVnxXIHI/iJgq6JBgQNHbn3C/SpYU++nqL9G7dMyqGD36QPFuqH/cayL8TjNZ67TgAzsPX8OvmRSqjrv3KFbeSlpS/R4enHkSemhgfc8Z2f49tE7qxWZ6x4Uyp5E6ur37FsRf/tEtKIUJGMRXN XCP-ng CI"
11+
912
# The following prefix will be added to the `name-label` parameter of XAPI objects
1013
# that the tests will create or import, such as VMs and SRs.
1114
# Default value: [your login/user]

tests/install/test_install.py

Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,12 @@
11
import logging
2+
import os
23
import pytest
4+
import time
5+
6+
from lib import commands, pxe
7+
from lib.common import wait_for
8+
from lib.host import Host
9+
from lib.pool import Pool
310

411
@pytest.mark.vm_definitions(
512
dict(name="vm 1",
@@ -21,5 +28,136 @@ def test_install_nested_821_uefi(self, create_vms, iso_remaster):
2128
assert len(create_vms) == 1
2229
host_vm = create_vms[0]
2330

31+
vif = host_vm.vifs()[0]
32+
mac_address = vif.param_get('MAC')
33+
logging.info("Host VM has MAC %s", mac_address)
34+
2435
host_vm.create_cd_vbd("xvdd")
2536
host_vm.insert_cd(iso_remaster)
37+
38+
host_vm.start()
39+
wait_for(host_vm.is_running, "Wait for host VM running")
40+
try:
41+
# catch host-vm IP address
42+
wait_for(lambda: pxe.arp_addresses_for(mac_address),
43+
"Wait for DHCP server to see Host VM in ARP tables",
44+
timeout_secs=10*60)
45+
ips = pxe.arp_addresses_for(mac_address)
46+
logging.info("Host VM has IPs %s", ips)
47+
assert len(ips) == 1
48+
host_vm.ip = ips[0]
49+
50+
host_vm.ssh(["ls"])
51+
logging.info("ssh works")
52+
53+
# wait for "yum install" phase to finish
54+
wait_for(lambda: host_vm.ssh(["grep",
55+
"'DISPATCH: NEW PHASE: Completing installation'",
56+
"/tmp/install-log"],
57+
check=False, simple_output=False,
58+
).returncode == 0,
59+
"Wait for rpm installation to succeed",
60+
timeout_secs=40*60) # FIXME too big
61+
62+
# wait for install to finish
63+
wait_for(lambda: host_vm.ssh(["grep",
64+
"'The installation completed successfully'",
65+
"/tmp/install-log"],
66+
check=False, simple_output=False,
67+
).returncode == 0,
68+
"Wait for installation to succeed",
69+
timeout_secs=40*60) # FIXME too big
70+
71+
# powercycle, catch any change of IP
72+
logging.info("Rebooting Host VM after successful installation")
73+
try:
74+
# use "poweroff" because "reboot" would cause ARP and
75+
# SSH to be checked before host is down, and require
76+
# ssh retries
77+
host_vm.ssh(["poweroff"])
78+
except commands.SSHCommandFailed as e:
79+
# ignore connection closed by reboot
80+
if e.returncode == 255 and "closed by remote host" in e.stdout:
81+
logging.info("sshd closed the connection")
82+
pass
83+
else:
84+
raise
85+
wait_for(host_vm.is_halted, "Wait for host VM halted")
86+
host_vm.eject_cd()
87+
88+
# FIXME: make a snapshot here
89+
90+
# FIXME: evict MAC from ARP cache first?
91+
host_vm.start()
92+
wait_for(host_vm.is_running, "Wait for host VM running")
93+
94+
ips = pxe.arp_addresses_for(mac_address)
95+
logging.info("Host VM has IPs %s", ips)
96+
assert len(ips) == 1
97+
host_vm.ip = ips[0]
98+
99+
wait_for(lambda: not os.system(f"nc -zw5 {host_vm.ip} 22"),
100+
"Wait for ssh back up on Host VM", retry_delay_secs=5)
101+
102+
# pool master must be reachable here
103+
# FIXME: not sure why we seem to need this, while port 22 has been seen open
104+
tries = 5
105+
while True:
106+
try:
107+
pool = Pool(host_vm.ip)
108+
except commands.SSHCommandFailed as e:
109+
if "Connection refused" not in e.stdout:
110+
raise
111+
tries -= 1
112+
if tries:
113+
logging.warning("retrying connection to pool master")
114+
time.sleep(2)
115+
continue
116+
# retries failed
117+
raise
118+
# it worked!
119+
break
120+
121+
# wait for XAPI
122+
# FIXME: flaky, must check logs extraction on failure
123+
for service in ["control-domain-params-init",
124+
"network-init",
125+
"storage-init",
126+
"generate-iscsi-iqn",
127+
"create-guest-templates",
128+
]:
129+
try:
130+
wait_for(lambda: pool.master.ssh(["test", "-e", f"/var/lib/misc/ran-{service}"],
131+
check=False, simple_output=False,
132+
).returncode == 0,
133+
f"Wait for ran-{service} stamp")
134+
except TimeoutError:
135+
logging.warning("investigating lack of ran-{service} stamp")
136+
out = pool.master.ssh(["systemctl", "status", service], check=False)
137+
logging.warning("service status: %s", out)
138+
out = pool.master.ssh(["grep", "-r", service, "/var/log"], check=False)
139+
logging.warning("in logs: %s", out)
140+
141+
wait_for(pool.master.is_enabled, "Wait for XAPI to be ready", timeout_secs=30 * 60)
142+
143+
logging.info("Powering off pool master")
144+
try:
145+
# use "poweroff" because "reboot" would cause ARP and
146+
# SSH to be checked before host is down, and require
147+
# ssh retries
148+
pool.master.ssh(["poweroff"])
149+
except commands.SSHCommandFailed as e:
150+
# ignore connection closed by reboot
151+
if e.returncode == 255 and "closed by remote host" in e.stdout:
152+
logging.info("sshd closed the connection")
153+
pass
154+
else:
155+
raise
156+
157+
wait_for(host_vm.is_halted, "Wait for host VM halted")
158+
159+
except Exception as e:
160+
logging.critical("caught exception %s", e)
161+
#wait_for(lambda: False, 'Wait "forever"', timeout_secs=100*60)
162+
host_vm.shutdown(force=True)
163+
raise

0 commit comments

Comments
 (0)