forked from xcp-ng/xcp-ng-tests
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathtest_linstor_sr.py
More file actions
241 lines (207 loc) · 9.14 KB
/
Copy pathtest_linstor_sr.py
File metadata and controls
241 lines (207 loc) · 9.14 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
import logging
import pytest
import time
from .conftest import LINSTOR_PACKAGE
from lib.commands import SSHCommandFailed
from lib.common import wait_for, vm_image
from tests.storage import vdi_is_open
# Requirements:
# - two or more XCP-ng hosts >= 8.2 with additional unused disk(s) for the SR
# - access to XCP-ng RPM repository from the host
class TestLinstorSRCreateDestroy:
"""
Tests that do not use fixtures that setup the SR or import VMs,
because they precisely need to test SR creation and destruction,
and VM import.
"""
def test_create_sr_without_linstor(self, host, lvm_disks, provisioning_type, storage_pool_name):
# This test must be the first in the series in this module
assert not host.is_package_installed('python-linstor'), \
"linstor must not be installed on the host at the beginning of the tests"
try:
sr = host.sr_create('linstor', 'LINSTOR-SR-test', {
'group-name': storage_pool_name,
'redundancy': '1',
'provisioning': provisioning_type
}, shared=True)
try:
sr.destroy()
except Exception:
pass
assert False, "SR creation should not have succeeded!"
except SSHCommandFailed as e:
logging.info("SR creation failed, as expected: {}".format(e))
def test_create_and_destroy_sr(self, pool_with_linstor, provisioning_type, storage_pool_name):
# Create and destroy tested in the same test to leave the host as unchanged as possible
master = pool_with_linstor.master
sr = master.sr_create('linstor', 'LINSTOR-SR-test', {
'group-name': storage_pool_name,
'redundancy': '1',
'provisioning': provisioning_type
}, shared=True)
# import a VM in order to detect vm import issues here rather than in the vm_on_linstor_sr fixture used in
# the next tests, because errors in fixtures break teardown
vm = master.import_vm(vm_image('mini-linux-x86_64-bios'), sr.uuid)
vm.destroy(verify=True)
sr.destroy(verify=True)
@pytest.mark.usefixtures("linstor_sr")
class TestLinstorSR:
@pytest.mark.quicktest
def test_quicktest(self, linstor_sr, provisioning_type):
try:
linstor_sr.run_quicktest()
except Exception:
if provisioning_type == "thick":
pytest.xfail(reason="Known failure for thick provisioning")
raise # Let thin failures fail test
else:
if provisioning_type == "thick":
pytest.fail("Expected failure for thick provisioning did not occur (XPASS)")
def test_vdi_is_not_open(self, vdi_on_linstor_sr):
assert not vdi_is_open(vdi_on_linstor_sr)
@pytest.mark.small_vm # run with a small VM to test the features
@pytest.mark.big_vm # and ideally with a big VM to test it scales
def test_start_and_shutdown_VM(self, vm_on_linstor_sr):
vm = vm_on_linstor_sr
vm.start()
vm.wait_for_os_booted()
vm.shutdown(verify=True)
@pytest.mark.small_vm
@pytest.mark.big_vm
def test_snapshot(self, vm_on_linstor_sr):
vm = vm_on_linstor_sr
vm.start()
try:
vm.wait_for_os_booted()
vm.test_snapshot_on_running_vm()
finally:
vm.shutdown(verify=True)
# *** tests with reboots (longer tests).
@pytest.mark.reboot
@pytest.mark.small_vm
def test_reboot(self, vm_on_linstor_sr, host, linstor_sr):
sr = linstor_sr
vm = vm_on_linstor_sr
host.reboot(verify=True)
wait_for(sr.all_pbds_attached, "Wait for PBD attached")
# start the VM as a way to check that the underlying SR is operational
vm.start(on=host.uuid)
vm.wait_for_os_booted()
vm.shutdown(verify=True)
@pytest.mark.reboot
def test_linstor_missing(self, linstor_sr, host):
sr = linstor_sr
linstor_installed = True
try:
host.yum_remove(['python-linstor', 'linstor-client'])
linstor_installed = False
try:
sr.scan()
assert False, "SR scan should have failed"
except SSHCommandFailed:
logging.info("SR scan failed as expected.")
host.reboot(verify=True)
# give the host some time to try to attach the SR
time.sleep(10)
logging.info("Assert PBD not attached")
assert not sr.all_pbds_attached()
host.yum_install(['xcp-ng-linstor'])
linstor_installed = True
# Needed because the linstor driver is not in the xapi
# sm-plugins list because xcp-ng-linstor RPM has been
# removed by the `yum remove ...` call.
host.restart_toolstack(verify=True)
sr.plug_pbds(verify=True)
sr.scan()
finally:
if not linstor_installed:
host.yum_install([LINSTOR_PACKAGE])
@pytest.mark.reboot
@pytest.mark.small_vm
@pytest.mark.upgrade_test
def test_linstor_sr_pool_update(self, linstor_sr, vm_on_linstor_sr):
"""
Perform update on the Linstor SR pool hosts while ensuring VM availability.
1. Identify all hosts in the SR pool and order them with the master first.
2. Update all hosts if updates are available.
3. Reboot all hosts.
4. Sequentially ensure that the VM can start on all hosts.
"""
import concurrent.futures, threading
sr = linstor_sr
vm = vm_on_linstor_sr
updates_applied = []
updates_lock = threading.Lock()
# Sort hosts so that pool master is first (optional)
hosts = sorted(sr.pool.hosts, key=lambda h: h != sr.pool.master)
# RPU is disabled for pools with XOSTOR SRs.
# LINSTOR expects that we always use satellites and controllers with the same version on all hosts.
def install_updates_on(host):
logging.info("Checking on host %s", host.hostname_or_ip)
if host.has_updates(enablerepo="xcp-ng-linstor-testing"):
host.install_updates(enablerepo="xcp-ng-linstor-testing")
with updates_lock:
updates_applied.append(host)
else:
logging.info("No updates available for host %s", host.hostname_or_ip)
with concurrent.futures.ThreadPoolExecutor() as executor:
executor.map(install_updates_on, hosts)
# Reboot updated hosts
def reboot_updated(host):
host.reboot(verify=True)
with concurrent.futures.ThreadPoolExecutor() as executor:
executor.map(reboot_updated, updates_applied)
# Ensure VM is able to boot on all the hosts
for h in hosts:
vm.start(on=h.uuid)
vm.wait_for_os_booted()
vm.shutdown(verify=True)
sr.scan()
# *** End of tests with reboots
# --- Test diskless resources --------------------------------------------------
def _get_diskful_hosts(host, controller_option, volume_name):
# Find host where volume is diskless
# | {volume_name} | {host} | 7017 | Unused | Ok | UpToDate | 2023-10-24 18:52:05 |
lines = host.ssh([
"linstor", controller_option, "resource", "list",
"|", "grep", volume_name, "|", "grep", "UpToDate"
]).splitlines()
diskfuls = []
for line in lines:
hostname = line.split('|')[2].strip()
diskfuls += hostname
return diskfuls
def _ensure_resource_remain_diskless(host, controller_option, volume_name, diskless):
diskfuls = _get_diskful_hosts(host, controller_option, volume_name)
for diskless_host in diskless:
assert diskless_host.param_get("name-label").lower() not in diskfuls
class TestLinstorDisklessResource:
@pytest.mark.small_vm
def test_diskless_kept(self, host, linstor_sr, vm_on_linstor_sr, storage_pool_name):
vm = vm_on_linstor_sr
vdi_uuids = vm.vdi_uuids(sr_uuid=linstor_sr.uuid)
vdi_uuid = vdi_uuids[0]
assert vdi_uuid is not None
controller_option = "--controllers="
for member in host.pool.hosts:
controller_option += f"{member.hostname_or_ip},"
sr_group_name = "xcp-sr-" + storage_pool_name.replace("/", "_")
# Get volume name from VDI uuid
# "xcp/volume/{vdi_uuid}/volume-name": "{volume_name}"
output = host.ssh([
"linstor-kv-tool", "--dump-volumes", "-g", sr_group_name,
"|", "grep", "volume-name", "|", "grep", vdi_uuid
])
volume_name = output.split(': ')[1].split('"')[1]
diskfuls = _get_diskful_hosts(host, controller_option, volume_name)
diskless = []
for member in host.pool.hosts:
if member.param_get("name-label").lower() not in diskfuls:
diskless += [member]
assert diskless
# Start VM on host with diskless resource
vm.start(on=diskless[0].uuid)
vm.wait_for_os_booted()
_ensure_resource_remain_diskless(host, controller_option, volume_name, diskless)
vm.shutdown(verify=True)
_ensure_resource_remain_diskless(host, controller_option, volume_name, diskless)