1812c4071SJuraj Linkeš# SPDX-License-Identifier: BSD-3-Clause 2812c4071SJuraj Linkeš# Copyright(c) 2010-2014 Intel Corporation 3680d8a24SJuraj Linkeš# Copyright(c) 2022-2023 PANTHEON.tech s.r.o. 4680d8a24SJuraj Linkeš# Copyright(c) 2022-2023 University of New Hampshire 5812c4071SJuraj Linkeš 6c9d31a7eSJuraj Linkešimport atexit 7cecfe0aaSJuraj Linkešimport json 8c9d31a7eSJuraj Linkešimport os 9c9d31a7eSJuraj Linkešimport subprocess 10c9d31a7eSJuraj Linkešfrom enum import Enum 11c9d31a7eSJuraj Linkešfrom pathlib import Path 12c9d31a7eSJuraj Linkešfrom subprocess import SubprocessError 13c9d31a7eSJuraj Linkeš 14cecfe0aaSJuraj Linkešfrom scapy.packet import Packet # type: ignore[import] 15cecfe0aaSJuraj Linkeš 16c9d31a7eSJuraj Linkešfrom .exception import ConfigurationError 17c9d31a7eSJuraj Linkeš 18*840b1e01SJuraj LinkešREGEX_FOR_PCI_ADDRESS: str = "/[0-9a-fA-F]{4}:[0-9a-fA-F]{2}:[0-9a-fA-F]{2}.[0-9]{1}/" 1957c58bf8SJuraj Linkeš 20812c4071SJuraj Linkeš 21c020b7ceSJuraj Linkešdef expand_range(range_str: str) -> list[int]: 22c020b7ceSJuraj Linkeš """ 23c020b7ceSJuraj Linkeš Process range string into a list of integers. There are two possible formats: 24c020b7ceSJuraj Linkeš n - a single integer 25c020b7ceSJuraj Linkeš n-m - a range of integers 26c020b7ceSJuraj Linkeš 27c020b7ceSJuraj Linkeš The returned range includes both n and m. Empty string returns an empty list. 28c020b7ceSJuraj Linkeš """ 29c020b7ceSJuraj Linkeš expanded_range: list[int] = [] 30c020b7ceSJuraj Linkeš if range_str: 31c020b7ceSJuraj Linkeš range_boundaries = range_str.split("-") 32c020b7ceSJuraj Linkeš # will throw an exception when items in range_boundaries can't be converted, 33c020b7ceSJuraj Linkeš # serving as type check 34517b4b26SJuraj Linkeš expanded_range.extend(range(int(range_boundaries[0]), int(range_boundaries[-1]) + 1)) 35c020b7ceSJuraj Linkeš 36c020b7ceSJuraj Linkeš return expanded_range 37c020b7ceSJuraj Linkeš 38c020b7ceSJuraj Linkeš 39*840b1e01SJuraj Linkešdef get_packet_summaries(packets: list[Packet]) -> str: 40cecfe0aaSJuraj Linkeš if len(packets) == 1: 41cecfe0aaSJuraj Linkeš packet_summaries = packets[0].summary() 42cecfe0aaSJuraj Linkeš else: 43517b4b26SJuraj Linkeš packet_summaries = json.dumps(list(map(lambda pkt: pkt.summary(), packets)), indent=4) 44cecfe0aaSJuraj Linkeš return f"Packet contents: \n{packet_summaries}" 45cecfe0aaSJuraj Linkeš 46cecfe0aaSJuraj Linkeš 47*840b1e01SJuraj Linkešclass StrEnum(Enum): 48*840b1e01SJuraj Linkeš @staticmethod 49*840b1e01SJuraj Linkeš def _generate_next_value_(name: str, start: int, count: int, last_values: object) -> str: 50*840b1e01SJuraj Linkeš return name 51*840b1e01SJuraj Linkeš 52*840b1e01SJuraj Linkeš def __str__(self) -> str: 53*840b1e01SJuraj Linkeš return self.name 54680d8a24SJuraj Linkeš 55680d8a24SJuraj Linkeš 56680d8a24SJuraj Linkešclass MesonArgs(object): 57680d8a24SJuraj Linkeš """ 58680d8a24SJuraj Linkeš Aggregate the arguments needed to build DPDK: 59680d8a24SJuraj Linkeš default_library: Default library type, Meson allows "shared", "static" and "both". 60680d8a24SJuraj Linkeš Defaults to None, in which case the argument won't be used. 61680d8a24SJuraj Linkeš Keyword arguments: The arguments found in meson_options.txt in root DPDK directory. 62680d8a24SJuraj Linkeš Do not use -D with them, for example: 63680d8a24SJuraj Linkeš meson_args = MesonArgs(enable_kmods=True). 64680d8a24SJuraj Linkeš """ 65680d8a24SJuraj Linkeš 66680d8a24SJuraj Linkeš _default_library: str 67680d8a24SJuraj Linkeš 68680d8a24SJuraj Linkeš def __init__(self, default_library: str | None = None, **dpdk_args: str | bool): 69517b4b26SJuraj Linkeš self._default_library = f"--default-library={default_library}" if default_library else "" 70680d8a24SJuraj Linkeš self._dpdk_args = " ".join( 71680d8a24SJuraj Linkeš ( 72680d8a24SJuraj Linkeš f"-D{dpdk_arg_name}={dpdk_arg_value}" 73680d8a24SJuraj Linkeš for dpdk_arg_name, dpdk_arg_value in dpdk_args.items() 74680d8a24SJuraj Linkeš ) 75680d8a24SJuraj Linkeš ) 76680d8a24SJuraj Linkeš 77680d8a24SJuraj Linkeš def __str__(self) -> str: 78680d8a24SJuraj Linkeš return " ".join(f"{self._default_library} {self._dpdk_args}".split()) 79c9d31a7eSJuraj Linkeš 80c9d31a7eSJuraj Linkeš 81c9d31a7eSJuraj Linkešclass _TarCompressionFormat(StrEnum): 82c9d31a7eSJuraj Linkeš """Compression formats that tar can use. 83c9d31a7eSJuraj Linkeš 84c9d31a7eSJuraj Linkeš Enum names are the shell compression commands 85c9d31a7eSJuraj Linkeš and Enum values are the associated file extensions. 86c9d31a7eSJuraj Linkeš """ 87c9d31a7eSJuraj Linkeš 88c9d31a7eSJuraj Linkeš gzip = "gz" 89c9d31a7eSJuraj Linkeš compress = "Z" 90c9d31a7eSJuraj Linkeš bzip2 = "bz2" 91c9d31a7eSJuraj Linkeš lzip = "lz" 92c9d31a7eSJuraj Linkeš lzma = "lzma" 93c9d31a7eSJuraj Linkeš lzop = "lzo" 94c9d31a7eSJuraj Linkeš xz = "xz" 95c9d31a7eSJuraj Linkeš zstd = "zst" 96c9d31a7eSJuraj Linkeš 97c9d31a7eSJuraj Linkeš 98c9d31a7eSJuraj Linkešclass DPDKGitTarball(object): 99c9d31a7eSJuraj Linkeš """Create a compressed tarball of DPDK from the repository. 100c9d31a7eSJuraj Linkeš 101c9d31a7eSJuraj Linkeš The DPDK version is specified with git object git_ref. 102c9d31a7eSJuraj Linkeš The tarball will be compressed with _TarCompressionFormat, 103c9d31a7eSJuraj Linkeš which must be supported by the DTS execution environment. 104c9d31a7eSJuraj Linkeš The resulting tarball will be put into output_dir. 105c9d31a7eSJuraj Linkeš 106c9d31a7eSJuraj Linkeš The class supports the os.PathLike protocol, 107c9d31a7eSJuraj Linkeš which is used to get the Path of the tarball:: 108c9d31a7eSJuraj Linkeš 109c9d31a7eSJuraj Linkeš from pathlib import Path 110c9d31a7eSJuraj Linkeš tarball = DPDKGitTarball("HEAD", "output") 111c9d31a7eSJuraj Linkeš tarball_path = Path(tarball) 112c9d31a7eSJuraj Linkeš 113c9d31a7eSJuraj Linkeš Arguments: 114c9d31a7eSJuraj Linkeš git_ref: A git commit ID, tag ID or tree ID. 115c9d31a7eSJuraj Linkeš output_dir: The directory where to put the resulting tarball. 116c9d31a7eSJuraj Linkeš tar_compression_format: The compression format to use. 117c9d31a7eSJuraj Linkeš """ 118c9d31a7eSJuraj Linkeš 119c9d31a7eSJuraj Linkeš _git_ref: str 120c9d31a7eSJuraj Linkeš _tar_compression_format: _TarCompressionFormat 121c9d31a7eSJuraj Linkeš _tarball_dir: Path 122c9d31a7eSJuraj Linkeš _tarball_name: str 123c9d31a7eSJuraj Linkeš _tarball_path: Path | None 124c9d31a7eSJuraj Linkeš 125c9d31a7eSJuraj Linkeš def __init__( 126c9d31a7eSJuraj Linkeš self, 127c9d31a7eSJuraj Linkeš git_ref: str, 128c9d31a7eSJuraj Linkeš output_dir: str, 129c9d31a7eSJuraj Linkeš tar_compression_format: _TarCompressionFormat = _TarCompressionFormat.xz, 130c9d31a7eSJuraj Linkeš ): 131c9d31a7eSJuraj Linkeš self._git_ref = git_ref 132c9d31a7eSJuraj Linkeš self._tar_compression_format = tar_compression_format 133c9d31a7eSJuraj Linkeš 134c9d31a7eSJuraj Linkeš self._tarball_dir = Path(output_dir, "tarball") 135c9d31a7eSJuraj Linkeš 136c9d31a7eSJuraj Linkeš self._get_commit_id() 137c9d31a7eSJuraj Linkeš self._create_tarball_dir() 138c9d31a7eSJuraj Linkeš 139c9d31a7eSJuraj Linkeš self._tarball_name = ( 140c9d31a7eSJuraj Linkeš f"dpdk-tarball-{self._git_ref}.tar.{self._tar_compression_format.value}" 141c9d31a7eSJuraj Linkeš ) 142c9d31a7eSJuraj Linkeš self._tarball_path = self._check_tarball_path() 143c9d31a7eSJuraj Linkeš if not self._tarball_path: 144c9d31a7eSJuraj Linkeš self._create_tarball() 145c9d31a7eSJuraj Linkeš 146c9d31a7eSJuraj Linkeš def _get_commit_id(self) -> None: 147c9d31a7eSJuraj Linkeš result = subprocess.run( 148c9d31a7eSJuraj Linkeš ["git", "rev-parse", "--verify", self._git_ref], 149c9d31a7eSJuraj Linkeš text=True, 150c9d31a7eSJuraj Linkeš capture_output=True, 151c9d31a7eSJuraj Linkeš ) 152c9d31a7eSJuraj Linkeš if result.returncode != 0: 153c9d31a7eSJuraj Linkeš raise ConfigurationError( 154c9d31a7eSJuraj Linkeš f"{self._git_ref} is neither a path to an existing DPDK " 155c9d31a7eSJuraj Linkeš "archive nor a valid git reference.\n" 156c9d31a7eSJuraj Linkeš f"Command: {result.args}\n" 157c9d31a7eSJuraj Linkeš f"Stdout: {result.stdout}\n" 158c9d31a7eSJuraj Linkeš f"Stderr: {result.stderr}" 159c9d31a7eSJuraj Linkeš ) 160c9d31a7eSJuraj Linkeš self._git_ref = result.stdout.strip() 161c9d31a7eSJuraj Linkeš 162c9d31a7eSJuraj Linkeš def _create_tarball_dir(self) -> None: 163c9d31a7eSJuraj Linkeš os.makedirs(self._tarball_dir, exist_ok=True) 164c9d31a7eSJuraj Linkeš 165c9d31a7eSJuraj Linkeš def _check_tarball_path(self) -> Path | None: 166c9d31a7eSJuraj Linkeš if self._tarball_name in os.listdir(self._tarball_dir): 167c9d31a7eSJuraj Linkeš return Path(self._tarball_dir, self._tarball_name) 168c9d31a7eSJuraj Linkeš return None 169c9d31a7eSJuraj Linkeš 170c9d31a7eSJuraj Linkeš def _create_tarball(self) -> None: 171c9d31a7eSJuraj Linkeš self._tarball_path = Path(self._tarball_dir, self._tarball_name) 172c9d31a7eSJuraj Linkeš 173c9d31a7eSJuraj Linkeš atexit.register(self._delete_tarball) 174c9d31a7eSJuraj Linkeš 175c9d31a7eSJuraj Linkeš result = subprocess.run( 176c9d31a7eSJuraj Linkeš 'git -C "$(git rev-parse --show-toplevel)" archive ' 177c9d31a7eSJuraj Linkeš f'{self._git_ref} --prefix="dpdk-tarball-{self._git_ref + os.sep}" | ' 178c9d31a7eSJuraj Linkeš f"{self._tar_compression_format} > {Path(self._tarball_path.absolute())}", 179c9d31a7eSJuraj Linkeš shell=True, 180c9d31a7eSJuraj Linkeš text=True, 181c9d31a7eSJuraj Linkeš capture_output=True, 182c9d31a7eSJuraj Linkeš ) 183c9d31a7eSJuraj Linkeš 184c9d31a7eSJuraj Linkeš if result.returncode != 0: 185c9d31a7eSJuraj Linkeš raise SubprocessError( 186c9d31a7eSJuraj Linkeš f"Git archive creation failed with exit code {result.returncode}.\n" 187c9d31a7eSJuraj Linkeš f"Command: {result.args}\n" 188c9d31a7eSJuraj Linkeš f"Stdout: {result.stdout}\n" 189c9d31a7eSJuraj Linkeš f"Stderr: {result.stderr}" 190c9d31a7eSJuraj Linkeš ) 191c9d31a7eSJuraj Linkeš 192c9d31a7eSJuraj Linkeš atexit.unregister(self._delete_tarball) 193c9d31a7eSJuraj Linkeš 194c9d31a7eSJuraj Linkeš def _delete_tarball(self) -> None: 195c9d31a7eSJuraj Linkeš if self._tarball_path and os.path.exists(self._tarball_path): 196c9d31a7eSJuraj Linkeš os.remove(self._tarball_path) 197c9d31a7eSJuraj Linkeš 198*840b1e01SJuraj Linkeš def __fspath__(self) -> str: 199c9d31a7eSJuraj Linkeš return str(self._tarball_path) 200