aboutsummaryrefslogtreecommitdiff
path: root/src/mlia/backend/install.py
diff options
context:
space:
mode:
Diffstat (limited to 'src/mlia/backend/install.py')
-rw-r--r--src/mlia/backend/install.py250
1 files changed, 67 insertions, 183 deletions
diff --git a/src/mlia/backend/install.py b/src/mlia/backend/install.py
index 37a277b..c76e3e2 100644
--- a/src/mlia/backend/install.py
+++ b/src/mlia/backend/install.py
@@ -11,17 +11,12 @@ from abc import abstractmethod
from dataclasses import dataclass
from pathlib import Path
from typing import Callable
-from typing import Iterable
from typing import Optional
from typing import Union
-from mlia.backend.executor.runner import BackendRunner
-from mlia.backend.executor.system import remove_system
+from mlia.backend.repo import get_backend_repository
from mlia.utils.download import DownloadArtifact
from mlia.utils.filesystem import all_files_exist
-from mlia.utils.filesystem import all_paths_valid
-from mlia.utils.filesystem import copy_all
-from mlia.utils.filesystem import get_mlia_resources
from mlia.utils.filesystem import temp_directory
from mlia.utils.filesystem import working_directory
from mlia.utils.py_manager import get_package_manager
@@ -29,52 +24,6 @@ from mlia.utils.py_manager import get_package_manager
logger = logging.getLogger(__name__)
-# Mapping backend -> device_type -> system_name
-_SUPPORTED_SYSTEMS = {
- "Corstone-300": {
- "Ethos-U55": "Corstone-300: Cortex-M55+Ethos-U55",
- "Ethos-U65": "Corstone-300: Cortex-M55+Ethos-U65",
- "ethos-u55": "Corstone-300: Cortex-M55+Ethos-U55",
- "ethos-u65": "Corstone-300: Cortex-M55+Ethos-U65",
- },
- "Corstone-310": {
- "Ethos-U55": "Corstone-310: Cortex-M85+Ethos-U55",
- "Ethos-U65": "Corstone-310: Cortex-M85+Ethos-U65",
- "ethos-u55": "Corstone-310: Cortex-M85+Ethos-U55",
- "ethos-u65": "Corstone-310: Cortex-M85+Ethos-U65",
- },
-}
-
-# Mapping system_name -> application
-_SYSTEM_TO_APP_MAP = {
- "Corstone-300: Cortex-M55+Ethos-U55": "Generic Inference Runner: Ethos-U55",
- "Corstone-300: Cortex-M55+Ethos-U65": "Generic Inference Runner: Ethos-U65",
- "Corstone-310: Cortex-M85+Ethos-U55": "Generic Inference Runner: Ethos-U55",
- "Corstone-310: Cortex-M85+Ethos-U65": "Generic Inference Runner: Ethos-U65",
-}
-
-
-def get_system_name(backend: str, device_type: str) -> str:
- """Get the system name for the given backend and device type."""
- return _SUPPORTED_SYSTEMS[backend][device_type]
-
-
-def get_application_name(system_name: str) -> str:
- """Get application name for the provided system name."""
- return _SYSTEM_TO_APP_MAP[system_name]
-
-
-def get_all_system_names(backend: str) -> list[str]:
- """Get all systems supported by the backend."""
- return list(_SUPPORTED_SYSTEMS.get(backend, {}).values())
-
-
-def get_all_application_names(backend: str) -> list[str]:
- """Get all applications supported by the backend."""
- app_set = {_SYSTEM_TO_APP_MAP[sys] for sys in get_all_system_names(backend)}
- return list(app_set)
-
-
@dataclass
class InstallFromPath:
"""Installation from the local path."""
@@ -95,29 +44,24 @@ InstallationType = Union[InstallFromPath, DownloadAndInstall]
class Installation(ABC):
"""Base class for the installation process of the backends."""
- @property
- @abstractmethod
- def name(self) -> str:
- """Return name of the backend."""
-
- @property
- @abstractmethod
- def description(self) -> str:
- """Return description of the backend."""
+ def __init__(self, name: str, description: str) -> None:
+ """Init the installation."""
+ self.name = name
+ self.description = description
@property
@abstractmethod
def could_be_installed(self) -> bool:
- """Return true if backend could be installed in current environment."""
+ """Check if backend could be installed in current environment."""
@property
@abstractmethod
def already_installed(self) -> bool:
- """Return true if backend is already installed."""
+ """Check if backend is already installed."""
@abstractmethod
def supports(self, install_type: InstallationType) -> bool:
- """Return true if installation supports requested installation type."""
+ """Check if installation supports requested installation type."""
@abstractmethod
def install(self, install_type: InstallationType) -> None:
@@ -134,103 +78,53 @@ class BackendInfo:
backend_path: Path
copy_source: bool = True
- system_config: str | None = None
+ settings: dict | None = None
PathChecker = Callable[[Path], Optional[BackendInfo]]
BackendInstaller = Callable[[bool, Path], Path]
-class BackendMetadata:
- """Backend installation metadata."""
+class BackendInstallation(Installation):
+ """Backend installation."""
def __init__(
self,
name: str,
description: str,
- system_config: str,
- apps_resources: list[str],
fvp_dir_name: str,
download_artifact: DownloadArtifact | None,
- supported_platforms: list[str] | None = None,
+ supported_platforms: list[str] | None,
+ path_checker: PathChecker,
+ backend_installer: BackendInstaller | None,
) -> None:
- """
- Initialize BackendMetadata.
+ """Init the backend installation."""
+ super().__init__(name, description)
- Members expected_systems and expected_apps are filled automatically.
- """
- self.name = name
- self.description = description
- self.system_config = system_config
- self.apps_resources = apps_resources
self.fvp_dir_name = fvp_dir_name
self.download_artifact = download_artifact
self.supported_platforms = supported_platforms
-
- self.expected_systems = get_all_system_names(name)
- self.expected_apps = get_all_application_names(name)
-
- @property
- def expected_resources(self) -> Iterable[Path]:
- """Return list of expected resources."""
- resources = [self.system_config, *self.apps_resources]
-
- return (get_mlia_resources() / resource for resource in resources)
-
- @property
- def supported_platform(self) -> bool:
- """Return true if current platform supported."""
- if not self.supported_platforms:
- return True
-
- return platform.system() in self.supported_platforms
-
-
-class BackendInstallation(Installation):
- """Backend installation."""
-
- def __init__(
- self,
- backend_runner: BackendRunner,
- metadata: BackendMetadata,
- path_checker: PathChecker,
- backend_installer: BackendInstaller | None,
- ) -> None:
- """Init the backend installation."""
- self.backend_runner = backend_runner
- self.metadata = metadata
self.path_checker = path_checker
self.backend_installer = backend_installer
@property
- def name(self) -> str:
- """Return name of the backend."""
- return self.metadata.name
-
- @property
- def description(self) -> str:
- """Return description of the backend."""
- return self.metadata.description
-
- @property
def already_installed(self) -> bool:
"""Return true if backend already installed."""
- return self.backend_runner.all_installed(
- self.metadata.expected_systems, self.metadata.expected_apps
- )
+ backend_repo = get_backend_repository()
+ return backend_repo.is_backend_installed(self.name)
@property
def could_be_installed(self) -> bool:
"""Return true if backend could be installed."""
- if not self.metadata.supported_platform:
- return False
-
- return all_paths_valid(self.metadata.expected_resources)
+ return (
+ not self.supported_platforms
+ or platform.system() in self.supported_platforms
+ )
def supports(self, install_type: InstallationType) -> bool:
"""Return true if backends supported type of the installation."""
if isinstance(install_type, DownloadAndInstall):
- return self.metadata.download_artifact is not None
+ return self.download_artifact is not None
if isinstance(install_type, InstallFromPath):
return self.path_checker(install_type.backend_path) is not None
@@ -240,41 +134,38 @@ class BackendInstallation(Installation):
def install(self, install_type: InstallationType) -> None:
"""Install the backend."""
if isinstance(install_type, DownloadAndInstall):
- download_artifact = self.metadata.download_artifact
- assert download_artifact is not None, "No artifact provided"
+ assert self.download_artifact is not None, "No artifact provided"
- self.download_and_install(download_artifact, install_type.eula_agreement)
+ self._download_and_install(
+ self.download_artifact, install_type.eula_agreement
+ )
elif isinstance(install_type, InstallFromPath):
- backend_path = self.path_checker(install_type.backend_path)
- assert backend_path is not None, "Unable to resolve backend path"
+ backend_info = self.path_checker(install_type.backend_path)
- self.install_from(backend_path)
+ assert backend_info is not None, "Unable to resolve backend path"
+ self._install_from(backend_info)
else:
raise Exception(f"Unable to install {install_type}")
- def install_from(self, backend_info: BackendInfo) -> None:
+ def _install_from(self, backend_info: BackendInfo) -> None:
"""Install backend from the directory."""
- mlia_resources = get_mlia_resources()
-
- with temp_directory() as tmpdir:
- fvp_dist_dir = tmpdir / self.metadata.fvp_dir_name
-
- system_config = self.metadata.system_config
- if backend_info.system_config:
- system_config = backend_info.system_config
-
- resources_to_copy = [mlia_resources / system_config]
- if backend_info.copy_source:
- resources_to_copy.append(backend_info.backend_path)
-
- copy_all(*resources_to_copy, dest=fvp_dist_dir)
-
- self.backend_runner.install_system(fvp_dist_dir)
-
- for app in self.metadata.apps_resources:
- self.backend_runner.install_application(mlia_resources / app)
+ backend_repo = get_backend_repository()
+
+ if backend_info.copy_source:
+ backend_repo.copy_backend(
+ self.name,
+ backend_info.backend_path,
+ self.fvp_dir_name,
+ backend_info.settings,
+ )
+ else:
+ backend_repo.add_backend(
+ self.name,
+ backend_info.backend_path,
+ backend_info.settings,
+ )
- def download_and_install(
+ def _download_and_install(
self, download_artifact: DownloadArtifact, eula_agrement: bool
) -> None:
"""Download and install the backend."""
@@ -288,11 +179,10 @@ class BackendInstallation(Installation):
with tarfile.open(downloaded_to) as archive:
archive.extractall(dist_dir)
- assert self.backend_installer, (
- f"Backend '{self.metadata.name}' does not support "
- "download and installation."
- )
- backend_path = self.backend_installer(eula_agrement, dist_dir)
+ backend_path = dist_dir
+ if self.backend_installer:
+ backend_path = self.backend_installer(eula_agrement, dist_dir)
+
if self.path_checker(backend_path) is None:
raise Exception("Downloaded artifact has invalid structure")
@@ -300,18 +190,23 @@ class BackendInstallation(Installation):
def uninstall(self) -> None:
"""Uninstall the backend."""
- remove_system(self.metadata.fvp_dir_name)
+ backend_repo = get_backend_repository()
+ backend_repo.remove_backend(self.name)
class PackagePathChecker:
"""Package path checker."""
def __init__(
- self, expected_files: list[str], backend_subfolder: str | None = None
+ self,
+ expected_files: list[str],
+ backend_subfolder: str | None = None,
+ settings: dict = None,
) -> None:
"""Init the path checker."""
self.expected_files = expected_files
self.backend_subfolder = backend_subfolder
+ self.settings = settings
def __call__(self, backend_path: Path) -> BackendInfo | None:
"""Check if directory contains all expected files."""
@@ -319,15 +214,14 @@ class PackagePathChecker:
if not all_files_exist(resolved_paths):
return None
+ actual_backend_path = backend_path
if self.backend_subfolder:
subfolder = backend_path / self.backend_subfolder
- if not subfolder.is_dir():
- return None
+ if subfolder.is_dir():
+ actual_backend_path = subfolder
- return BackendInfo(subfolder)
-
- return BackendInfo(backend_path)
+ return BackendInfo(actual_backend_path, settings=self.settings)
class StaticPathChecker:
@@ -338,13 +232,13 @@ class StaticPathChecker:
static_backend_path: Path,
expected_files: list[str],
copy_source: bool = False,
- system_config: str | None = None,
+ settings: dict | None = None,
) -> None:
"""Init static path checker."""
self.static_backend_path = static_backend_path
self.expected_files = expected_files
self.copy_source = copy_source
- self.system_config = system_config
+ self.settings = settings
def __call__(self, backend_path: Path) -> BackendInfo | None:
"""Check if directory equals static backend path with all expected files."""
@@ -358,7 +252,7 @@ class StaticPathChecker:
return BackendInfo(
backend_path,
copy_source=self.copy_source,
- system_config=self.system_config,
+ settings=self.settings,
)
@@ -392,8 +286,8 @@ class PyPackageBackendInstallation(Installation):
expected_packages: list[str],
) -> None:
"""Init the backend installation."""
- self._name = name
- self._description = description
+ super().__init__(name, description)
+
self._packages_to_install = packages_to_install
self._packages_to_uninstall = packages_to_uninstall
self._expected_packages = expected_packages
@@ -401,16 +295,6 @@ class PyPackageBackendInstallation(Installation):
self.package_manager = get_package_manager()
@property
- def name(self) -> str:
- """Return name of the backend."""
- return self._name
-
- @property
- def description(self) -> str:
- """Return description of the backend."""
- return self._description
-
- @property
def could_be_installed(self) -> bool:
"""Check if backend could be installed."""
return True