diff --git a/jobs.py b/jobs.py index 3c4feac43..605bd600d 100755 --- a/jobs.py +++ b/jobs.py @@ -56,7 +56,7 @@ "paths": ["tests/quicktest"], }, "storage-main": { - "description": "tests all storage drivers (except linstor), but avoids migrations and reboots", + "description": "tests all storage drivers, but avoids migrations and reboots", "requirements": [ "A pool with at least 3 hosts.", "An additional free disk on every host.", @@ -70,10 +70,10 @@ }, "paths": ["tests/storage"], "markers": "(small_vm or no_vm) and not reboot", - "name_filter": "not migration and not linstor and not test_fsp_sr.py", + "name_filter": "not migration and not test_fsp_sr.py", }, "storage-migrations": { - "description": "tests migrations with all storage drivers (except linstor)", + "description": "tests migrations with all storage drivers", "requirements": [ "A pool with at least 3 hosts.", "An additional free disk on every host.", @@ -88,10 +88,10 @@ }, "paths": ["tests/storage"], "markers": "", - "name_filter": "migration and not linstor", + "name_filter": "migration", }, "storage-reboots": { - "description": "storage driver tests that involve rebooting hosts (except linstor and flaky tests)", + "description": "storage driver tests that involve rebooting hosts (except flaky tests)", "requirements": [ "A pool with at least 3 hosts, whose master host can be rebooted (best if reboots fast).", "An additional free disk on every host.", @@ -105,7 +105,6 @@ }, "paths": ["tests/storage"], "markers": "reboot and not flaky", - "name_filter": "not linstor", }, "sb-main": { "description": "tests uefistored/varstored and SecureBoot using a small unix VM (or no VM when none needed)", @@ -230,7 +229,6 @@ } BROKEN_TESTS = [ - "tests/storage/linstor", # needs updating and fixing "tests/misc/test_update_host.py", # doesn't test anything currently unless the host is out of date "tests/migration/test_host_evacuate.py::TestHostEvacuateWithNetwork", # not really broken but we'll handle it later "tests/storage/fsp", # driver not present by default. Needs test setup to be improved. diff --git a/tests/storage/linstor/conftest.py b/tests/storage/linstor/conftest.py index b024bf1ac..5b75ba21f 100644 --- a/tests/storage/linstor/conftest.py +++ b/tests/storage/linstor/conftest.py @@ -1,114 +1,70 @@ import logging import pytest -from lib.common import wait_for_not +import lib.commands as commands -GROUP_NAME = 'linstor_group' -LINSTOR_PACKAGES = ['drbd', 'kmod-drbd', 'linstor-client', 'linstor-controller', 'linstor-satellite', 'python-linstor'] +# explicit import for package-scope fixtures +from pkgfixtures import pool_with_saved_yum_state -# FIXME: make fixtures robust: clean behind if something fails in a setup or teardown +GROUP_NAME = 'linstor_group' +STORAGE_POOL_NAME = f'{GROUP_NAME}/thin_device' +LINSTOR_RELEASE_PACKAGE = 'xcp-ng-release-linstor' +LINSTOR_PACKAGE = 'xcp-ng-linstor' @pytest.fixture(scope='package') -def lvm_disks(host): - disks = [] +def lvm_disk(host, sr_disk_for_all_hosts): + device = '/dev/' + sr_disk_for_all_hosts hosts = host.pool.hosts for host in hosts: - local_disks = host.disks() - assert len(local_disks) > 1, "at least two disks are required" - disks.append('/dev/' + local_disks[1]) - - for i in range(len(hosts)): - hosts[i].ssh(['pvcreate', '-ff', '-y', disks[i]]) - hosts[i].ssh(['vgcreate', GROUP_NAME, disks[i]]) - - yield disks - - for i in range(len(hosts)): - hosts[i].ssh(['vgremove', GROUP_NAME]) - hosts[i].ssh(['pvremove', disks[i]]) - -def check_linstor_packages(host): - if not host.check_packages_available(LINSTOR_PACKAGES): - raise Exception('Unable to find LINSTOR packages in the yum repositories of {}'.format(host)) - -@pytest.fixture(scope='package') -def hosts_with_linstor(host, additional_repos): - master = host - hosts = master.pool.hosts - - for host in hosts: - check_linstor_packages(host) - - # Configure master host. - master.yum_save_state() - master.yum_install(LINSTOR_PACKAGES) - master.ssh(['systemctl', 'restart', 'linstor-satellite']) - master.ssh(['systemctl', 'restart', 'linstor-controller']) - - # Waiting for startup... - try: - wait_for_not(lambda: master.ssh_with_result(['linstor', 'node', 'list']).returncode) - except Exception as e: - master.yum_restore_saved_state() - raise e - - # Configure slaves. - for host in hosts[1:]: - host.yum_save_state() - host.yum_install(LINSTOR_PACKAGES) - host.ssh(['systemctl', 'restart', 'linstor-satellite']) - - yield hosts - - for host in hosts: - host.yum_restore_saved_state() - -def delete_linstor_nodes(hosts_with_linstor): - master = hosts_with_linstor[0] - master_ip = master.pool.host_ip(master.uuid) - control_arg = '--controllers=' + master_ip - - for host in hosts_with_linstor: try: - host.ssh(['linstor', control_arg, 'node', 'delete', '`uname -n`']) - except Exception: - logging.error('Failed to delete properly node on host {}'.format(host)) - pass + host.ssh(['pvcreate', '-ff', '-y', device]) + except commands.SSHCommandFailed as e: + if e.stdout.endswith('Mounted filesystem?'): + host.ssh(['vgremove', '-f', GROUP_NAME, '-y']) + host.ssh(['pvcreate', '-ff', '-y', device]) + elif e.stdout.endswith('excluded by a filter.'): + host.ssh(['wipefs', '-a', device]) + host.ssh(['pvcreate', '-ff', '-y', device]) + else: + raise e + + host.ssh(['vgcreate', GROUP_NAME, device]) + host.ssh(['lvcreate', '-l', '100%FREE', '-T', STORAGE_POOL_NAME]) + + yield device -def create_linstor_sr(hosts_with_linstor): - master = hosts_with_linstor[0] - pool = master.pool - master_ip = pool.host_ip(master.uuid) - control_arg = '--controllers=' + master_ip - - delete_linstor_nodes(hosts_with_linstor) - - for host in hosts_with_linstor[1:]: - host_ip = pool.host_ip(host.uuid) - host.ssh(['linstor', control_arg, 'node', 'create', '`uname -n`', host_ip, '--node-type', 'Satellite']) - master.ssh(['linstor', 'node', 'create', '`uname -n`', master_ip, '--node-type', 'Combined']) - - try: - return master.sr_create('linstor', 'LINSTOR-SR-test', { - 'hosts': ','.join([host.hostname() for host in hosts_with_linstor]), - 'group-name': GROUP_NAME, - 'redundancy': len(hosts_with_linstor), - 'provisioning': 'thick' - }, shared=True) - except Exception as e: - delete_linstor_nodes(hosts_with_linstor) - raise e + for host in hosts: + host.ssh(['vgremove', '-f', GROUP_NAME]) + host.ssh(['pvremove', device]) -def destroy_linstor_sr(hosts_with_linstor, sr): - sr.destroy(verify=True, force=True) - delete_linstor_nodes(hosts_with_linstor) +@pytest.fixture(scope='package') +def pool_with_linstor(hostA2, lvm_disk, pool_with_saved_yum_state): + pool = pool_with_saved_yum_state + for host in pool.hosts: + if host.is_package_installed(LINSTOR_PACKAGE): + raise Exception( + f'{LINSTOR_PACKAGE} is already installed on host {host}. This should not be the case.' + ) + + for host in pool.hosts: + host.yum_install([LINSTOR_RELEASE_PACKAGE]) + host.yum_install([LINSTOR_PACKAGE]) + # Needed because the linstor driver is not in the xapi sm-plugins list + # before installing the LINSTOR packages. + host.restart_toolstack(verify=True) + + yield pool @pytest.fixture(scope='package') -def linstor_sr(hosts_with_linstor, lvm_disks): - sr = create_linstor_sr(hosts_with_linstor) +def linstor_sr(pool_with_linstor): + sr = pool_with_linstor.master.sr_create('linstor', 'LINSTOR-SR-test', { + 'group-name': STORAGE_POOL_NAME, + 'redundancy': str(min(len(pool_with_linstor.hosts), 3)), + 'provisioning': 'thin' + }, shared=True) yield sr - destroy_linstor_sr(hosts_with_linstor, sr) + sr.destroy() @pytest.fixture(scope='module') def vdi_on_linstor_sr(linstor_sr): @@ -120,6 +76,5 @@ def vdi_on_linstor_sr(linstor_sr): def vm_on_linstor_sr(host, linstor_sr, vm_ref): vm = host.import_vm(vm_ref, sr_uuid=linstor_sr.uuid) yield vm - # teardown logging.info("<< Destroy VM") vm.destroy(verify=True) diff --git a/tests/storage/linstor/test_linstor_sr.py b/tests/storage/linstor/test_linstor_sr.py index 585707db5..8c540196a 100644 --- a/tests/storage/linstor/test_linstor_sr.py +++ b/tests/storage/linstor/test_linstor_sr.py @@ -2,7 +2,7 @@ import pytest import time -from .conftest import GROUP_NAME, create_linstor_sr, destroy_linstor_sr +from .conftest import STORAGE_POOL_NAME, LINSTOR_PACKAGE from lib.commands import SSHCommandFailed from lib.common import wait_for, vm_image from tests.storage import vdi_is_open @@ -10,23 +10,23 @@ # Requirements: # - one XCP-ng host >= 8.2 with an additional unused disk for the SR # - access to XCP-ng RPM repository from the host -# - a repo with the LINSTOR RPMs must be given using the command line param `--additional-repos` class TestLinstorSRCreateDestroy: - vm = None - - def test_create_sr_without_linstor(self, hosts, lvm_disks): - master = hosts[0] + """ + 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_disk): # This test must be the first in the series in this module - assert not master.binary_exists('linstor'), \ + assert not host.is_package_installed('python-linstor'), \ "linstor must not be installed on the host at the beginning of the tests" try: - sr = master.sr_create('linstor', 'LINSTOR-SR-test', { - 'hosts': ','.join([host.hostname() for host in hosts]), - 'group-name': GROUP_NAME, - 'redundancy': len(hosts), - 'provisioning': 'thick' + sr = host.sr_create('linstor', 'LINSTOR-SR-test', { + 'group-name': STORAGE_POOL_NAME, + 'redundancy': '1', + 'provisioning': 'thin' }, shared=True) try: sr.destroy() @@ -36,15 +36,19 @@ def test_create_sr_without_linstor(self, hosts, lvm_disks): except SSHCommandFailed as e: logging.info("SR creation failed, as expected: {}".format(e)) - def test_create_and_destroy_sr(self, hosts_with_linstor, lvm_disks): + def test_create_and_destroy_sr(self, pool_with_linstor): # Create and destroy tested in the same test to leave the host as unchanged as possible - master = hosts_with_linstor[0] - sr = create_linstor_sr(hosts_with_linstor) + master = pool_with_linstor.master + sr = master.sr_create('linstor', 'LINSTOR-SR-test', { + 'group-name': STORAGE_POOL_NAME, + 'redundancy': '1', + 'provisioning': 'thin' + }, 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) - destroy_linstor_sr(hosts_with_linstor, sr) + sr.destroy(verify=True) @pytest.mark.usefixtures("linstor_sr") class TestLinstorSR: @@ -84,11 +88,10 @@ def test_reboot(self, vm_on_linstor_sr, host, linstor_sr): @pytest.mark.reboot def test_linstor_missing(self, linstor_sr, host): - packages = ['python-linstor', 'linstor-client'] sr = linstor_sr linstor_installed = True try: - host.yum_remove(packages) + host.yum_remove(['python-linstor', 'linstor-client']) linstor_installed = False try: sr.scan() @@ -100,12 +103,18 @@ def test_linstor_missing(self, linstor_sr, host): time.sleep(10) logging.info("Assert PBD not attached") assert not sr.all_pbds_attached() - host.yum_install(packages) + 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(packages) + host.yum_install([LINSTOR_PACKAGE]) # *** End of tests with reboots diff --git a/tests/storage/linstor/test_linstor_sr_crosspool_migration.py b/tests/storage/linstor/test_linstor_sr_crosspool_migration.py index 340060aa1..74b1c0b5d 100644 --- a/tests/storage/linstor/test_linstor_sr_crosspool_migration.py +++ b/tests/storage/linstor/test_linstor_sr_crosspool_migration.py @@ -9,7 +9,6 @@ # - A VM to import to the LINSTOR SR # And: # - access to XCP-ng RPM repository from hostA1 -# - a repo with the LINSTOR RPMs must be given using the command line param `--additional-repos` @pytest.mark.small_vm # run with a small VM to test the features @pytest.mark.big_vm # and ideally on a big VM to test it scales diff --git a/tests/storage/linstor/test_linstor_sr_intrapool_migration.py b/tests/storage/linstor/test_linstor_sr_intrapool_migration.py index 5915b3acb..b1853feee 100644 --- a/tests/storage/linstor/test_linstor_sr_intrapool_migration.py +++ b/tests/storage/linstor/test_linstor_sr_intrapool_migration.py @@ -9,12 +9,14 @@ # - A VM to import to the LINSTOR SR # And: # - access to XCP-ng RPM repository from hostA1 -# - a repo with the LINSTOR RPMs must be given using the command line param `--additional-repos` @pytest.mark.small_vm # run with a small VM to test the features -@pytest.mark.big_vm # and ideally on a big VM to test it scales +@pytest.mark.big_vm # and ideally with a big VM to test it scales @pytest.mark.usefixtures("hostA2", "local_sr_on_hostA2") class Test: + def test_live_intrapool_shared_migration(self, host, hostA2, vm_on_linstor_sr, linstor_sr): + live_storage_migration_then_come_back(vm_on_linstor_sr, host, linstor_sr, hostA2, linstor_sr) + def test_cold_intrapool_migration(self, host, hostA2, vm_on_linstor_sr, linstor_sr, local_sr_on_hostA2): cold_migration_then_come_back(vm_on_linstor_sr, host, linstor_sr, hostA2, local_sr_on_hostA2)