Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,7 @@ Constructor:
* `is_package_installed_via_rpm(self, package: str, cwd: "Path | str | None" = None) -> bool` - verify if package installed using rpm
* `is_package_installed_via_dpkg(self, package: str, cwd: "Path | str | None" = None) -> bool`- verify if package installed using dpkg
* `install_package_via_dnf(self, package: str, cwd: "Path | str | None" = None) -> "ConnectionCompletedProcess":` - install package using dnf
* `install_rdma_drivers(self, build_path: "Path | str") -> None:` - install RDMA drivers from build path.

#### ESXiPackageManager:
* `get_driver_info(self, interface_name: str) -> DriverInfo` - Get driver info (name, version).
Expand Down Expand Up @@ -143,6 +144,7 @@ Constructor:
* `_get_inf_device_section_name(self, build: str, section_dictionary: Dict[str, List[str]], component_id: str, client_os: bool) -> str` get the section name from the parsed section dictionary corresponding to the specified component id
* `_get_server_or_client_section(self, section_dictionary: Dict[str, List[str]], client_os: bool) -> List[str]` get all the component ids from the parsed section dictionary for either the server or client OS section
* `_get_default_vals_from_inf(self, device_section_name: str, section_dictionary: Dict[str, List[str]]) -> List[Tuple[str, str]]` get all advanced feature names and the default value for each feature
* `install_rdma_drivers(self, build_path: "Path | str") -> None:` - install RDMA drivers from build path.

#### BSDPackageManager:
* API from Unix Package Manager
Expand Down
3 changes: 3 additions & 0 deletions examples/linux_example.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,3 +51,6 @@
package_manager.is_package_installed_via_rpm(package="make", cwd="/home/user")
package_manager.is_package_installed_via_dpkg(package="docker")
package_manager.install_package_via_dnf("1.2.3", cwd="/home")

# install rdma drivers from build path
package_manager.install_rdma_drivers("/home/rdma_drivers/build")
3 changes: 3 additions & 0 deletions examples/windows_example.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,3 +31,6 @@

package_manager.install_build(r"C:\Users\admin\Downloads\drivers", device_id)
package_manager.install_build(r"C:\Users\admin\Downloads\drivers") # will install on all devices

# install rdma drivers from build path
package_manager.install_rdma_drivers(r"C:\Users\admin\Downloads\drivers")
8 changes: 8 additions & 0 deletions mfd_package_manager/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -327,3 +327,11 @@ def _verify_pip_parameters(
level=log_levels.MODULE_DEBUG,
msg="Connection interpreter is not available for other connection than Python (RPyC and Local)",
)

def install_rdma_drivers(self) -> None:
"""
Install RDMA drivers if not installed.

Comment thread
adrianlasota marked this conversation as resolved.
Resolved
:raises PackageManagerModuleException: on failure
"""
raise NotImplementedError("Method install_rdma_drivers is not implemented for base PackageManager class.")
50 changes: 50 additions & 0 deletions mfd_package_manager/linux.py
Original file line number Diff line number Diff line change
Expand Up @@ -741,3 +741,53 @@ def install_package_via_zypper(self, package: str, cwd: "Path | str | None" = No
stderr_to_stdout=True,
cwd=cwd,
)

def install_rdma_drivers(self, build_path: "Path | str") -> None:
"""
Install RDMA drivers.

tar -zxvf irdma-X.X.X.tgz
cd irdma-X.X.X
./build.sh
./build_core.sh
./install_core.sh

:param build_path: Path to build
"""
if isinstance(build_path, str):
build_path = self._controller_connection.path(build_path)
if not build_path.exists():
raise PackageManagerNotFoundException(f"Build path {build_path} does not exist.")
build_path = build_path / "RDMA" / "Linux"
irdma_tar_files = list(build_path.glob("irdma-*.tgz"))
if not irdma_tar_files:
raise PackageManagerNotFoundException("No irdma tar files found in build path.")
if len(irdma_tar_files) > 1:
raise PackageManagerModuleException("Multiple irdma tar files found in build path.")
irdma_tar_file = irdma_tar_files[0]
remote_irdma_tar = self._connection.path(f"/tmp/{irdma_tar_file.name}")
copy(
src_conn=self._controller_connection,
dst_conn=self._connection,
source=irdma_tar_file,
target=remote_irdma_tar,
)
self._connection.execute_command(
f"tar -zxvf {remote_irdma_tar} -C /tmp/ --no-same-owner", expected_return_codes={0}, shell=True
)
irdma_dir_name = irdma_tar_file.name.replace(".tgz", "")
remote_irdma_dir = self._connection.path(f"/tmp/{irdma_dir_name}")
logger.log(level=log_levels.MODULE_DEBUG, msg="Building and installing RDMA drivers.")
self._connection.execute_command("./build.sh", expected_return_codes={0}, shell=True, cwd=remote_irdma_dir)
logger.log(level=log_levels.MODULE_DEBUG, msg="Building RDMA core drivers.")
self._connection.execute_command(
f"sh -c '{remote_irdma_dir}/build_core.sh -y'",
expected_return_codes={0},
shell=True,
stderr_to_stdout=True,
)
logger.log(level=log_levels.MODULE_DEBUG, msg="Installing RDMA core drivers.")
self._connection.execute_command(
"./install_core.sh", expected_return_codes={0}, shell=True, cwd=remote_irdma_dir
)
logger.log(level=log_levels.MODULE_DEBUG, msg="RDMA drivers installed successfully.")
65 changes: 65 additions & 0 deletions mfd_package_manager/windows.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,14 @@ class WindowsPackageManager(PackageManager):
"WS2025": range(26000, 26999),
}

RDMA_FOLDER_OS_VERSION_MATCH = {
"ws2019": range(17600, 19042 + 1),
"W10": range(19044, 19044 + 1),
"ws2022": list(chain(range(20348, 22000), [23598], range(25000, 25999))),
"W11": range(22000, 22999),
"ws2025": range(26000, 26999),
Comment thread
adrianlasota marked this conversation as resolved.
Resolved
}

def _prepare_map(self) -> Dict[str, Set]:
"""Update mapping for Windows driver names."""
driver_map = cpy.copy(DRIVER_DEVICE_ID_MAP)
Expand Down Expand Up @@ -542,6 +550,19 @@ def _get_folder_for_os_version(self, os_version: int) -> str:
return folder
raise PackageManagerConnectedOSNotSupported(f"Windows in version {os_version} is not supported by module.")

def _get_rdma_folder_for_os_version(self, os_version: int) -> str:
"""
Return correct Windows OS directory required for RDMA driver searching.

:param os_version: Version of Windows kernel.
:return: Directory correct for version.
:raises PackageManagerConnectedOSNotSupported: When version is not supported.
"""
for folder, version in self.RDMA_FOLDER_OS_VERSION_MATCH.items():
if os_version in version:
return folder
raise PackageManagerConnectedOSNotSupported(f"Windows in version {os_version} is not supported by module.")
Comment thread
adrianlasota marked this conversation as resolved.
Resolved

def find_drivers(self, build_path: Union[str, Path], device_id: "DeviceID") -> List[Path]:
"""
Find drivers in build path for given driver_details.
Expand Down Expand Up @@ -902,3 +923,47 @@ def _get_default_vals_from_inf(
except KeyError:
logger.log(level=logging.WARNING, msg="Unable to find the default feature values")
return []

def install_rdma_drivers(self, build_path: "Path | str") -> None:
"""
Install RDMA drivers from build path.

:param build_path: Path to build
:raises PackageManagerNotFoundException: when not found something in flow
"""
if isinstance(build_path, str):
build_path = self._controller_connection.path(build_path)
build_path = build_path / "RDMA" / "Windows"
if not build_path.exists():
raise PackageManagerNotFoundException(f"Build path {build_path} does not exist.")
kernel_version = int(self._connection.get_system_info().kernel_version)
windows_version_folder = self._get_rdma_folder_for_os_version(kernel_version)
logger.log(
level=log_levels.MODULE_DEBUG,
msg=f"Going to use {windows_version_folder} windows version directory for {kernel_version}.",
)
specific_rdma_on_controller_path = build_path / windows_version_folder

if not specific_rdma_on_controller_path.exists():
raise PackageManagerNotFoundException(
f"RDMA path {specific_rdma_on_controller_path} does not exist on controller."
)
rdma_remote_path = self._connection.path(self.DRIVERS_REMOTE_LOCATION, "RDMA")
copy(
src_conn=self._controller_connection,
dst_conn=self._connection,
source=specific_rdma_on_controller_path / "*",
target=rdma_remote_path,
)
inf_file_path = rdma_remote_path / "indv2.inf"
if not inf_file_path.exists():
raise PackageManagerNotFoundException(f"RDMA inf file path {inf_file_path} does not exist.")
logger.log(level=log_levels.MODULE_DEBUG, msg=f"Installing RDMA driver from {inf_file_path}")
self.install_certificates_from_driver(str(inf_file_path))
self.install_inf_driver_for_matching_devices(str(inf_file_path))
driver_list = self.get_driver_files()
for driver in driver_list:
if "indv2.inf" in driver.original_name.casefold():
logger.log(level=log_levels.MODULE_DEBUG, msg="RDMA driver installed successfully.")
return
raise PackageManagerModuleException("RDMA driver installation failed.")
3 changes: 2 additions & 1 deletion tests/unit/test_mfd_package_manager/test_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,8 @@ def test_controller_connection_not_supported(self, mocker):
conn2 = mocker.create_autospec(RPyCConnection)
conn2.get_os_name.return_value = OSName.ESXI
with pytest.raises(
PackageManagerConnectedOSNotSupported, match="Not supported OS for controller PackageManager: [OSName.ESXI|VMKernel]"
PackageManagerConnectedOSNotSupported,
match="Not supported OS for controller PackageManager: [OSName.ESXI|VMKernel]",
):
PackageManager(connection=conn, controller_connection=conn2)

Expand Down
123 changes: 123 additions & 0 deletions tests/unit/test_mfd_package_manager/test_linux.py
Original file line number Diff line number Diff line change
Expand Up @@ -708,3 +708,126 @@ def test_ethtool(self, manager, mocker):
assert manager._LinuxPackageManager__ethtool is None
manager._ethtool
assert manager._LinuxPackageManager__ethtool is not None

def test_install_rdma_drivers_success(self, manager, mocker):
# prepare controller and remote paths
manager._controller_connection = mocker.create_autospec(LocalConnection)
controller_build_path = Path("/home/user/build")
manager._controller_connection.path.return_value = controller_build_path

# pretend all Paths exist
mocker.patch("mfd_package_manager.linux.Path.exists", return_value=True)

# glob returns exactly one irdma tar file located under RDMA/Linux
irdma_file = controller_build_path / "RDMA" / "Linux" / "irdma-1.2.3.tgz"
mocker.patch("mfd_package_manager.linux.Path.glob", return_value=[irdma_file])

# make connection.path return Path objects for remote paths
manager._connection.path.side_effect = lambda p: Path(p)

copy_mock = mocker.patch("mfd_package_manager.linux.copy")

# ensure execute_command returns success for all commands
manager._connection.execute_command.return_value = ConnectionCompletedProcess(args="", stdout="", return_code=0)

# call the method
manager.install_rdma_drivers(str(controller_build_path))

# copy should be called with controller src and connection dst
copy_mock.assert_called_once_with(
src_conn=manager._controller_connection,
dst_conn=manager._connection,
source=irdma_file,
target=Path(f"/tmp/{irdma_file.name}"),
)

# verify build and install commands were invoked (cwd equals remote irdma dir)
remote_dir = Path(f"/tmp/{irdma_file.name.replace('.tgz','')}")
manager._connection.execute_command.assert_any_call(
"./build.sh", expected_return_codes={0}, shell=True, cwd=remote_dir
)
manager._connection.execute_command.assert_any_call(
"./install_core.sh", expected_return_codes={0}, shell=True, cwd=remote_dir
)

def test_install_rdma_drivers_no_tar(self, manager, mocker):
manager._controller_connection = mocker.create_autospec(LocalConnection)
controller_build_path = Path("/home/user/build")
manager._controller_connection.path.return_value = controller_build_path

mocker.patch("mfd_package_manager.linux.Path.exists", return_value=True)
mocker.patch("mfd_package_manager.linux.Path.glob", return_value=[])

with pytest.raises(PackageManagerNotFoundException, match="No irdma tar files found in build path."):
manager.install_rdma_drivers(str(controller_build_path))

def test_install_rdma_drivers_multiple_tar(self, manager, mocker):
manager._controller_connection = mocker.create_autospec(LocalConnection)
controller_build_path = Path("/home/user/build")
manager._controller_connection.path.return_value = controller_build_path

mocker.patch("mfd_package_manager.linux.Path.exists", return_value=True)
tf1 = controller_build_path / "RDMA" / "Linux" / "irdma-1.2.3.tgz"
tf2 = controller_build_path / "RDMA" / "Linux" / "irdma-2.0.0.tgz"
mocker.patch("mfd_package_manager.linux.Path.glob", return_value=[tf1, tf2])

with pytest.raises(PackageManagerModuleException, match="Multiple irdma tar files found in build path."):
manager.install_rdma_drivers(str(controller_build_path))

def test_install_rdma_drivers_runs_build_core(self, manager, mocker):
# Ensure the sh -c build_core command path is executed
manager._controller_connection = mocker.create_autospec(LocalConnection)
controller_build_path = Path("/home/user/build")
manager._controller_connection.path.return_value = controller_build_path

mocker.patch("mfd_package_manager.linux.Path.exists", return_value=True)
irdma_file = controller_build_path / "RDMA" / "Linux" / "irdma-1.2.3.tgz"
mocker.patch("mfd_package_manager.linux.Path.glob", return_value=[irdma_file])

manager._connection.path.side_effect = lambda p: Path(p)
manager._connection.execute_command.return_value = ConnectionCompletedProcess(args="", stdout="", return_code=0)

manager.install_rdma_drivers(str(controller_build_path))

remote_dir = Path(f"/tmp/{irdma_file.name.replace('.tgz','')}")
expected = f"sh -c '{remote_dir}/build_core.sh -y'"
manager._connection.execute_command.assert_any_call(
expected, expected_return_codes={0}, shell=True, stderr_to_stdout=True
)

def test_install_rdma_drivers_tar_extraction(self, manager, mocker):
# Ensure tar extraction command is executed with expected args
manager._controller_connection = mocker.create_autospec(LocalConnection)
controller_build_path = Path("/home/user/build")
manager._controller_connection.path.return_value = controller_build_path

mocker.patch("mfd_package_manager.linux.Path.exists", return_value=True)
irdma_file = controller_build_path / "RDMA" / "Linux" / "irdma-1.2.3.tgz"
mocker.patch("mfd_package_manager.linux.Path.glob", return_value=[irdma_file])

# make remote path resolver return pathlib.Path
manager._connection.path.side_effect = lambda p: Path(p)
# stub execute_command to success
manager._connection.execute_command.return_value = ConnectionCompletedProcess(
args="", stdout="", return_code=0
)

manager.install_rdma_drivers(str(controller_build_path))

remote_irdma_tar = Path(f"/tmp/{irdma_file.name}")
manager._connection.execute_command.assert_any_call(
f"tar -zxvf {remote_irdma_tar} -C /tmp/ --no-same-owner",
expected_return_codes={0},
shell=True,
)

def test_install_rdma_drivers_missing_build(self, manager, mocker):
manager._controller_connection = mocker.create_autospec(LocalConnection)
controller_build_path = Path("/home/user/build")
manager._controller_connection.path.return_value = controller_build_path

mocker.patch("mfd_package_manager.linux.Path.exists", return_value=False)
with pytest.raises(
PackageManagerNotFoundException, match=re.escape(f"Build path {controller_build_path} does not exist.")
):
manager.install_rdma_drivers(str(controller_build_path))
Loading