Skip to content

Commit fe7ba92

Browse files
committed
Add few bond tests
- add some tests for Bond - add new abstractions (Network and Bond) with fixtures Signed-off-by: Sebastien Marie <semarie@kapouay.eu.org>
1 parent 8cb5751 commit fe7ba92

File tree

9 files changed

+355
-3
lines changed

9 files changed

+355
-3
lines changed

jobs.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -468,6 +468,8 @@
468468
BROKEN_TESTS = [
469469
# not really broken but has complex prerequisites (3 NICs on 3 different networks)
470470
"tests/migration/test_host_evacuate.py::TestHostEvacuateWithNetwork",
471+
# not really broken but has complex prerequisites (3 NICs)
472+
"tests/network/test_bond.py::test_bond",
471473
# running quicktest on zfsvol generates dangling TAP devices that are hard to
472474
# cleanup. Bug needs to be fixed before enabling quicktest on zfsvol.
473475
"tests/storage/zfsvol/test_zfsvol_sr.py::TestZfsvolVm::test_quicktest",

lib/bond.py

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
from __future__ import annotations
2+
3+
import logging
4+
5+
from lib.common import _param_add, _param_clear, _param_get, _param_remove, _param_set, safe_split
6+
from lib.pif import PIF
7+
8+
from typing import TYPE_CHECKING
9+
10+
if TYPE_CHECKING:
11+
from lib.host import Host
12+
13+
class Bond:
14+
xe_prefix = "bond"
15+
16+
def __init__(self, host: Host, uuid: str):
17+
self.host = host
18+
self.uuid = uuid
19+
20+
def param_get(self, param_name, key=None, accept_unknown_key=False):
21+
return _param_get(self.host, Bond.xe_prefix, self.uuid, param_name, key, accept_unknown_key)
22+
23+
def param_set(self, param_name, value, key=None):
24+
_param_set(self.host, Bond.xe_prefix, self.uuid, param_name, value, key)
25+
26+
def param_add(self, param_name, value, key=None):
27+
_param_add(self.host, Bond.xe_prefix, self.uuid, param_name, value, key)
28+
29+
def param_clear(self, param_name):
30+
_param_clear(self.host, Bond.xe_prefix, self.uuid, param_name)
31+
32+
def param_remove(self, param_name, key, accept_unknown_key=False):
33+
_param_remove(self.host, Bond.xe_prefix, self.uuid, param_name, key, accept_unknown_key)
34+
35+
def destroy(self):
36+
logging.info(f"Destroying bond: {self.uuid}")
37+
self.host.xe('bond-destroy', {'uuid': self.uuid})
38+
39+
def master(self) -> PIF:
40+
uuid = self.param_get('master')
41+
assert uuid is not None, "no master on Bond"
42+
return PIF(uuid, self.host)
43+
44+
def slaves(self) -> list[str]:
45+
return safe_split(self.param_get('slaves'))
46+
47+
def mode(self) -> str | None:
48+
return self.param_get("mode")

lib/common.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ class PackageManagerEnum(Enum):
5151
UNKNOWN = 1
5252
RPM = 2
5353
APT_GET = 3
54+
APK = 4
5455

5556
# Common VM images used in tests
5657
def vm_image(vm_key):

lib/host.py

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
if TYPE_CHECKING:
1717
from lib.pool import Pool
1818

19+
from lib.bond import Bond
1920
from lib.common import (
2021
DiskDevName,
2122
_param_add,
@@ -32,6 +33,7 @@
3233
wait_for_not,
3334
)
3435
from lib.netutil import wrap_ip
36+
from lib.network import Network
3537
from lib.pif import PIF
3638
from lib.sr import SR
3739
from lib.vdi import VDI
@@ -768,3 +770,41 @@ def lvs(self, vgName: Optional[str] = None, ignore_MGT: bool = True) -> List[str
768770
continue
769771
ret.append(line.strip())
770772
return ret
773+
774+
def pifs(self, device: Optional[str] = None) -> list[PIF]:
775+
args: Dict[str, str | bool] = {
776+
"host-uuid": self.uuid,
777+
}
778+
779+
if device is not None:
780+
args["device"] = device
781+
782+
return [PIF(uuid, self) for uuid in safe_split(self.xe("pif-list", args, minimal=True))]
783+
784+
def create_bond(self, network: Network, pifs: list[PIF], mode: Optional[str] = None) -> Bond:
785+
args: dict[str, str | bool] = {
786+
'network-uuid': network.uuid,
787+
'pif-uuids': ','.join([pif.uuid for pif in pifs]),
788+
}
789+
790+
if mode is not None:
791+
args['mode'] = mode
792+
793+
uuid = self.xe("bond-create", args, minimal=True)
794+
logging.info(f"New Bond: {uuid}")
795+
796+
return Bond(self, uuid)
797+
798+
def create_network(self, label: str, description: Optional[str] = None) -> Network:
799+
args: dict[str, str | bool] = {
800+
'name-label': label,
801+
}
802+
803+
if description is not None:
804+
args['name-description'] = description
805+
806+
logging.info(f"Creating network '{label}'")
807+
uuid = self.xe("network-create", args, minimal=True)
808+
logging.info(f"New Network: {uuid}")
809+
810+
return Network(self, uuid)

lib/network.py

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
from __future__ import annotations
2+
3+
import logging
4+
5+
from lib.common import _param_add, _param_clear, _param_get, _param_remove, _param_set, safe_split
6+
7+
from typing import TYPE_CHECKING
8+
9+
if TYPE_CHECKING:
10+
from lib.host import Host
11+
12+
class Network:
13+
xe_prefix = "network"
14+
15+
def __init__(self, host: Host, uuid: str):
16+
self.host = host
17+
self.uuid = uuid
18+
19+
def param_get(self, param_name, key=None, accept_unknown_key=False):
20+
return _param_get(self.host, Network.xe_prefix, self.uuid, param_name, key, accept_unknown_key)
21+
22+
def param_set(self, param_name, value, key=None):
23+
_param_set(self.host, Network.xe_prefix, self.uuid, param_name, value, key)
24+
25+
def param_add(self, param_name, value, key=None):
26+
_param_add(self.host, Network.xe_prefix, self.uuid, param_name, value, key)
27+
28+
def param_clear(self, param_name):
29+
_param_clear(self.host, Network.xe_prefix, self.uuid, param_name)
30+
31+
def param_remove(self, param_name, key, accept_unknown_key=False):
32+
_param_remove(self.host, Network.xe_prefix, self.uuid, param_name, key, accept_unknown_key)
33+
34+
def destroy(self):
35+
logging.info(f"Destroying network '{self.param_get('name-label')}': {self.uuid}")
36+
self.host.xe('network-destroy', {'uuid': self.uuid})
37+
38+
def pif_uuids(self) -> list[str]:
39+
return safe_split(self.param_get('PIF-uuids'), '; ')
40+
41+
def vif_uuids(self) -> list[str]:
42+
return safe_split(self.param_get('VIF-uuids'), '; ')
43+
44+
def is_private(self) -> bool:
45+
return len(self.pif_uuids()) == 0
46+
47+
def managed(self) -> bool:
48+
return self.param_get('managed') == 'true'
49+
50+
def MTU(self) -> int:
51+
return int(self.param_get('MTU') or '0')

lib/pif.py

Lines changed: 38 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,16 @@
1-
from lib.common import _param_add, _param_clear, _param_get, _param_remove, _param_set
1+
from __future__ import annotations
2+
3+
from lib.common import _param_add, _param_clear, _param_get, _param_remove, _param_set, strtobool
4+
5+
from typing import TYPE_CHECKING
6+
7+
if TYPE_CHECKING:
8+
from lib.host import Host
29

310
class PIF:
411
xe_prefix = "pif"
512

6-
def __init__(self, uuid, host):
13+
def __init__(self, uuid: str, host: Host):
714
self.uuid = uuid
815
self.host = host
916

@@ -26,3 +33,32 @@ def param_add(self, param_name, value, key=None):
2633
def param_clear(self, param_name):
2734
_param_clear(self.host, self.xe_prefix, self.uuid,
2835
param_name)
36+
37+
def is_managed(self) -> bool:
38+
return strtobool(self.param_get("managed"))
39+
40+
def is_physical(self) -> bool:
41+
return strtobool(self.param_get("physical"))
42+
43+
def is_currently_attached(self) -> bool:
44+
return strtobool(self.param_get("currently-attached"))
45+
46+
def is_management(self) -> bool:
47+
return strtobool(self.param_get("management"))
48+
49+
def network_uuid(self) -> str:
50+
uuid = self.param_get("network-uuid")
51+
assert uuid is not None, "unexpected PIF without network-uuid"
52+
return uuid
53+
54+
def reconfigure_ip(self, mode: str) -> None:
55+
self.host.xe("pif-reconfigure-ip", {
56+
"uuid": self.uuid,
57+
"mode": mode,
58+
})
59+
60+
def reconfigure_ipv6(self, mode: str) -> None:
61+
self.host.xe("pif-reconfigure-ipv6", {
62+
"uuid": self.uuid,
63+
"mode": mode,
64+
})

lib/vm.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -478,6 +478,8 @@ def detect_package_manager(self):
478478
return PackageManagerEnum.RPM
479479
elif self.file_exists('/usr/bin/apt-get'):
480480
return PackageManagerEnum.APT_GET
481+
elif self.file_exits('/sbin/apk'):
482+
return PackageManagerEnum.APK
481483
else:
482484
return PackageManagerEnum.UNKNOWN
483485

tests/network/conftest.py

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,47 @@
11
import pytest
22

3+
import logging
4+
5+
from lib.host import Host
6+
from lib.network import Network
7+
8+
from typing import Generator
9+
310
@pytest.fixture(scope='package')
4-
def host_no_sdn_controller(host):
11+
def host_no_sdn_controller(host: Host):
512
""" An XCP-ng with no SDN controller. """
613
if host.xe('sdn-controller-list', minimal=True):
714
pytest.skip("This test requires an XCP-ng with no SDN controller")
15+
16+
17+
@pytest.fixture(scope='module')
18+
def empty_network(host: Host) -> Generator[Network, None, None]:
19+
try:
20+
net = host.create_network(label="empty_network for tests")
21+
yield net
22+
finally:
23+
if net is not None:
24+
net.destroy()
25+
26+
@pytest.fixture(params=[])
27+
def bond_devices(request: pytest.FixtureRequest) -> list[str]:
28+
return request.param
29+
30+
@pytest.fixture(params=["lacp"])
31+
def bond_mode(request: pytest.FixtureRequest) -> str:
32+
return request.param
33+
34+
@pytest.fixture
35+
def bond(host: Host, empty_network: Network, bond_devices: list[str], bond_mode: str):
36+
pifs = []
37+
logging.info(f"bond: resolve PIFs on {host.hostname_or_ip} using \
38+
{[(pif.network_uuid(), pif.param_get('device')) for pif in host.pifs()]}")
39+
for name in bond_devices:
40+
[pif] = host.pifs(device=name)
41+
pifs.append(pif)
42+
43+
bond = host.create_bond(empty_network, pifs, mode=bond_mode)
44+
try:
45+
yield bond
46+
finally:
47+
bond.destroy()

0 commit comments

Comments
 (0)