1414from lisa .operating_system import Debian , Fedora , Oracle , Posix , Suse , Ubuntu
1515from lisa .tools import Git , Lscpu , Tar , Wget
1616from lisa .tools .lscpu import CpuArchitecture
17- from lisa .util import UnsupportedDistroException
17+ from lisa .util import UnsupportedCpuArchitectureException , UnsupportedDistroException
1818
1919DPDK_STABLE_GIT_REPO = "https://dpdk.org/git/dpdk-stable"
20-
20+ # 32bit test relies on newer versions of DPDK.
21+ # Release candidates are not in stable, so use the github mirror.
22+ DPDK_32BIT_DEFAULT_BRANCH = "v24.11"
23+ # MANA testing requires newer versions of DPDK.
24+ DPDK_MANA_DEFAULT_BRANCH = "v24.11"
2125# azure routing table magic subnet prefix
2226# signals 'route all traffic on this subnet'
2327AZ_ROUTE_ALL_TRAFFIC = "0.0.0.0/0"
2428
29+ ARCH_COMPATIBILITY_MATRIX = {
30+ CpuArchitecture .X64 : [CpuArchitecture .X64 ],
31+ CpuArchitecture .I386 : [CpuArchitecture .I386 , CpuArchitecture .X64 ],
32+ CpuArchitecture .ARM64 : [CpuArchitecture .ARM64 ],
33+ }
34+
2535
2636# Attempt to clean up the DPDK package dependency mess
2737# Make a Installer class that implements the common steps
@@ -36,7 +46,7 @@ class OsPackageDependencies:
3646 # the packages to install on that OS.
3747 def __init__ (
3848 self ,
39- matcher : Callable [[Posix ], bool ],
49+ matcher : Callable [[Posix , Optional [ CpuArchitecture ] ], bool ],
4050 packages : Optional [Sequence [Union [str , Tool , Type [Tool ]]]] = None ,
4151 stop_on_match : bool = False ,
4252 ) -> None :
@@ -47,14 +57,21 @@ def __init__(
4757
4858class DependencyInstaller :
4959 # provide a list of OsPackageDependencies for a project
50- def __init__ (self , requirements : List [OsPackageDependencies ]) -> None :
60+ def __init__ (
61+ self ,
62+ requirements : List [OsPackageDependencies ],
63+ arch : Optional [CpuArchitecture ] = None ,
64+ ) -> None :
5165 self .requirements = requirements
66+ self ._arch = arch
5267
5368 # evaluate the list of package dependencies,
5469 def install_required_packages (
55- self , node : Node , extra_args : Union [List [str ], None ]
70+ self ,
71+ os : Posix ,
72+ extra_args : Union [List [str ], None ],
73+ arch : Optional [CpuArchitecture ] = None ,
5674 ) -> None :
57- os = node .os
5875 assert isinstance (os , Posix ), (
5976 "DependencyInstaller is not compatible with this OS: "
6077 f"{ os .information .vendor } { os .information .release } "
@@ -63,13 +80,16 @@ def install_required_packages(
6380 # stop on list end or if exclusive_match parameter is true.
6481 packages : List [Union [str , Tool , Type [Tool ]]] = []
6582 for requirement in self .requirements :
66- if requirement .matcher (os ) and requirement .packages :
67- packages += requirement .packages
83+ if requirement .matcher (os , arch ):
84+ if requirement .packages is not None and len (requirement .packages ) > 0 :
85+ packages += requirement .packages
6886 if requirement .stop_on_match :
6987 break
88+
7089 os .install_packages (packages = packages , extra_args = extra_args )
7190
7291 # NOTE: It is up to the caller to raise an exception on an invalid OS
92+ # see unsupported_os_thrower as a catch-all 'list end' function
7393
7494
7595class Downloader :
@@ -201,23 +221,43 @@ def _uninstall(self) -> None:
201221 def _install_dependencies (self ) -> None :
202222 if self ._os_dependencies is not None :
203223 self ._os_dependencies .install_required_packages (
204- self ._node , extra_args = self ._package_manager_extra_args
224+ self ._os , extra_args = self ._package_manager_extra_args , arch = self . _arch
205225 )
206226
207227 # define how to check the installed version
208228 def get_installed_version (self ) -> VersionInfo :
209229 raise NotImplementedError (f"get_installed_version { self ._err_msg } " )
210230
211- def _should_install (self , required_version : Optional [VersionInfo ] = None ) -> bool :
212- return (not self ._check_if_installed ()) or (
213- required_version is not None
214- and required_version > self .get_installed_version ()
231+ def _should_install (
232+ self ,
233+ required_version : Optional [VersionInfo ] = None ,
234+ required_arch : Optional [CpuArchitecture ] = None ,
235+ ) -> bool :
236+ return (
237+ (not self ._check_if_installed ())
238+ # NOTE: Taking advantage of longer-than-expected lifetimes here.
239+ # If the tool still exists we ~should~ be able to check the old version
240+ # from a previous test environment.
241+ # At the moment, we use create() to force re-initialization.
242+ # If we ever fix things so that we use .get,
243+ # we will need this check. So add it now.q
244+ or bool (self ._arch and required_arch != self ._arch )
245+ or (
246+ required_version is not None
247+ and required_version > self .get_installed_version ()
248+ )
215249 )
216250
217251 # run the defined setup and installation steps.
218- def do_installation (self , required_version : Optional [VersionInfo ] = None ) -> None :
252+ def do_installation (
253+ self ,
254+ required_version : Optional [VersionInfo ] = None ,
255+ required_arch : Optional [CpuArchitecture ] = None ,
256+ ) -> None :
219257 self ._setup_node ()
220- if self ._should_install ():
258+ if self ._should_install (
259+ required_version = required_version , required_arch = required_arch
260+ ):
221261 self ._uninstall ()
222262 self ._install_dependencies ()
223263 self ._install ()
@@ -227,6 +267,7 @@ def __init__(
227267 node : Node ,
228268 os_dependencies : Optional [DependencyInstaller ] = None ,
229269 downloader : Optional [Downloader ] = None ,
270+ arch : Optional [CpuArchitecture ] = None ,
230271 ) -> None :
231272 self ._node = node
232273 if not isinstance (self ._node .os , Posix ):
@@ -237,11 +278,22 @@ def __init__(
237278 self ._package_manager_extra_args : List [str ] = []
238279 self ._os_dependencies = os_dependencies
239280 self ._downloader = downloader
281+ self ._arch = arch
282+ if self ._arch :
283+ # avoid building/running arm64 on i386, etc
284+ system_arch = self ._node .tools [Lscpu ].get_architecture ()
285+ if system_arch not in ARCH_COMPATIBILITY_MATRIX [self ._arch ]:
286+ raise UnsupportedCpuArchitectureException (system_arch )
240287
241288
242289# Base class for package manager installation
243290class PackageManagerInstall (Installer ):
244- def __init__ (self , node : Node , os_dependencies : DependencyInstaller ) -> None :
291+ def __init__ (
292+ self ,
293+ node : Node ,
294+ os_dependencies : DependencyInstaller ,
295+ arch : Optional [CpuArchitecture ],
296+ ) -> None :
245297 super ().__init__ (node , os_dependencies )
246298
247299 # uninstall from the package manager
@@ -250,7 +302,10 @@ def _uninstall(self) -> None:
250302 return
251303 if self ._os_dependencies is not None :
252304 for os_package_check in self ._os_dependencies .requirements :
253- if os_package_check .matcher (self ._os ) and os_package_check .packages :
305+ if (
306+ os_package_check .matcher (self ._os , self ._arch )
307+ and os_package_check .packages
308+ ):
254309 self ._os .uninstall_packages (os_package_check .packages )
255310 if os_package_check .stop_on_match :
256311 break
@@ -263,7 +318,10 @@ def _check_if_installed(self) -> bool:
263318 # This will take too long if it's more than a few packages.
264319 if self ._os_dependencies is not None :
265320 for os_package_check in self ._os_dependencies .requirements :
266- if os_package_check .matcher (self ._os ) and os_package_check .packages :
321+ if (
322+ os_package_check .matcher (self ._os , self ._arch )
323+ and os_package_check .packages
324+ ):
267325 for pkg in os_package_check .packages :
268326 if not self ._os .package_exists (pkg ):
269327 return False
@@ -272,11 +330,40 @@ def _check_if_installed(self) -> bool:
272330 return True
273331
274332
275- def force_dpdk_default_source (variables : Dict [str , Any ]) -> None :
333+ # force specific default sources for arch tests (os-independent)
334+ def force_dpdk_default_source_variables (
335+ variables : Dict [str , Any ], build_arch : Optional [CpuArchitecture ] = None
336+ ) -> None :
337+ if build_arch :
338+ variables ["build_arch" ] = build_arch
339+
340+ if build_arch == CpuArchitecture .I386 :
341+ if not variables .get ("dpdk_branch" , None ):
342+ # assign a default branch with needed MANA commits for 32bit test
343+ variables ["dpdk_branch" ] = DPDK_32BIT_DEFAULT_BRANCH
276344 if not variables .get ("dpdk_source" , None ):
277345 variables ["dpdk_source" ] = DPDK_STABLE_GIT_REPO
278346
279347
348+ # force source builds for distros and environments which need a later verison.
349+ # ie. ubuntu 18.04 and MANA
350+ def set_default_dpdk_source (node : Node , variables : Dict [str , Any ]) -> None :
351+ # DPDK packages 17.11 which is EOL and doesn't have the
352+ # net_vdev_netvsc pmd used for simple handling of hyper-v
353+ # guests. Force stable source build on this platform.
354+ # Default to 20.11 unless another version is provided by the
355+ # user. 20.11 is the latest dpdk version for 18.04.
356+ if isinstance (node .os , Ubuntu ) and node .os .information .version < "20.4.0" :
357+ variables ["dpdk_source" ] = variables .get ("dpdk_source" , DPDK_STABLE_GIT_REPO )
358+ variables ["dpdk_branch" ] = variables .get ("dpdk_branch" , "v20.11" )
359+ # MANA runs need a later version of DPDK
360+ if node .nics .is_mana_device_present ():
361+ variables ["dpdk_source" ] = variables .get ("dpdk_source" , DPDK_STABLE_GIT_REPO )
362+ variables ["dpdk_branch" ] = variables .get (
363+ "dpdk_branch" , DPDK_MANA_DEFAULT_BRANCH
364+ )
365+
366+
280367_UBUNTU_LTS_VERSIONS = ["24.4.0" , "22.4.0" , "20.4.0" , "18.4.0" ]
281368
282369
@@ -383,13 +470,27 @@ def is_url_for_git_repo(url: str) -> bool:
383470 return scheme == "git" or check_for_git_https
384471
385472
386- def unsupported_os_thrower (os : Posix ) -> bool :
473+ # utility matcher to throw an OS error type for a match.
474+ def unsupported_os_thrower (os : Posix , arch : Optional [CpuArchitecture ]) -> bool :
475+ if arch :
476+ message_suffix = f"OS and Architecture ({ arch } )"
477+ else :
478+ message_suffix = "OS"
387479 raise UnsupportedDistroException (
388480 os ,
389- message = ( "Installer did not define dependencies for this os." ) ,
481+ message = f "Installer did not define dependencies for this { message_suffix } " ,
390482 )
391483
392484
485+ # utility matcher to throw an OS error when i386 is not supported
486+ def i386_not_implemented_thrower (os : Posix , arch : Optional [CpuArchitecture ]) -> bool :
487+ if arch and arch == CpuArchitecture .I386 :
488+ raise NotImplementedError (
489+ "i386 is not implemented for this (installer,OS) combo."
490+ )
491+ return False
492+
493+
393494def get_debian_backport_repo_args (os : Debian ) -> List [str ]:
394495 # ex: 'bionic-backports' or 'buster-backports'
395496 # these backport repos are available for the older OS's
0 commit comments