Skip to content

Commit 27fd8c7

Browse files
committed
DPDK: Allow arbitrary rdma-core source build
Fulfilling an ask to be able to test multiple rdma-core versions to allow development and regression tests. Setting rdma_core_source to either a git or tar.gz link allows you to build from source. For git either set rdma_core_ref or it will pick the latest version sorted tag.
1 parent 44c42cf commit 27fd8c7

File tree

4 files changed

+294
-94
lines changed

4 files changed

+294
-94
lines changed

lisa/util/__init__.py

Lines changed: 58 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
Union,
2525
cast,
2626
)
27+
from urllib.parse import urlparse
2728

2829
import paramiko
2930
import pluggy
@@ -47,7 +48,7 @@
4748
# source -
4849
# https://github.com/django/django/blob/stable/1.3.x/django/core/validators.py#L45
4950
__url_pattern = re.compile(
50-
r"^(?:http|ftp)s?://" # http:// or https://
51+
r"^(?:http|s?ftp)s?://" # http:// or https://
5152
r"(?:(?:[A-Z0-9](?:[A-Z0-9-]{0,61}[A-Z0-9])?\.)"
5253
r"+(?:[A-Z]{2,6}\.?|[A-Z0-9-]{2,}\.?)|" # ...domain
5354
r"localhost|" # localhost...
@@ -563,7 +564,9 @@ def find_group_in_lines(
563564
return result
564565

565566

566-
def deep_update_dict(src: Dict[str, Any], dest: Dict[str, Any]) -> Dict[str, Any]:
567+
def deep_update_dict(
568+
src: Dict[str, Any], dest: Optional[Dict[str, Any]]
569+
) -> Dict[str, Any]:
567570
if (
568571
dest is None
569572
or isinstance(dest, int)
@@ -577,7 +580,7 @@ def deep_update_dict(src: Dict[str, Any], dest: Dict[str, Any]) -> Dict[str, Any
577580

578581
if isinstance(result, dict):
579582
for key, value in src.items():
580-
if isinstance(value, dict) and key in dest:
583+
if dest and isinstance(value, dict) and key in dest:
581584
value = deep_update_dict(value, dest[key])
582585
result[key] = value
583586
elif isinstance(src, dict):
@@ -598,6 +601,58 @@ def is_valid_url(url: str, raise_error: bool = True) -> bool:
598601
return is_url
599602

600603

604+
def is_valid_source_code_package(
605+
source_url: str,
606+
expected_package_name_pattern: Pattern[str],
607+
allowed_protocols: Optional[List[str]] = None,
608+
expected_domains: Optional[List[str]] = None,
609+
) -> bool:
610+
# avoid using a mutable default parameter
611+
if not allowed_protocols:
612+
allowed_protocols = [
613+
"https",
614+
"sftp",
615+
]
616+
# first, check if it's a url.
617+
if not is_valid_url(url=source_url, raise_error=False):
618+
return False
619+
620+
# NOTE: urllib might not work as you'd expect.
621+
# It doesn't throw on lots of things you wouldn't expect to be urls.
622+
# You must verify the parts on your own, some of them may be empty, some null.
623+
# check: https://docs.python.org/3/library/urllib.parse.html#url-parsing
624+
625+
try:
626+
parts = urlparse(source_url)
627+
except ValueError:
628+
return False
629+
630+
# ex: from https://www.com/path/to/file.tar
631+
# scheme : https
632+
# netloc : www.com
633+
# path : path/to/file.tar
634+
635+
# get the filename from the path portion of the url
636+
file_path = parts.path.split("/")[-1]
637+
full_match = expected_package_name_pattern.match(file_path)
638+
if not full_match:
639+
return False
640+
641+
# check the expected domain is correct if present
642+
valid_netloc = not expected_domains or any(
643+
[domain.endswith(parts.netloc) for domain in expected_domains]
644+
)
645+
646+
# optional but default is check access is via sftp/https
647+
valid_scheme = any([parts.scheme == x for x in allowed_protocols])
648+
return (
649+
valid_scheme
650+
and parts.netloc != ""
651+
and valid_netloc
652+
and (full_match.group(0) == file_path)
653+
)
654+
655+
601656
def filter_ansi_escape(content: str) -> str:
602657
return __ansi_escape.sub("", content)
603658

microsoft/testsuites/dpdk/dpdktestpmd.py

Lines changed: 49 additions & 91 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import re
55
from pathlib import PurePosixPath
66
from typing import Any, List, Tuple, Type, Union
7+
from urllib.parse import urlparse
78

89
from assertpy import assert_that, fail
910
from semver import VersionInfo
@@ -19,7 +20,6 @@
1920
Kill,
2021
Lscpu,
2122
Lspci,
22-
Make,
2323
Modprobe,
2424
Pidof,
2525
Pkgconfig,
@@ -40,6 +40,7 @@
4040
is_ubuntu_latest_or_prerelease,
4141
is_ubuntu_lts_version,
4242
)
43+
from microsoft.testsuites.dpdk.rdma_core import RdmaCoreManager
4344

4445
PACKAGE_MANAGER_SOURCE = "package_manager"
4546

@@ -136,22 +137,6 @@ def command(self) -> str:
136137
_rx_pps_key: r"Rx-pps:\s+([0-9]+)",
137138
}
138139

139-
def get_rdma_core_package_name(self) -> str:
140-
distro = self.node.os
141-
package = ""
142-
# check if rdma-core is installed already...
143-
if self.node.tools[Pkgconfig].package_info_exists("libibuverbs"):
144-
return package
145-
if isinstance(distro, Debian):
146-
package = "rdma-core ibverbs-providers libibverbs-dev"
147-
elif isinstance(distro, Suse):
148-
package = "rdma-core-devel librdmacm1"
149-
elif isinstance(distro, Fedora):
150-
package = "librdmacm-devel"
151-
else:
152-
fail("Invalid OS for rdma-core source installation.")
153-
return package
154-
155140
@property
156141
def can_install(self) -> bool:
157142
for _os in [Debian, Fedora, Suse]:
@@ -465,8 +450,17 @@ def add_sample_apps_to_build_list(self, apps: Union[List[str], None]) -> None:
465450

466451
def __init__(self, *args: Any, **kwargs: Any) -> None:
467452
super().__init__(*args, **kwargs)
453+
# set source args for builds if needed, first for dpdk
468454
self._dpdk_source = kwargs.pop("dpdk_source", PACKAGE_MANAGER_SOURCE)
469455
self._dpdk_branch = kwargs.pop("dpdk_branch", "main")
456+
# then for rdma-core
457+
rdma_core_source = kwargs.pop("rdma_core_source", "")
458+
rdma_core_ref = kwargs.pop("rdma_core_ref", "")
459+
self.rdma_core = RdmaCoreManager(
460+
node=self.node,
461+
rdma_core_source=rdma_core_source,
462+
rdma_core_ref=rdma_core_ref,
463+
)
470464
self._sample_apps_to_build = kwargs.pop("sample_apps", [])
471465
self._dpdk_version_info = VersionInfo(0, 0)
472466
self._testpmd_install_path: str = ""
@@ -522,54 +516,6 @@ def _check_pps_data(self, rx_or_tx: str) -> None:
522516
f"empty or all zeroes for dpdktestpmd.{rx_or_tx.lower()}_pps_data."
523517
).is_true()
524518

525-
def _install_upstream_rdma_core_for_mana(self) -> None:
526-
node = self.node
527-
wget = node.tools[Wget]
528-
make = node.tools[Make]
529-
tar = node.tools[Tar]
530-
distro = node.os
531-
532-
if isinstance(distro, Debian):
533-
distro.install_packages(
534-
"cmake libudev-dev "
535-
"libnl-3-dev libnl-route-3-dev ninja-build pkg-config "
536-
"valgrind python3-dev cython3 python3-docutils pandoc "
537-
"libssl-dev libelf-dev python3-pip libnuma-dev"
538-
)
539-
elif isinstance(distro, Fedora):
540-
distro.group_install_packages("Development Tools")
541-
distro.install_packages(
542-
"cmake gcc libudev-devel "
543-
"libnl3-devel pkg-config "
544-
"valgrind python3-devel python3-docutils "
545-
"openssl-devel unzip "
546-
"elfutils-devel python3-pip libpcap-devel "
547-
"tar wget dos2unix psmisc kernel-devel-$(uname -r) "
548-
"librdmacm-devel libmnl-devel kernel-modules-extra numactl-devel "
549-
"kernel-headers elfutils-libelf-devel meson ninja-build libbpf-devel "
550-
)
551-
else:
552-
# check occcurs before this function
553-
return
554-
555-
tar_path = wget.get(
556-
url=(
557-
"https://github.com/linux-rdma/rdma-core/"
558-
"releases/download/v46.0/rdma-core-46.0.tar.gz"
559-
),
560-
file_path=str(node.working_path),
561-
)
562-
563-
tar.extract(tar_path, dest_dir=str(node.working_path), gzip=True, sudo=True)
564-
source_path = node.working_path.joinpath("rdma-core-46.0")
565-
node.execute(
566-
"cmake -DIN_PLACE=0 -DNO_MAN_PAGES=1 -DCMAKE_INSTALL_PREFIX=/usr",
567-
shell=True,
568-
cwd=source_path,
569-
sudo=True,
570-
)
571-
make.make_install(source_path)
572-
573519
def _set_backport_repo_args(self) -> None:
574520
distro = self.node.os
575521
# skip attempting to use backports for latest/prerlease
@@ -597,6 +543,18 @@ def _install(self) -> bool:
597543
self._testpmd_output_during_rescind = ""
598544
self._last_run_output = ""
599545
node = self.node
546+
distro = node.os
547+
if not (
548+
isinstance(distro, Fedora)
549+
or isinstance(distro, Debian)
550+
or isinstance(distro, Suse)
551+
):
552+
raise SkippedException(
553+
UnsupportedDistroException(
554+
distro, "DPDK tests not implemented for this OS."
555+
)
556+
)
557+
600558
# before doing anything: determine if backport repo needs to be enabled
601559
self._set_backport_repo_args()
602560

@@ -608,42 +566,50 @@ def _install(self) -> bool:
608566
self._load_drivers_for_dpdk()
609567
return True
610568

611-
# otherwise, install from package manager, git, or tar
612-
613-
self._install_dependencies()
569+
# if this is mana VM, we don't support other distros yet
570+
is_mana_test_supported = isinstance(distro, Ubuntu) or isinstance(
571+
distro, Fedora
572+
)
573+
if self.is_mana and not is_mana_test_supported:
574+
raise SkippedException("MANA DPDK test is not supported on this OS")
614575

615-
# if this is mana VM, we need an upstream rdma-core package (for now)
616-
if self.is_mana:
617-
if not (isinstance(node.os, Ubuntu) or isinstance(node.os, Fedora)):
618-
raise SkippedException("MANA DPDK test is not supported on this OS")
576+
# if we need an rdma-core source install, do it now.
577+
if self.rdma_core.can_install_from_source() or (
578+
is_mana_test_supported and self.is_mana
579+
):
580+
# ensure no older version is installed
581+
distro.uninstall_packages("rdma-core")
582+
self.rdma_core.do_source_install()
619583

620-
# ensure no older dependency is installed
621-
node.os.uninstall_packages("rdma-core")
622-
self._install_upstream_rdma_core_for_mana()
584+
# otherwise, install kernel and dpdk deps from package manager, git, or tar
585+
self._install_dependencies()
586+
# install any missing rdma-core packages
587+
rdma_packages = self.rdma_core.get_missing_distro_packages()
588+
distro.install_packages(rdma_packages)
623589

624590
# installing from distro package manager
625591
if self.use_package_manager_install():
626592
self.node.log.info(
627593
"Installing dpdk and dev package from package manager..."
628594
)
629-
if isinstance(node.os, Debian):
630-
node.os.install_packages(
595+
if isinstance(distro, Debian):
596+
distro.install_packages(
631597
["dpdk", "dpdk-dev"],
632598
extra_args=self._backport_repo_args,
633599
)
634-
elif isinstance(node.os, (Fedora, Suse)):
635-
node.os.install_packages(["dpdk", "dpdk-devel"])
600+
elif isinstance(distro, (Fedora, Suse)):
601+
distro.install_packages(["dpdk", "dpdk-devel"])
636602
else:
637603
raise NotImplementedError(
638604
"Dpdk package names are missing in dpdktestpmd.install"
639-
f" for os {node.os.name}"
605+
f" for os {distro.name}"
640606
)
641607
self.node.log.info(
642608
f"Installed DPDK version {str(self._dpdk_version_info)} "
643609
"from package manager"
644610
)
645611

646-
self._dpdk_version_info = node.os.get_package_information("dpdk")
612+
self._dpdk_version_info = distro.get_package_information("dpdk")
647613
self.find_testpmd_binary()
648614
self._load_drivers_for_dpdk()
649615
return True
@@ -867,9 +833,6 @@ def _install_suse_dependencies(self) -> None:
867833
suse.install_packages(self._suse_packages)
868834
if not self.use_package_manager_install():
869835
self._install_ninja_and_meson()
870-
rdma_core_packages = self.get_rdma_core_package_name()
871-
if rdma_core_packages:
872-
suse.install_packages(rdma_core_packages.split())
873836

874837
def _install_ubuntu_dependencies(self) -> None:
875838
node = self.node
@@ -904,9 +867,6 @@ def _install_ubuntu_dependencies(self) -> None:
904867
# MANA tests use linux-modules-extra-azure, install if it's available.
905868
if self.is_mana and ubuntu.is_package_in_repo("linux-modules-extra-azure"):
906869
ubuntu.install_packages("linux-modules-extra-azure")
907-
rdma_core_packages = self.get_rdma_core_package_name()
908-
if rdma_core_packages:
909-
ubuntu.install_packages(rdma_core_packages.split())
910870

911871
def _install_fedora_dependencies(self) -> None:
912872
node = self.node
@@ -919,7 +879,7 @@ def _install_fedora_dependencies(self) -> None:
919879
return # appease the type checker
920880

921881
# DPDK is very sensitive to rdma-core/kernel mismatches
922-
# update to latest kernel before instaling dependencies
882+
# update to latest kernel before installing dependencies
923883
rhel.install_packages("kernel")
924884
node.reboot()
925885

@@ -935,9 +895,7 @@ def _install_fedora_dependencies(self) -> None:
935895

936896
# RHEL 8 doesn't require special cases for installed packages.
937897
# TODO: RHEL9 may require updates upon release
938-
rdma_core_packages = self.get_rdma_core_package_name()
939-
if rdma_core_packages:
940-
self._fedora_packages += rdma_core_packages.split()
898+
if not self.rdma_core.is_installed_from_source:
941899
rhel.group_install_packages("Infiniband Support")
942900

943901
rhel.group_install_packages("Development Tools")

microsoft/testsuites/dpdk/dpdkutil.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -312,6 +312,8 @@ def initialize_node_resources(
312312
_set_forced_source_by_distro(node, variables)
313313
dpdk_source = variables.get("dpdk_source", PACKAGE_MANAGER_SOURCE)
314314
dpdk_branch = variables.get("dpdk_branch", "")
315+
rdma_core_source = variables.get("rdma_core_source", "")
316+
rdma_core_ref = variables.get("rdma_core_git_ref", "")
315317
force_net_failsafe_pmd = variables.get("dpdk_force_net_failsafe_pmd", False)
316318
log.info(
317319
"Dpdk initialize_node_resources running"
@@ -348,6 +350,8 @@ def initialize_node_resources(
348350
dpdk_branch=dpdk_branch,
349351
sample_apps=sample_apps,
350352
force_net_failsafe_pmd=force_net_failsafe_pmd,
353+
rdma_core_source=rdma_core_source,
354+
rdma_core_ref=rdma_core_ref,
351355
)
352356

353357
# init and enable hugepages (required by dpdk)

0 commit comments

Comments
 (0)