11
11
from lisa import Node
12
12
from lisa .executable import Tool
13
13
from lisa .operating_system import Debian , Fedora , Oracle , Posix , Redhat , Suse , Ubuntu
14
- from lisa .tools import Git , Tar , Wget
15
- from lisa .util import UnsupportedDistroException
14
+ from lisa .tools import Git , Lscpu , Tar , Wget
15
+ from lisa .tools .lscpu import CpuArchitecture
16
+ from lisa .util import UnsupportedCpuArchitectureException , UnsupportedDistroException
16
17
17
18
DPDK_STABLE_GIT_REPO = "https://dpdk.org/git/dpdk-stable"
18
19
19
20
# azure routing table magic subnet prefix
20
21
# signals 'route all traffic on this subnet'
21
22
AZ_ROUTE_ALL_TRAFFIC = "0.0.0.0/0"
22
23
24
+ ARCH_COMPATIBILITY_MATRIX = {
25
+ CpuArchitecture .X64 : [CpuArchitecture .X64 ],
26
+ CpuArchitecture .I386 : [CpuArchitecture .I386 , CpuArchitecture .X64 ],
27
+ CpuArchitecture .ARM64 : [CpuArchitecture .ARM64 ],
28
+ }
29
+
23
30
24
31
# Attempt to clean up the DPDK package dependency mess
25
32
# Make a Installer class that implements the common steps
@@ -34,7 +41,7 @@ class OsPackageDependencies:
34
41
# the packages to install on that OS.
35
42
def __init__ (
36
43
self ,
37
- matcher : Callable [[Posix ], bool ],
44
+ matcher : Callable [[Posix , Optional [ CpuArchitecture ] ], bool ],
38
45
packages : Optional [Sequence [Union [str , Tool , Type [Tool ]]]] = None ,
39
46
stop_on_match : bool = False ,
40
47
) -> None :
@@ -45,14 +52,21 @@ def __init__(
45
52
46
53
class DependencyInstaller :
47
54
# provide a list of OsPackageDependencies for a project
48
- def __init__ (self , requirements : List [OsPackageDependencies ]) -> None :
55
+ def __init__ (
56
+ self ,
57
+ requirements : List [OsPackageDependencies ],
58
+ arch : Optional [CpuArchitecture ] = None ,
59
+ ) -> None :
49
60
self .requirements = requirements
61
+ self ._arch = arch
50
62
51
63
# evaluate the list of package dependencies,
52
64
def install_required_packages (
53
- self , node : Node , extra_args : Union [List [str ], None ]
65
+ self ,
66
+ os : Posix ,
67
+ extra_args : Union [List [str ], None ],
68
+ arch : Optional [CpuArchitecture ] = None ,
54
69
) -> None :
55
- os = node .os
56
70
assert isinstance (os , Posix ), (
57
71
"DependencyInstaller is not compatible with this OS: "
58
72
f"{ os .information .vendor } { os .information .release } "
@@ -61,13 +75,16 @@ def install_required_packages(
61
75
# stop on list end or if exclusive_match parameter is true.
62
76
packages : List [Union [str , Tool , Type [Tool ]]] = []
63
77
for requirement in self .requirements :
64
- if requirement .matcher (os ) and requirement .packages :
65
- packages += requirement .packages
78
+ if requirement .matcher (os , arch ):
79
+ if requirement .packages is not None and len (requirement .packages ) > 0 :
80
+ packages += requirement .packages
66
81
if requirement .stop_on_match :
67
82
break
83
+
68
84
os .install_packages (packages = packages , extra_args = extra_args )
69
85
70
86
# NOTE: It is up to the caller to raise an exception on an invalid OS
87
+ # see unsupported_os_thrower as a catch-all 'list end' function
71
88
72
89
73
90
class Downloader :
@@ -200,23 +217,43 @@ def _uninstall(self) -> None:
200
217
def _install_dependencies (self ) -> None :
201
218
if self ._os_dependencies is not None :
202
219
self ._os_dependencies .install_required_packages (
203
- self ._node , extra_args = self ._package_manager_extra_args
220
+ self ._os , extra_args = self ._package_manager_extra_args , arch = self . _arch
204
221
)
205
222
206
223
# define how to check the installed version
207
224
def get_installed_version (self ) -> VersionInfo :
208
225
raise NotImplementedError (f"get_installed_version { self ._err_msg } " )
209
226
210
- def _should_install (self , required_version : Optional [VersionInfo ] = None ) -> bool :
211
- return (not self ._check_if_installed ()) or (
212
- required_version is not None
213
- and required_version > self .get_installed_version ()
227
+ def _should_install (
228
+ self ,
229
+ required_version : Optional [VersionInfo ] = None ,
230
+ required_arch : Optional [CpuArchitecture ] = None ,
231
+ ) -> bool :
232
+ return (
233
+ (not self ._check_if_installed ())
234
+ # NOTE: Taking advantage of longer-than-expected lifetimes here.
235
+ # If the tool still exists we ~should~ be able to check the old version
236
+ # from a previous test environment.
237
+ # At the moment, we use create() to force re-initialization.
238
+ # If we ever fix things so that we use .get,
239
+ # we will need this check. So add it now.q
240
+ or (required_arch != self ._arch )
241
+ or (
242
+ required_version is not None
243
+ and required_version > self .get_installed_version ()
244
+ )
214
245
)
215
246
216
247
# run the defined setup and installation steps.
217
- def do_installation (self , required_version : Optional [VersionInfo ] = None ) -> None :
248
+ def do_installation (
249
+ self ,
250
+ required_version : Optional [VersionInfo ] = None ,
251
+ required_arch : Optional [CpuArchitecture ] = None ,
252
+ ) -> None :
218
253
self ._setup_node ()
219
- if self ._should_install ():
254
+ if self ._should_install (
255
+ required_version = required_version , required_arch = required_arch
256
+ ):
220
257
self ._uninstall ()
221
258
self ._install_dependencies ()
222
259
self ._install ()
@@ -226,6 +263,7 @@ def __init__(
226
263
node : Node ,
227
264
os_dependencies : Optional [DependencyInstaller ] = None ,
228
265
downloader : Optional [Downloader ] = None ,
266
+ arch : Optional [CpuArchitecture ] = None ,
229
267
) -> None :
230
268
self ._node = node
231
269
if not isinstance (self ._node .os , Posix ):
@@ -236,20 +274,34 @@ def __init__(
236
274
self ._package_manager_extra_args : List [str ] = []
237
275
self ._os_dependencies = os_dependencies
238
276
self ._downloader = downloader
277
+ self ._arch = arch
278
+ if self ._arch :
279
+ # avoid building/running arm64 on i386, etc
280
+ system_arch = self ._node .tools [Lscpu ].get_architecture ()
281
+ if system_arch not in ARCH_COMPATIBILITY_MATRIX [self ._arch ]:
282
+ raise UnsupportedCpuArchitectureException (system_arch )
239
283
240
284
241
285
# Base class for package manager installation
242
286
class PackageManagerInstall (Installer ):
243
- def __init__ (self , node : Node , os_dependencies : DependencyInstaller ) -> None :
287
+ def __init__ (
288
+ self ,
289
+ node : Node ,
290
+ os_dependencies : DependencyInstaller ,
291
+ arch : Optional [CpuArchitecture ],
292
+ ) -> None :
244
293
super ().__init__ (node , os_dependencies )
245
294
246
295
# uninstall from the package manager
247
296
def _uninstall (self ) -> None :
248
297
if not (isinstance (self ._os , Posix ) and self ._check_if_installed ()):
249
298
return
250
299
if self ._os_dependencies is not None :
251
- for os_package_check in self ._os_dependencies .requirements :
252
- if os_package_check .matcher (self ._os ) and os_package_check .packages :
300
+ for os_package_check in self ._os_dependencies :
301
+ if (
302
+ os_package_check .matcher (self ._os , self ._arch )
303
+ and os_package_check .packages
304
+ ):
253
305
self ._os .uninstall_packages (os_package_check .packages )
254
306
if os_package_check .stop_on_match :
255
307
break
@@ -261,7 +313,7 @@ def _check_if_installed(self) -> bool:
261
313
# For dpdk, pkg-manager install is only for 'dpdk' and 'dpdk-dev'
262
314
# This will take too long if it's more than a few packages.
263
315
if self ._os_dependencies is not None :
264
- for os_package_check in self ._os_dependencies . requirements :
316
+ for os_package_check in self ._os_dependencies :
265
317
if os_package_check .matcher (self ._os ) and os_package_check .packages :
266
318
for pkg in os_package_check .packages :
267
319
if not self ._os .package_exists (pkg ):
@@ -271,9 +323,13 @@ def _check_if_installed(self) -> bool:
271
323
return True
272
324
273
325
274
- def force_dpdk_default_source (variables : Dict [str , Any ]) -> None :
326
+ def force_dpdk_default_source (
327
+ variables : Dict [str , Any ], build_arch : Optional [CpuArchitecture ] = None
328
+ ) -> None :
275
329
if not variables .get ("dpdk_source" , None ):
276
330
variables ["dpdk_source" ] = DPDK_STABLE_GIT_REPO
331
+ if build_arch :
332
+ variables ["build_arch" ] = build_arch
277
333
278
334
279
335
_UBUNTU_LTS_VERSIONS = ["24.4.0" , "22.4.0" , "20.4.0" , "18.4.0" ]
@@ -368,13 +424,27 @@ def is_url_for_git_repo(url: str) -> bool:
368
424
return scheme == "git" or check_for_git_https
369
425
370
426
371
- def unsupported_os_thrower (os : Posix ) -> bool :
427
+ # utility matcher to throw an OS error type for a match.
428
+ def unsupported_os_thrower (os : Posix , arch : Optional [CpuArchitecture ]) -> bool :
429
+ if arch :
430
+ message_suffix = f"OS and Architecture ({ arch } )"
431
+ else :
432
+ message_suffix = "OS"
372
433
raise UnsupportedDistroException (
373
434
os ,
374
- message = ( "Installer did not define dependencies for this os." ) ,
435
+ message = f "Installer did not define dependencies for this { message_suffix } " ,
375
436
)
376
437
377
438
439
+ # utility matcher to throw an OS error when i386 is not supported
440
+ def i386_not_implemented_thrower (os : Posix , arch : Optional [CpuArchitecture ]) -> bool :
441
+ if arch and arch == CpuArchitecture .I386 :
442
+ raise NotImplementedError (
443
+ "i386 is not implemented for this (installer,OS) combo."
444
+ )
445
+ return False
446
+
447
+
378
448
def get_debian_backport_repo_args (os : Debian ) -> List [str ]:
379
449
# ex: 'bionic-backports' or 'buster-backports'
380
450
# these backport repos are available for the older OS's
0 commit comments