From bfe86bacda9374dacfce717f2ce659b506b30622 Mon Sep 17 00:00:00 2001 From: Eitan Raviv Date: Fri, 25 Feb 2022 00:28:36 +0200 Subject: [PATCH] network: re-enable test_ipv6_support module Re-enable tests and refactor. Change-Id: Ib47213ddad77366f881ab0f2e83a305c06b40e1a Signed-off-by: Eitan Raviv --- network-suite-master/fixtures/fqdn.py | 92 ------------ network-suite-master/fixtures/storage.py | 9 +- network-suite-master/ovirtlib/storagelib.py | 125 ++++++++++------ .../test-scenarios/conftest.py | 5 +- .../test-scenarios/test_ipv6_support.py | 136 +++++------------- 5 files changed, 124 insertions(+), 243 deletions(-) diff --git a/network-suite-master/fixtures/fqdn.py b/network-suite-master/fixtures/fqdn.py index fa07115f..7b47adea 100644 --- a/network-suite-master/fixtures/fqdn.py +++ b/network-suite-master/fixtures/fqdn.py @@ -5,7 +5,6 @@ import pytest from ovirtlib import sshlib -from ovirtlib import syncutil OVN_CONF = '/etc/ovirt-provider-ovn/conf.d/10-setup-ovirt-provider-ovn.conf' @@ -35,94 +34,3 @@ def _fetch_fqdn(answer_file): for line in f: if line.startswith(FQDN_ENTRY): return line.strip().split(':', 1)[1] - - -@pytest.fixture(scope='session') -def host0_eth2_ipv6(host0_facts): - """ - nics created by lago are managed by nmcli and have autoconf ipv6 but have - not been assigned an address. this function requests a dynamic assignment - of an ipv6 to 'eth2' and retrieves it. - :return: the ipv6 address as string - :raise: timeout exception if global ipv6 address not found on NIC - """ - host_0 = sshlib.Node(host0_facts.default_ip(), host0_facts.ssh_password) - return _enable_dynamic_ipv6(host_0, 'eth2') - - -@pytest.fixture(scope='session') -def host0_eth1_ipv6(host0_facts): - """ - nics created by lago are managed by nmcli and have autoconf ipv6 but have - not been assigned an address. this function requests a dynamic assignment - of an ipv6 to 'eth1' and retrieves it. - :return: the ipv6 address as string - :raise: timeout exception if global ipv6 address not found on NIC - """ - host_0 = sshlib.Node(host0_facts.default_ip(), host0_facts.ssh_password) - return _enable_dynamic_ipv6(host_0, 'eth1') - - -@pytest.fixture(scope='session') -def engine_storage_ipv6(engine_facts): - """ - lago creates a network with an ipv6 subnet and connects it to NIC - 'eth1' of the engine. It names the network 'storage' but does not assign an - ipv6 address to the NIC. - this function requests a dynamic assignment of an ipv6 to 'eth1' of the - engine machine and retrieves it. - :return: the ipv6 address as string - :raise: timeout exception if global ipv6 address not found on NIC - """ - engine = sshlib.Node(engine_facts.default_ip(), engine_facts.ssh_password) - ENGINE_STORAGE_NIC = 'eth1' - return _enable_dynamic_ipv6(engine, ENGINE_STORAGE_NIC) - - -def _enable_dynamic_ipv6(ssh_node, nic_name): - """ - this function connects to the specified lago VM using its ssh API to: - * request the host OS to dynamically assign an ipv6 address to the - specified NIC - * wait for the address to be assigned (it might take up to a few seconds) - * retrieve the address - :return: the ipv6 address as string - :raise: timeout exception if global ipv6 address not found on NIC - """ - _assign_ipv6(ssh_node, nic_name) - return syncutil.sync( - exec_func=_get_ipv6, - exec_func_args=(ssh_node, nic_name), - success_criteria=lambda ipv6: ipv6 != '', - timeout=10, - ) - - -def _assign_ipv6(ssh_node, nic_name): - """ - lago creates ipv6 subnets and sets ipv6 autoconf on its VMs' NICs but does - not assign ipv6 addresses to the NICs. - this function connects to a lago VM using its ssh API and requests an ipv6 - address be assigned to the NIC using nmcli. - :param ssh_node: an sshlib.Node that exposes an ssh API into itself - :param nic_name: the name of the NIC to assign an ipv6 address to - :raise: exception if an error occurred during the assignment - """ - res = ssh_node.exec_command(' '.join(['nmcli', 'con', 'modify', nic_name, 'ipv6.method', 'auto'])) - - if res.code: - raise Exception('nmcli con modify failed: exit code %s, error "%s"' % (res.code, res.err)) - res = ssh_node.exec_command(' '.join(['nmcli', 'con', 'up', nic_name])) - if res.code: - raise Exception('nmcli con up failed: exit code %s, error "%s"' % (res.code, res.err)) - - -def _get_ipv6(ssh_node, nic_name): - """ - :param ssh_node: an sshlib.Node that exposes an ssh API into itself - :param nic_name: the name of the NIC from which to get the ipv6 address - :return: the ipv6 address of the lago vm on eth1 as string or empty string - """ - INET6 = 'inet6 ' - res = ssh_node.exec_command(['ip -o -6 a show', nic_name, 'scope global']) - return res.out[res.out.find(INET6) + len(INET6) : res.out.find('/')] diff --git a/network-suite-master/fixtures/storage.py b/network-suite-master/fixtures/storage.py index e054ff08..28afe440 100644 --- a/network-suite-master/fixtures/storage.py +++ b/network-suite-master/fixtures/storage.py @@ -22,16 +22,11 @@ def default_storage_domain(system, engine_facts, host_0_up, default_data_center) try: storage_domain.import_by_name(DEFAULT_DOMAIN_NAME) except EntityNotFoundError: + nfs_storage_data = storagelib.NfsStorageData(engine_facts.default_ip(urlize=True), DEFAULT_DOMAIN_PATH) storage_domain.create( name=DEFAULT_DOMAIN_NAME, - domain_type=storagelib.StorageDomainType.DATA, host=host_0_up, - host_storage_data=storagelib.HostStorageData( - storage_type=storagelib.StorageType.NFS, - address=engine_facts.default_ip(urlize=True), - path=DEFAULT_DOMAIN_PATH, - nfs_version=storagelib.NfsVersion.V4_2, - ), + host_storage_data=nfs_storage_data, ) storage_domain.wait_for_unattached_status() default_data_center.attach_storage_domain(storage_domain) diff --git a/network-suite-master/ovirtlib/storagelib.py b/network-suite-master/ovirtlib/storagelib.py index 5137be00..53db453b 100644 --- a/network-suite-master/ovirtlib/storagelib.py +++ b/network-suite-master/ovirtlib/storagelib.py @@ -3,6 +3,7 @@ # SPDX-License-Identifier: GPL-2.0-or-later # # +import abc import contextlib from ovirtsdk4 import types @@ -55,25 +56,26 @@ class NfsVersion(object): V4_2 = types.NfsVersion.V4_2 -class HostStorageData(object): - def __init__(self, storage_type, address, path, nfs_version=None, logical_units=()): +class HostStorageData(metaclass=abc.ABCMeta): + def __init__(self, storage_type, domain_type, address=None, path=None): """ - :param storage_type: string indicates the storage type. - :param address: string indicates the NFS storage address. - :param path: string indicates the NFS storage path. - :param nfs_version: string indicates the NFS storage version. - :param logical_units: tuple of LogicalUnits. - Represent logical units (luns) in case of an iSCSI storage domain. + :param storage_type: storagelib.StorageType + :param domain_type: storagelib.StorageDomainType + :param address: string indicates the storage address. + :param path: string indicates the storage path. """ - self._type = storage_type + self._storage_type = storage_type + self._domain_type = domain_type self._address = address self._path = path - self._nfs_version = nfs_version - self._logical_units = logical_units @property - def type(self): - return self._type + def storage_type(self): + return self._storage_type + + @property + def domain_type(self): + return self._domain_type @property def address(self): @@ -83,14 +85,62 @@ def address(self): def path(self): return self._path + @abc.abstractmethod + def as_sdk_type(self): + """ + :return: representation of this object as the equivalent ovirtsdk4.types object. + ovirtlib is a wrapper and encapsulator of ovirtsdk4.types and ovirtsdk4.services, + so this method should not be used outside ovirtlib + """ + + +class NfsStorageData(HostStorageData): + def __init__(self, address, path, domain_type=StorageDomainType.DATA, version=NfsVersion.V4_2): + super(NfsStorageData, self).__init__(StorageType.NFS, domain_type, address, path) + self._version = version + @property - def nfs_version(self): - return self._nfs_version + def version(self): + return self._version + + def as_sdk_type(self): + return types.HostStorage( + type=self.storage_type, + address=self.address, + path=self.path, + nfs_version=self.version, + ) + + def __repr__(self): + return ( + f'<{self.__class__.__name__}| ' + f'address:{self.address}, ' + f'path:{self.path}, ' + f'version:{self.version}>' + f'domain_type:{self.domain_type}, ' + ) + + +class IscsiStorageData(HostStorageData): + def __init__(self, domain_type=StorageDomainType.DATA, logical_units=()): + super(IscsiStorageData, self).__init__(StorageType.ISCSI, domain_type) + self._logical_units = logical_units @property def logical_units(self): return self._logical_units + def as_sdk_type(self): + return types.HostStorage( + type=self.storage_type, + address=self.address, + path=self.path, + logical_units=[lun.as_sdk_type() for lun in self.logical_units], + ) + + def __repr__(self): + return f'<{self.__class__.__name__}| logical_units:{self.logical_units}, domain_type:{self.domain_type}>' + class StorageDomainStatus(object): @@ -111,24 +161,17 @@ def status(self): def wait_for_unattached_status(self): self._wait_for_status(StorageDomainStatus.UNATTACHED) - def create(self, name, host, domain_type, host_storage_data): + def create(self, name, host, host_storage_data): """ :param name: string :param host: hostlib.Host - :param domain_type: StorageDomainType :param host_storage_data: HostStorageData """ sdk_type = types.StorageDomain( name=name, host=host.get_sdk_type(), - type=domain_type, - storage=types.HostStorage( - type=host_storage_data.type, - address=host_storage_data.address, - path=host_storage_data.path, - nfs_version=host_storage_data.nfs_version, - logical_units=self._get_sdk_type_logical_units(host_storage_data.logical_units), - ), + type=host_storage_data.domain_type, + storage=host_storage_data.as_sdk_type(), ) self._create_sdk_entity(sdk_type) @@ -180,20 +223,9 @@ def create_disk(self, name): disk.wait_for_up_status() return disk - def _get_sdk_type_logical_units(self, logical_units): - return [ - types.LogicalUnit( - id=lundata.id, - address=lundata.address, - port=lundata.port, - target=lundata.target, - ) - for lundata in logical_units - ] - def __repr__(self): return self._execute_without_raising( - lambda: (f'<{self.__class__.__name__}| ' f'name:{self.name}, ' f'status:{self.status}, ' f'id:{self.id}>') + lambda: f'<{self.__class__.__name__}| name:{self.name}, status:{self.status}, id:{self.id}>' ) @@ -233,8 +265,8 @@ def wait_for_up_status(self): class LogicalUnit(object): - def __init__(self, id, address, port, target): - self._id = id + def __init__(self, lun_id, address, port, target): + self._id = lun_id self._address = address self._port = port self._target = target @@ -255,13 +287,24 @@ def port(self): def target(self): return self._target + def as_sdk_type(self): + return types.LogicalUnit(id=self.id, address=self.address, port=self.port, target=self.target) + + def __repr__(self): + return ( + f'<{self.__class__.__name__}| ' + f'address:{self.address}, ' + f'port:{self.port}, ' + f'target:{self.target}, ' + f'lun_id:{self.id}>' + ) + @contextlib.contextmanager -def storage_domain(system, name, domain_type, host, host_storage_data): +def storage_domain(system, name, host, host_storage_data): sd = StorageDomain(system) sd.create( name=name, - domain_type=domain_type, host=host, host_storage_data=host_storage_data, ) diff --git a/network-suite-master/test-scenarios/conftest.py b/network-suite-master/test-scenarios/conftest.py index 9b4b8a18..cb2a06a8 100644 --- a/network-suite-master/test-scenarios/conftest.py +++ b/network-suite-master/test-scenarios/conftest.py @@ -30,9 +30,6 @@ from fixtures.engine import api from fixtures.engine import test_invocation_logger -from fixtures.fqdn import engine_storage_ipv6 -from fixtures.fqdn import host0_eth1_ipv6 -from fixtures.fqdn import host0_eth2_ipv6 from fixtures.fqdn import ovirt_provider_ovn_with_ip_fqdn from fixtures.storage import default_storage_domain @@ -87,6 +84,7 @@ from ost_utils.pytest.fixtures.engine import engine_ip from ost_utils.pytest.fixtures.engine import engine_ip_url from ost_utils.pytest.fixtures.engine import engine_ips_for_network +from ost_utils.pytest.fixtures.engine import engine_storage_ips from ost_utils.pytest.fixtures.engine import engine_username from ost_utils.pytest.fixtures.env import ost_images_distro from ost_utils.pytest.fixtures.env import root_dir @@ -95,6 +93,7 @@ from ost_utils.pytest.fixtures.env import working_dir from ost_utils.pytest.fixtures.network import management_network_name from ost_utils.pytest.fixtures.network import management_subnet +from ost_utils.pytest.fixtures.network import storage_network_name from ost_utils.pytest.fixtures.network import storage_subnet from ost_utils.pytest.fixtures.sdk import get_user_service_for_user from ost_utils.pytest.fixtures.sdk import system_service diff --git a/network-suite-master/test-scenarios/test_ipv6_support.py b/network-suite-master/test-scenarios/test_ipv6_support.py index 02f9e85f..044ef122 100644 --- a/network-suite-master/test-scenarios/test_ipv6_support.py +++ b/network-suite-master/test-scenarios/test_ipv6_support.py @@ -6,6 +6,10 @@ import contextlib import socket +import pytest + +from fixtures.host import ETH2 + from ovirtlib import clusterlib from ovirtlib import datacenterlib from ovirtlib import hostlib @@ -15,48 +19,39 @@ from ovirtlib import templatelib from ovirtlib import virtlib -from ovirtlib.storagelib import storage_domain from testlib import suite +SD_NAMES = { + 'nfs': 'nfs-ipv6', + 'iscsi': 'iscsi-ipv6', +} + + @suite.skip_suites_below('4.3') -@suite.xfail_suite_master('TODO') def test_non_mgmt_display_network_over_ipv6( - system, - default_data_center, - default_cluster, - host_0_up, - host0_eth1_ipv6, - host0_eth2_ipv6, - engine_storage_ipv6, + system, default_data_center, default_cluster, host_0_up, nfs_storage_data, af ): """ This test verifies that: * it is possible to create a display role over an ipv6 only network * it is possible to connect with a graphic display to a VM over this network - Note: host0_eth1_ipv6 fixture is mandatory because if there is no ipv6 - address on eth1 of the host, connection with the storage server - cannot be maintained """ - assert host0_eth1_ipv6 != '' - with netlib.new_network('ipv6-disp_net', default_data_center) as net: + if not af.is6: + return pytest.mark.skip(reason='ipv6 specific test') + with netlib.new_network('ipv6-display-net', default_data_center) as net: with clusterlib.network_assignment(default_cluster, net) as cl_net: cl_net.set_usages((netlib.NetworkUsage.DISPLAY,)) - v6_no_gw = netattachlib.StaticIpv6Assignment(addr=host0_eth2_ipv6, prefix='64') - attach_data = netattachlib.NetworkAttachmentData(net, 'eth2', (netattachlib.NO_V4, v6_no_gw)) + attach_data = netattachlib.NetworkAttachmentData( + net, ETH2, (netattachlib.NO_V4, netattachlib.IPV6_POLY_DHCP_AUTOCONF) + ) with hostlib.setup_networks(host_0_up, (attach_data,)): host_0_up.wait_for_networks_in_sync() VM0 = 'vm_non_mgmt_display_net_over_ipv6' DSK = 'disk_non_mgmt_display_net_over_ipv6' with vm_powering_up( - system, - default_data_center, - default_cluster, - host_0_up, - engine_storage_ipv6, - VM0, - DSK, + system, default_data_center, default_cluster, host_0_up, nfs_storage_data, VM0, DSK ) as vm: _try_spice_console_connect(vm) @@ -73,111 +68,60 @@ def _try_spice_console_connect(vm): @suite.xfail_suite_master('iSCSI not yet working on RHEL8') def test_run_vm_over_ipv6_iscsi_storage_domain( - system, - default_data_center, - default_cluster, - host_0_up, - engine_storage_ipv6, - lun_id, + system, default_data_center, default_cluster, host_0_up, iscsi_storage_data, af ): """ This test verifies that: * it is possible to create an iSCSI storage domain over an ipv6 network * it is possible to power up a VM over such a storage domain """ + if not af.is6: + return pytest.mark.skip(reason='ipv6 specific test') VM0 = 'vm_over_iscsi_ipv6_storage_domain' DSK = 'disk_over_iscsi_ipv6_storage_domain' - with ipv6_iscsi_storage_domain(system, host_0_up, engine_storage_ipv6, lun_id) as sd: + with storagelib.storage_domain(system, SD_NAMES['iscsi'], host_0_up, iscsi_storage_data) as sd: with datacenterlib.attached_storage_domain(default_data_center, sd) as sd_attached: with vm_down(system, default_cluster, sd_attached, VM0, DSK) as vm: vm.run() vm.wait_for_powering_up_status() -@suite.xfail_suite_master('depends on https://gerrit.ovirt.org/#/c/103385/') def test_run_vm_over_ipv6_nfs_storage_domain( - system, - default_data_center, - default_cluster, - host_0_up, - engine_storage_ipv6, + system, default_data_center, default_cluster, host_0_up, nfs_storage_data, af ): """ This test verifies that: * it is possible to create an NFS storage domain over an ipv6 network * it is possible to power up a VM over such a storage domain """ + if not af.is6: + return pytest.mark.skip(reason='ipv6 specific test') VM0 = 'vm_over_nfs_ipv6_storage_domain' DSK = 'disk_over_nfs_ipv6_storage_domain' - with ipv6_nfs_storage_domain(system, host_0_up, engine_storage_ipv6) as sd: + with storagelib.storage_domain(system, SD_NAMES['nfs'], host_0_up, nfs_storage_data) as sd: with datacenterlib.attached_storage_domain(default_data_center, sd) as sd_attached: with vm_down(system, default_cluster, sd_attached, VM0, DSK) as vm: vm.run() vm.wait_for_powering_up_status() -@contextlib.contextmanager -def ipv6_nfs_storage_domain(system, host, engine_storage_ipv6): - DOMAIN_NAME = 'nfs-ipv6' - DEFAULT_DOMAIN_PATH = '/exports/nfs/share2' - - sd = storagelib.StorageDomain(system) - host_storage_data = storagelib.HostStorageData( - storage_type=storagelib.StorageType.NFS, - address='[' + engine_storage_ipv6 + ']', - path=DEFAULT_DOMAIN_PATH, - nfs_version=storagelib.NfsVersion.V4_2, - ) - - with storage_domain( - system, - DOMAIN_NAME, - storagelib.StorageDomainType.DATA, - host, - host_storage_data, - ) as sd: - yield sd +@pytest.fixture(scope='module') +def nfs_storage_data(engine_storage_ips): + return storagelib.NfsStorageData(f'[{engine_storage_ips[0]}]', '/exports/nfs/share2') -@contextlib.contextmanager -def ipv6_iscsi_storage_domain(system, host, engine_storage_ipv6, lun_id): - DOMAIN_NAME = 'iscsi-ipv6' - ISCSI_ADDRESS = engine_storage_ipv6 - ISCSI_PORT = 3260 - ISCSI_TARGET = 'iqn.2014-07.org.ovirt:storage' - +@pytest.fixture(scope='module') +def iscsi_storage_data(lun_id, engine_storage_ips): lun = storagelib.LogicalUnit( - id=lun_id, - address=ISCSI_ADDRESS, - port=ISCSI_PORT, - target=ISCSI_TARGET, - ) - - host_storage_data = storagelib.HostStorageData( - storage_type=storagelib.StorageType.ISCSI, - address=None, - path=None, - logical_units=(lun,), + lun_id=lun_id, address=engine_storage_ips[0], port=3260, target='iqn.2014-07.org.ovirt:storage' ) - - with storage_domain( - system, - DOMAIN_NAME, - storagelib.StorageDomainType.DATA, - host, - host_storage_data, - ) as sd: - yield sd + return storagelib.IscsiStorageData(logical_units=(lun,)) @contextlib.contextmanager def vm_down(system, default_cluster, storage_domain, vm_name, disk_name): with virtlib.vm_pool(system, size=1) as (vm,): - vm.create( - vm_name=vm_name, - cluster=default_cluster, - template=templatelib.TEMPLATE_BLANK, - ) + vm.create(vm_name=vm_name, cluster=default_cluster, template=templatelib.TEMPLATE_BLANK) disk = storage_domain.create_disk(disk_name) disk_att_id = vm.attach_disk(disk=disk) vm.wait_for_disk_up_status(disk, disk_att_id) @@ -186,16 +130,8 @@ def vm_down(system, default_cluster, storage_domain, vm_name, disk_name): @contextlib.contextmanager -def vm_powering_up( - system, - default_data_center, - default_cluster, - host, - engine_storage_ipv6, - vm_name, - disk_name, -): - with ipv6_nfs_storage_domain(system, host, engine_storage_ipv6) as sd: +def vm_powering_up(system, default_data_center, default_cluster, host, nfs_storage_data, vm_name, disk_name): + with storagelib.storage_domain(system, SD_NAMES['nfs'], host, nfs_storage_data) as sd: with datacenterlib.attached_storage_domain(default_data_center, sd) as sd_attached: with vm_down(system, default_cluster, sd_attached, vm_name, disk_name) as vm: vm.run()