14
14
from lisa .operating_system import Debian , Fedora , Oracle , Posix , Suse , Ubuntu
15
15
from lisa .tools import Git , Lscpu , Tar , Wget
16
16
from lisa .tools .lscpu import CpuArchitecture
17
- from lisa .util import UnsupportedDistroException
17
+ from lisa .util import UnsupportedCpuArchitectureException , UnsupportedDistroException
18
18
19
19
DPDK_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"
21
25
# azure routing table magic subnet prefix
22
26
# signals 'route all traffic on this subnet'
23
27
AZ_ROUTE_ALL_TRAFFIC = "0.0.0.0/0"
24
28
29
+ ARCH_COMPATIBILITY_MATRIX = {
30
+ CpuArchitecture .X64 : [CpuArchitecture .X64 ],
31
+ CpuArchitecture .I386 : [CpuArchitecture .I386 , CpuArchitecture .X64 ],
32
+ CpuArchitecture .ARM64 : [CpuArchitecture .ARM64 ],
33
+ }
34
+
25
35
26
36
# Attempt to clean up the DPDK package dependency mess
27
37
# Make a Installer class that implements the common steps
@@ -36,7 +46,7 @@ class OsPackageDependencies:
36
46
# the packages to install on that OS.
37
47
def __init__ (
38
48
self ,
39
- matcher : Callable [[Posix ], bool ],
49
+ matcher : Callable [[Posix , Optional [ CpuArchitecture ] ], bool ],
40
50
packages : Optional [Sequence [Union [str , Tool , Type [Tool ]]]] = None ,
41
51
stop_on_match : bool = False ,
42
52
) -> None :
@@ -47,14 +57,21 @@ def __init__(
47
57
48
58
class DependencyInstaller :
49
59
# 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 :
51
65
self .requirements = requirements
66
+ self ._arch = arch
52
67
53
68
# evaluate the list of package dependencies,
54
69
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 ,
56
74
) -> None :
57
- os = node .os
58
75
assert isinstance (os , Posix ), (
59
76
"DependencyInstaller is not compatible with this OS: "
60
77
f"{ os .information .vendor } { os .information .release } "
@@ -63,13 +80,16 @@ def install_required_packages(
63
80
# stop on list end or if exclusive_match parameter is true.
64
81
packages : List [Union [str , Tool , Type [Tool ]]] = []
65
82
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
68
86
if requirement .stop_on_match :
69
87
break
88
+
70
89
os .install_packages (packages = packages , extra_args = extra_args )
71
90
72
91
# 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
73
93
74
94
75
95
class Downloader :
@@ -201,23 +221,43 @@ def _uninstall(self) -> None:
201
221
def _install_dependencies (self ) -> None :
202
222
if self ._os_dependencies is not None :
203
223
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
205
225
)
206
226
207
227
# define how to check the installed version
208
228
def get_installed_version (self ) -> VersionInfo :
209
229
raise NotImplementedError (f"get_installed_version { self ._err_msg } " )
210
230
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
+ )
215
249
)
216
250
217
251
# 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 :
219
257
self ._setup_node ()
220
- if self ._should_install ():
258
+ if self ._should_install (
259
+ required_version = required_version , required_arch = required_arch
260
+ ):
221
261
self ._uninstall ()
222
262
self ._install_dependencies ()
223
263
self ._install ()
@@ -227,6 +267,7 @@ def __init__(
227
267
node : Node ,
228
268
os_dependencies : Optional [DependencyInstaller ] = None ,
229
269
downloader : Optional [Downloader ] = None ,
270
+ arch : Optional [CpuArchitecture ] = None ,
230
271
) -> None :
231
272
self ._node = node
232
273
if not isinstance (self ._node .os , Posix ):
@@ -237,11 +278,22 @@ def __init__(
237
278
self ._package_manager_extra_args : List [str ] = []
238
279
self ._os_dependencies = os_dependencies
239
280
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 )
240
287
241
288
242
289
# Base class for package manager installation
243
290
class 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 :
245
297
super ().__init__ (node , os_dependencies )
246
298
247
299
# uninstall from the package manager
@@ -250,7 +302,10 @@ def _uninstall(self) -> None:
250
302
return
251
303
if self ._os_dependencies is not None :
252
304
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
+ ):
254
309
self ._os .uninstall_packages (os_package_check .packages )
255
310
if os_package_check .stop_on_match :
256
311
break
@@ -263,7 +318,10 @@ def _check_if_installed(self) -> bool:
263
318
# This will take too long if it's more than a few packages.
264
319
if self ._os_dependencies is not None :
265
320
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
+ ):
267
325
for pkg in os_package_check .packages :
268
326
if not self ._os .package_exists (pkg ):
269
327
return False
@@ -272,11 +330,40 @@ def _check_if_installed(self) -> bool:
272
330
return True
273
331
274
332
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
276
344
if not variables .get ("dpdk_source" , None ):
277
345
variables ["dpdk_source" ] = DPDK_STABLE_GIT_REPO
278
346
279
347
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
+
280
367
_UBUNTU_LTS_VERSIONS = ["24.4.0" , "22.4.0" , "20.4.0" , "18.4.0" ]
281
368
282
369
@@ -383,13 +470,27 @@ def is_url_for_git_repo(url: str) -> bool:
383
470
return scheme == "git" or check_for_git_https
384
471
385
472
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"
387
479
raise UnsupportedDistroException (
388
480
os ,
389
- message = ( "Installer did not define dependencies for this os." ) ,
481
+ message = f "Installer did not define dependencies for this { message_suffix } " ,
390
482
)
391
483
392
484
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
+
393
494
def get_debian_backport_repo_args (os : Debian ) -> List [str ]:
394
495
# ex: 'bionic-backports' or 'buster-backports'
395
496
# these backport repos are available for the older OS's
0 commit comments