Skip to content

Commit ffbdef2

Browse files
tests/storage/linstor: Added test_linstor_sr_expand_host to validate the expansion of Linstor SR by integrating a new host into the storage pool.
The test: - Ensures the new host has available disks and installs required Linstor packages. - Configures LVM and integrates it into Linstor SR pool. - Expands SR capacity and validates the increase. - Handles cleanup in case of failure, including removing the node, ejecting the host, and uninstalling Linstor packages. Signed-off-by: Rushikesh Jadhav <[email protected]>
1 parent 14deeb0 commit ffbdef2

File tree

1 file changed

+103
-1
lines changed

1 file changed

+103
-1
lines changed

tests/storage/linstor/test_linstor_sr.py

Lines changed: 103 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
import pytest
33
import time
44

5-
from .conftest import GROUP_NAME, LINSTOR_PACKAGE
5+
from .conftest import GROUP_NAME, LINSTOR_PACKAGE, LINSTOR_RELEASE_PACKAGE
66
from lib.commands import SSHCommandFailed
77
from lib.common import wait_for, vm_image
88
from tests.storage import vdi_is_open
@@ -143,6 +143,108 @@ def test_linstor_sr_expand_disk(self, linstor_sr, provisioning_type, storage_poo
143143
# Ensure VM is able to start and shutdown on expanded SR
144144
self.test_start_and_shutdown_VM(vm)
145145

146+
@pytest.mark.small_vm
147+
def test_linstor_sr_expand_host(self, linstor_sr, host, hostB1, provisioning_type,
148+
storage_pool_name, vm_on_linstor_sr):
149+
"""
150+
This test validates expansion of a LINSTOR SR by dynamically adding a new host with local storage to the pool.
151+
A VM is started on the SR before expansion begins to ensure the SR is in active use during the process.
152+
153+
It performs the following steps:
154+
- Installs LINSTOR packages on the new host (if missing).
155+
- Detects and prepares raw disks using LVM commands.
156+
- Joins the host (hostB1) to the existing pool and registers it with LINSTOR as a node.
157+
- Creates a new LINSTOR storage pool on the added host (LVM or LVM-thin, based on provisioning type).
158+
- Confirms SR expansion by verifying increased physical size.
159+
- Ensures SR functionality by rebooting the VM running on the SR.
160+
161+
Finally, the test cleans up by deleting the LINSTOR node, ejecting the host from the pool,
162+
and removing packages and LVM metadata.
163+
"""
164+
sr = linstor_sr
165+
vm = vm_on_linstor_sr
166+
vm.start()
167+
sr_size = sr.pool.master.xe('sr-param-get', {'uuid': sr.uuid, 'param-name': 'physical-size'})
168+
resized = False
169+
# Ensure that its a single host pool and not multi host pool
170+
assert len(hostB1.pool.hosts) == 1, "This test requires second host to be a single host"
171+
# Ensure that the host has disks available to use, we do not care about disks symmetry across pool
172+
available_disks = hostB1.available_disks()
173+
# We need the disk to be "raw" (non LVM_member etc) to use
174+
available_disks = [disk for disk in available_disks if hostB1.raw_disk_is_available(disk)]
175+
assert len(available_disks) >= 1, "This test requires second host to have free disk(s)"
176+
if not hostB1.is_package_installed(LINSTOR_PACKAGE):
177+
logging.info("Installing %s on host %s", LINSTOR_PACKAGE, hostB1)
178+
hostB1.yum_install([LINSTOR_RELEASE_PACKAGE])
179+
hostB1.yum_install([LINSTOR_PACKAGE], enablerepo="xcp-ng-linstor-testing")
180+
# Needed because the linstor driver is not in the xapi sm-plugins list
181+
# before installing the LINSTOR packages.
182+
hostB1.ssh(["systemctl", "restart", "multipathd"])
183+
hostB1.restart_toolstack(verify=True)
184+
185+
devices = [f"/dev/{disk}" for disk in available_disks]
186+
187+
for disk in available_disks:
188+
logging.info("Found Disk %s", disk)
189+
device = "/dev/" + disk
190+
hostB1.ssh(['pvcreate', device])
191+
192+
hostB1.ssh(['vgcreate', GROUP_NAME] + devices)
193+
194+
sr_group_name = "xcp-sr-" + storage_pool_name.replace("/", "_")
195+
hostname_hostB1 = hostB1.xe('host-param-get', {'uuid': hostB1.uuid,
196+
'param-name': 'name-label'})
197+
198+
controller_option = "--controllers="
199+
for member in host.pool.hosts:
200+
controller_option += f"{member.hostname_or_ip},"
201+
202+
hostB1_pool = hostB1.pool # Saving the hostB1 pool info before overwrite in join_pool.
203+
try:
204+
logging.info("Joining host %s to pool %s", hostB1, host)
205+
# This will cause hostB1 pool to overwrite itself as host.pool creating issues on next run.
206+
hostB1.join_pool(host.pool)
207+
logging.info("Current list of linstor nodes:")
208+
logging.info(host.ssh_with_result(["linstor", controller_option, "node", "list"]).stdout)
209+
logging.info("Creating linstor node")
210+
host.ssh(["linstor", controller_option, "node", "create", "--node-type", "combined",
211+
"--communication-type", "plain", hostname_hostB1, hostB1.hostname_or_ip]) # Linstor Node Create
212+
logging.info(hostB1.ssh_with_result(['systemctl', 'restart', 'linstor-satellite.service']).stdout)
213+
time.sleep(45) # Wait for node to come online
214+
logging.info("New list of linstor nodes:")
215+
logging.info(host.ssh_with_result(["linstor", controller_option, "node", "list"]).stdout)
216+
logging.info("Expanding with linstor node")
217+
218+
if provisioning_type == "thin":
219+
hostB1.ssh(['lvcreate', '-l', '+100%FREE', '-T', storage_pool_name])
220+
host.ssh_with_result(["linstor", controller_option, "storage-pool", "create", "lvmthin",
221+
hostname_hostB1, sr_group_name, storage_pool_name]).stdout # Expand linstor
222+
else:
223+
host.ssh_with_result(["linstor", controller_option, "storage-pool", "create", "lvm",
224+
hostname_hostB1, sr_group_name, storage_pool_name]).stdout # Expand linstor
225+
except Exception as e:
226+
logging.info("Exception: {}".format(e))
227+
host.ssh(["linstor", controller_option, "node", "delete", hostname_hostB1]) # Linstor Node Delete
228+
host.pool.eject_host(hostB1)
229+
hostB1.ssh(['vgremove', '-y', GROUP_NAME])
230+
hostB1.ssh(['pvremove', '-y'] + devices) # Device cleanup
231+
hostB1.yum_remove([LINSTOR_PACKAGE]) # Package cleanup
232+
233+
resized = True
234+
sr.scan()
235+
new_sr_size = sr.pool.master.xe('sr-param-get', {'uuid': sr.uuid, 'param-name': 'physical-size'})
236+
assert int(new_sr_size) > int(sr_size) and resized is True, \
237+
f"Expected SR size to increase but got old size: {sr_size}, new size: {new_sr_size}"
238+
logging.info("SR expansion completed from size %s to %s", sr_size, new_sr_size)
239+
vm.shutdown(verify=True)
240+
# Ensure VM is able to start and shutdown on expanded SR
241+
self.test_start_and_shutdown_VM(vm)
242+
243+
host.ssh_with_result(["linstor", controller_option, "node", "delete", hostname_hostB1]).stdout
244+
host.pool.eject_host(hostB1)
245+
hostB1.pool = hostB1_pool # Post eject, reset hostB1.pool for next run ("thick")
246+
hostB1.yum_remove([LINSTOR_PACKAGE]) # Package cleanup
247+
146248
# *** tests with reboots (longer tests).
147249

148250
@pytest.mark.reboot

0 commit comments

Comments
 (0)