1cfcb4484SDaniel P. Berrangé# SPDX-License-Identifier: GPL-2.0-or-later 2cfcb4484SDaniel P. Berrangé# 3cfcb4484SDaniel P. Berrangé# Utilities for python-based QEMU tests 4cfcb4484SDaniel P. Berrangé# 5cfcb4484SDaniel P. Berrangé# Copyright 2024 Red Hat, Inc. 6cfcb4484SDaniel P. Berrangé# 7cfcb4484SDaniel P. Berrangé# Authors: 8cfcb4484SDaniel P. Berrangé# Thomas Huth <thuth@redhat.com> 9cfcb4484SDaniel P. Berrangé 10cfcb4484SDaniel P. Berrangéimport os 11c055f1d2SDaniel P. Berrangéfrom subprocess import check_call, run, DEVNULL 12cfcb4484SDaniel P. Berrangéimport tarfile 13c283afbfSDaniel P. Berrangéfrom urllib.parse import urlparse 14379ee839SDaniel P. Berrangéimport zipfile 15cfcb4484SDaniel P. Berrangé 16c283afbfSDaniel P. Berrangéfrom .asset import Asset 17512fe088SDaniel P. Berrangé 18cfcb4484SDaniel P. Berrangé 19cfcb4484SDaniel P. Berrangédef tar_extract(archive, dest_dir, member=None): 20cfcb4484SDaniel P. Berrangé with tarfile.open(archive) as tf: 21cfcb4484SDaniel P. Berrangé if hasattr(tarfile, 'data_filter'): 22cfcb4484SDaniel P. Berrangé tf.extraction_filter = getattr(tarfile, 'data_filter', 23cfcb4484SDaniel P. Berrangé (lambda member, path: member)) 24cfcb4484SDaniel P. Berrangé if member: 25cfcb4484SDaniel P. Berrangé tf.extract(member=member, path=dest_dir) 26cfcb4484SDaniel P. Berrangé else: 27cfcb4484SDaniel P. Berrangé tf.extractall(path=dest_dir) 28cfcb4484SDaniel P. Berrangé 29c055f1d2SDaniel P. Berrangédef cpio_extract(archive, output_path): 30cfcb4484SDaniel P. Berrangé cwd = os.getcwd() 31cfcb4484SDaniel P. Berrangé os.chdir(output_path) 32c055f1d2SDaniel P. Berrangé # Not passing 'check=True' as cpio exits with non-zero 33c055f1d2SDaniel P. Berrangé # status if the archive contains any device nodes :-( 34c055f1d2SDaniel P. Berrangé if type(archive) == str: 35c055f1d2SDaniel P. Berrangé run(['cpio', '-i', '-F', archive], 36c055f1d2SDaniel P. Berrangé stdout=DEVNULL, stderr=DEVNULL) 37c055f1d2SDaniel P. Berrangé else: 38c055f1d2SDaniel P. Berrangé run(['cpio', '-i'], 39c055f1d2SDaniel P. Berrangé input=archive.read(), 40c055f1d2SDaniel P. Berrangé stdout=DEVNULL, stderr=DEVNULL) 41cfcb4484SDaniel P. Berrangé os.chdir(cwd) 42379ee839SDaniel P. Berrangé 43379ee839SDaniel P. Berrangédef zip_extract(archive, dest_dir, member=None): 44379ee839SDaniel P. Berrangé with zipfile.ZipFile(archive, 'r') as zf: 45379ee839SDaniel P. Berrangé if member: 46379ee839SDaniel P. Berrangé zf.extract(member=member, path=dest_dir) 47379ee839SDaniel P. Berrangé else: 48379ee839SDaniel P. Berrangé zf.extractall(path=dest_dir) 49512fe088SDaniel P. Berrangé 50512fe088SDaniel P. Berrangédef deb_extract(archive, dest_dir, member=None): 51512fe088SDaniel P. Berrangé cwd = os.getcwd() 52512fe088SDaniel P. Berrangé os.chdir(dest_dir) 53512fe088SDaniel P. Berrangé try: 54*37e9b19cSDaniel P. Berrangé proc = run(['ar', 't', archive], 55*37e9b19cSDaniel P. Berrangé check=True, capture_output=True, encoding='utf8') 56*37e9b19cSDaniel P. Berrangé file_path = proc.stdout.split()[2] 57*37e9b19cSDaniel P. Berrangé check_call(['ar', 'x', archive, file_path], 58*37e9b19cSDaniel P. Berrangé stdout=DEVNULL, stderr=DEVNULL) 59512fe088SDaniel P. Berrangé tar_extract(file_path, dest_dir, member) 60512fe088SDaniel P. Berrangé finally: 61512fe088SDaniel P. Berrangé os.chdir(cwd) 62c283afbfSDaniel P. Berrangé 63c283afbfSDaniel P. Berrangé''' 64c283afbfSDaniel P. Berrangé@params archive: filename, Asset, or file-like object to extract 65c283afbfSDaniel P. Berrangé@params dest_dir: target directory to extract into 66c283afbfSDaniel P. Berrangé@params member: optional member file to limit extraction to 67c283afbfSDaniel P. Berrangé 68c283afbfSDaniel P. BerrangéExtracts @archive into @dest_dir. All files are extracted 69c283afbfSDaniel P. Berrangéunless @member specifies a limit. 70c283afbfSDaniel P. Berrangé 71c283afbfSDaniel P. BerrangéIf @format is None, heuristics will be applied to guess the format 72c283afbfSDaniel P. Berrangéfrom the filename or Asset URL. @format must be non-None if @archive 73c283afbfSDaniel P. Berrangéis a file-like object. 74c283afbfSDaniel P. Berrangé''' 75c283afbfSDaniel P. Berrangédef archive_extract(archive, dest_dir, format=None, member=None): 76c283afbfSDaniel P. Berrangé if format is None: 77c283afbfSDaniel P. Berrangé format = guess_archive_format(archive) 78c283afbfSDaniel P. Berrangé if type(archive) == Asset: 79c283afbfSDaniel P. Berrangé archive = str(archive) 80c283afbfSDaniel P. Berrangé 81c283afbfSDaniel P. Berrangé if format == "tar": 82c283afbfSDaniel P. Berrangé tar_extract(archive, dest_dir, member) 83c283afbfSDaniel P. Berrangé elif format == "zip": 84c283afbfSDaniel P. Berrangé zip_extract(archive, dest_dir, member) 85c283afbfSDaniel P. Berrangé elif format == "cpio": 86c283afbfSDaniel P. Berrangé if member is not None: 87c283afbfSDaniel P. Berrangé raise Exception("Unable to filter cpio extraction") 88c283afbfSDaniel P. Berrangé cpio_extract(archive, dest_dir) 89c283afbfSDaniel P. Berrangé elif format == "deb": 90c283afbfSDaniel P. Berrangé if type(archive) != str: 91c283afbfSDaniel P. Berrangé raise Exception("Unable to use file-like object with deb archives") 92c283afbfSDaniel P. Berrangé deb_extract(archive, dest_dir, "./" + member) 93c283afbfSDaniel P. Berrangé else: 94c283afbfSDaniel P. Berrangé raise Exception(f"Unknown archive format {format}") 95c283afbfSDaniel P. Berrangé 96c283afbfSDaniel P. Berrangé''' 97c283afbfSDaniel P. Berrangé@params archive: filename, or Asset to guess 98c283afbfSDaniel P. Berrangé 99c283afbfSDaniel P. BerrangéGuess the format of @compressed, raising an exception if 100c283afbfSDaniel P. Berrangéno format can be determined 101c283afbfSDaniel P. Berrangé''' 102c283afbfSDaniel P. Berrangédef guess_archive_format(archive): 103c283afbfSDaniel P. Berrangé if type(archive) == Asset: 104c283afbfSDaniel P. Berrangé archive = urlparse(archive.url).path 105c283afbfSDaniel P. Berrangé elif type(archive) != str: 106c283afbfSDaniel P. Berrangé raise Exception(f"Unable to guess archive format for {archive}") 107c283afbfSDaniel P. Berrangé 108c283afbfSDaniel P. Berrangé if ".tar." in archive or archive.endswith("tgz"): 109c283afbfSDaniel P. Berrangé return "tar" 110c283afbfSDaniel P. Berrangé elif archive.endswith(".zip"): 111c283afbfSDaniel P. Berrangé return "zip" 112c283afbfSDaniel P. Berrangé elif archive.endswith(".cpio"): 113c283afbfSDaniel P. Berrangé return "cpio" 114c283afbfSDaniel P. Berrangé elif archive.endswith(".deb") or archive.endswith(".udeb"): 115c283afbfSDaniel P. Berrangé return "deb" 116c283afbfSDaniel P. Berrangé else: 117c283afbfSDaniel P. Berrangé raise Exception(f"Unknown archive format for {archive}") 118