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
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -301,6 +301,7 @@ Here is a place to write what OSes support your MFD module:
* WINDOWS
* FREEBSD
* ESXI
* EFI SHELL

## Issue reporting

Expand Down
10 changes: 10 additions & 0 deletions examples/simple_example.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,11 @@

import logging

import mfd_connect
from mfd_connect import RPyCConnection

from mfd_host import Host
from mfd_host.efishell import EFIShellHost
from mfd_host.linux import LinuxHost
from mfd_host.windows import WindowsHost
from mfd_host.freebsd import FreeBSDHost
Expand All @@ -34,3 +37,10 @@ def freebsd_driver_example():
connection = RPyCConnection(ip="20.20.20.20")
host = FreeBSDHost(connection=connection)
host.driver.load_module(module_path="test/test")


def efishell_simple_example():
"""Simple EFIShell host instantiation using RshellConnection."""
connection = getattr(mfd_connect, "RshellConnection")(ip="30.30.30.30")
Comment thread
adrianlasota marked this conversation as resolved.
Resolved
host = Host(connection=connection)
assert isinstance(host, EFIShellHost)
4 changes: 3 additions & 1 deletion mfd_host/base.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Copyright (C) 2025 Intel Corporation
# Copyright (C) 2025-2026 Intel Corporation
# SPDX-License-Identifier: MIT
"""Module for Host."""

Expand Down Expand Up @@ -75,13 +75,15 @@ def __new__(cls, connection: "Connection", *args, **kwargs):
from .windows import WindowsHost
from .esxi import ESXiHost
from .freebsd import FreeBSDHost
from .efishell import EFIShellHost

os_name = connection.get_os_name()
os_name_to_class = {
OSName.LINUX: LinuxHost,
OSName.WINDOWS: WindowsHost,
OSName.ESXI: ESXiHost,
OSName.FREEBSD: FreeBSDHost,
OSName.EFISHELL: EFIShellHost,
}

if os_name not in os_name_to_class.keys():
Expand Down
19 changes: 19 additions & 0 deletions mfd_host/efishell.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# Copyright (C) 2026 Intel Corporation
# SPDX-License-Identifier: MIT
"""Module for Host for UEFI Shell OS."""

import logging

from mfd_common_libs import add_logging_level, log_levels, os_supported
from mfd_typing import OSName

from .base import Host

logger = logging.getLogger(__name__)
add_logging_level(level_name="MODULE_DEBUG", level_value=log_levels.MODULE_DEBUG)


class EFIShellHost(Host):
"""Class for EFI Shell host."""

__init__ = os_supported(OSName.EFISHELL)(Host.__init__)
15 changes: 15 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,20 @@ build-backend = "setuptools.build_meta"
[tool.setuptools.dynamic]
dependencies = { file = ["requirements.txt"] }

[project]
name = "mfd-host"
description = "MFD Host provides a unified Python interface for managing and interacting with Systems Under Tests (SUTs) AKA Hosts and their capabilities across multiple operating systems."
requires-python = ">=3.10, <3.14"
version = "2.3.0"
dynamic = ["dependencies"]
license-files = ["LICENSE.md", "AUTHORS.md"]
readme = {file = "README.md", content-type = "text/markdown"}

[project.urls]
Homepage = "https://github.com/intel/mfd"
Repository = "https://github.com/intel/mfd-host"
Issues = "https://github.com/intel/mfd-host/issues"
Changelog = "https://github.com/intel/mfd-host/blob/main/CHANGELOG.md"

[tool.setuptools.packages.find]
exclude = ["examples", "tests*", "sphinx-doc"]
96 changes: 93 additions & 3 deletions tests/unit/test_mfd_host/test_base.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Copyright (C) 2025 Intel Corporation
# Copyright (C) 2025-2026 Intel Corporation
# SPDX-License-Identifier: MIT
"""Tests for `mfd_host` package."""

Expand All @@ -15,8 +15,9 @@


from mfd_host import Host
from mfd_host.efishell import EFIShellHost
from mfd_host.esxi import ESXiHost
from mfd_host.exceptions import HostConnectedOSNotSupported
from mfd_host.exceptions import HostConnectedOSNotSupported, HostConnectionTypeNotSupported
from mfd_host.freebsd import FreeBSDHost
from mfd_host.linux import LinuxHost
from mfd_host.windows import WindowsHost
Expand Down Expand Up @@ -57,10 +58,16 @@ def test_esxi_owner_created(self, mocker):

assert isinstance(Host(connection=conn), ESXiHost)

def test_unsupported_os(self, mocker):
def test_efishell_host_created(self, mocker):
conn = mocker.create_autospec(RPyCConnection)
conn.get_os_name.return_value = OSName.EFISHELL

assert isinstance(Host(connection=conn), EFIShellHost)

def test_unsupported_os(self, mocker):
conn = mocker.create_autospec(RPyCConnection)
conn.get_os_name.return_value = OSName.MELLANOX

with pytest.raises(HostConnectedOSNotSupported):
Host(connection=conn)

Expand Down Expand Up @@ -364,3 +371,86 @@ def test__are_interfaces_same(self, host):
interface_info_0 = LinuxInterfaceInfo(name="eth0", pci_address=pci_address_0)
assert Host._are_interfaces_same(interface, interface_info_0)
assert not Host._are_interfaces_same(interface, interface_info_1)


class TestEFIShellHostProperties:
@pytest.fixture
def efishell_host(self, mocker):
connection = mocker.create_autospec(RPyCConnection)
connection.get_os_name.return_value = OSName.EFISHELL
host = Host(connection=connection)

assert isinstance(host, EFIShellHost)
return host

def test_network_property(self, efishell_host, mocker):
expected_network = object()
mocked_owner = mocker.patch("mfd_network_adapter.NetworkAdapterOwner", return_value=expected_network)

assert efishell_host.network is expected_network
assert efishell_host.network is expected_network
mocked_owner.assert_called_once_with(connection=efishell_host.connection, cli_client=efishell_host.cli_client)

def test_driver_property(self, efishell_host, mocker):
expected_driver = object()
mocked_driver = mocker.patch("mfd_package_manager.PackageManager", return_value=expected_driver)

assert efishell_host.driver is expected_driver
assert efishell_host.driver is expected_driver
mocked_driver.assert_called_once_with(connection=efishell_host.connection)

def test_event_property_unsupported_for_efishell(self, efishell_host):
with pytest.raises(HostConnectionTypeNotSupported):
_ = efishell_host.event

def test_virtualization_property_unsupported_for_efishell(self, efishell_host):
with pytest.raises(HostConnectedOSNotSupported):
_ = efishell_host.virtualization

def test_utils_property(self, efishell_host, mocker):
expected_utils = object()
mocked_utils = mocker.patch("mfd_host.feature.utils.BaseFeatureUtils", return_value=expected_utils)

assert efishell_host.utils is expected_utils
assert efishell_host.utils is expected_utils
mocked_utils.assert_called_once_with(connection=efishell_host.connection, host=efishell_host)

def test_memory_property(self, efishell_host, mocker):
expected_memory = object()
mocked_memory = mocker.patch("mfd_host.feature.memory.BaseFeatureMemory", return_value=expected_memory)

assert efishell_host.memory is expected_memory
assert efishell_host.memory is expected_memory
mocked_memory.assert_called_once_with(connection=efishell_host.connection, host=efishell_host)

def test_stats_property(self, efishell_host, mocker):
expected_stats = object()
mocked_stats = mocker.patch("mfd_host.base.BaseFeatureStats", return_value=expected_stats)

assert efishell_host.stats is expected_stats
assert efishell_host.stats is expected_stats
mocked_stats.assert_called_once_with(connection=efishell_host.connection, host=efishell_host)

def test_cpu_property(self, efishell_host, mocker):
expected_cpu = object()
mocked_cpu = mocker.patch("mfd_host.feature.cpu.BaseFeatureCPU", return_value=expected_cpu)

assert efishell_host.cpu is expected_cpu
assert efishell_host.cpu is expected_cpu
mocked_cpu.assert_called_once_with(connection=efishell_host.connection, host=efishell_host)

def test_service_property(self, efishell_host, mocker):
expected_service = object()
mocked_service = mocker.patch("mfd_host.feature.service.BaseFeatureService", return_value=expected_service)

assert efishell_host.service is expected_service
assert efishell_host.service is expected_service
mocked_service.assert_called_once_with(connection=efishell_host.connection, host=efishell_host)

def test_device_property(self, efishell_host, mocker):
expected_device = object()
mocked_device = mocker.patch("mfd_host.feature.device.BaseFeatureDevice", return_value=expected_device)

assert efishell_host.device is expected_device
assert efishell_host.device is expected_device
mocked_device.assert_called_once_with(connection=efishell_host.connection, host=efishell_host)
Loading