1ba32e50aSDaniel P. Berrangé# SPDX-License-Identifier: GPL-2.0-or-later 2ba32e50aSDaniel P. Berrangé# 3ba32e50aSDaniel P. Berrangé# Utilities for python-based QEMU tests 4ba32e50aSDaniel P. Berrangé# 5ba32e50aSDaniel P. Berrangé# Copyright 2024 Red Hat, Inc. 6ba32e50aSDaniel P. Berrangé# 7ba32e50aSDaniel P. Berrangé# Authors: 8ba32e50aSDaniel P. Berrangé# Thomas Huth <thuth@redhat.com> 9ba32e50aSDaniel P. Berrangé 10ba32e50aSDaniel P. Berrangéimport gzip 11ba32e50aSDaniel P. Berrangéimport lzma 12ba32e50aSDaniel P. Berrangéimport os 13e6c9ab0bSAlex Bennéeimport stat 14ba32e50aSDaniel P. Berrangéimport shutil 15dd66e65fSDaniel P. Berrangéfrom urllib.parse import urlparse 16*98139588SDaniel P. Berrangéfrom subprocess import run, CalledProcessError, DEVNULL 17dd66e65fSDaniel P. Berrangé 18dd66e65fSDaniel P. Berrangéfrom .asset import Asset 19ba32e50aSDaniel P. Berrangé 20ba32e50aSDaniel P. Berrangé 21ba32e50aSDaniel P. Berrangédef gzip_uncompress(gz_path, output_path): 22ba32e50aSDaniel P. Berrangé if os.path.exists(output_path): 23ba32e50aSDaniel P. Berrangé return 24ba32e50aSDaniel P. Berrangé with gzip.open(gz_path, 'rb') as gz_in: 25ba32e50aSDaniel P. Berrangé try: 26ba32e50aSDaniel P. Berrangé with open(output_path, 'wb') as raw_out: 27ba32e50aSDaniel P. Berrangé shutil.copyfileobj(gz_in, raw_out) 28ba32e50aSDaniel P. Berrangé except: 29ba32e50aSDaniel P. Berrangé os.remove(output_path) 30ba32e50aSDaniel P. Berrangé raise 31ba32e50aSDaniel P. Berrangé 32ba32e50aSDaniel P. Berrangédef lzma_uncompress(xz_path, output_path): 33ba32e50aSDaniel P. Berrangé if os.path.exists(output_path): 34ba32e50aSDaniel P. Berrangé return 35ba32e50aSDaniel P. Berrangé with lzma.open(xz_path, 'rb') as lzma_in: 36ba32e50aSDaniel P. Berrangé try: 37ba32e50aSDaniel P. Berrangé with open(output_path, 'wb') as raw_out: 38ba32e50aSDaniel P. Berrangé shutil.copyfileobj(lzma_in, raw_out) 39ba32e50aSDaniel P. Berrangé except: 40ba32e50aSDaniel P. Berrangé os.remove(output_path) 41ba32e50aSDaniel P. Berrangé raise 42dd66e65fSDaniel P. Berrangé 43e6c9ab0bSAlex Bennée 44e6c9ab0bSAlex Bennéedef zstd_uncompress(zstd_path, output_path): 45e6c9ab0bSAlex Bennée if os.path.exists(output_path): 46e6c9ab0bSAlex Bennée return 47e6c9ab0bSAlex Bennée 48e6c9ab0bSAlex Bennée try: 49*98139588SDaniel P. Berrangé run(['zstd', "-f", "-d", zstd_path, 50*98139588SDaniel P. Berrangé "-o", output_path], capture_output=True, check=True) 51e6c9ab0bSAlex Bennée except CalledProcessError as e: 52e6c9ab0bSAlex Bennée os.remove(output_path) 53e6c9ab0bSAlex Bennée raise Exception( 54e6c9ab0bSAlex Bennée f"Unable to decompress zstd file {zstd_path} with {e}") from e 55e6c9ab0bSAlex Bennée 56e6c9ab0bSAlex Bennée # zstd copies source archive permissions for the output 57e6c9ab0bSAlex Bennée # file, so must make this writable for QEMU 58e6c9ab0bSAlex Bennée os.chmod(output_path, stat.S_IRUSR | stat.S_IWUSR) 59e6c9ab0bSAlex Bennée 60e6c9ab0bSAlex Bennée 61dd66e65fSDaniel P. Berrangé''' 62dd66e65fSDaniel P. Berrangé@params compressed: filename, Asset, or file-like object to uncompress 63dd66e65fSDaniel P. Berrangé@params uncompressed: filename to uncompress into 64dd66e65fSDaniel P. Berrangé@params format: optional compression format (gzip, lzma) 65dd66e65fSDaniel P. Berrangé 66dd66e65fSDaniel P. BerrangéUncompresses @compressed into @uncompressed 67dd66e65fSDaniel P. Berrangé 68dd66e65fSDaniel P. BerrangéIf @format is None, heuristics will be applied to guess the format 69dd66e65fSDaniel P. Berrangéfrom the filename or Asset URL. @format must be non-None if @uncompressed 70dd66e65fSDaniel P. Berrangéis a file-like object. 71dd66e65fSDaniel P. Berrangé 72dd66e65fSDaniel P. BerrangéReturns the fully qualified path to the uncompessed file 73dd66e65fSDaniel P. Berrangé''' 74dd66e65fSDaniel P. Berrangédef uncompress(compressed, uncompressed, format=None): 75dd66e65fSDaniel P. Berrangé if format is None: 76dd66e65fSDaniel P. Berrangé format = guess_uncompress_format(compressed) 77dd66e65fSDaniel P. Berrangé 78dd66e65fSDaniel P. Berrangé if format == "xz": 79dd66e65fSDaniel P. Berrangé lzma_uncompress(str(compressed), uncompressed) 80dd66e65fSDaniel P. Berrangé elif format == "gz": 81dd66e65fSDaniel P. Berrangé gzip_uncompress(str(compressed), uncompressed) 82e6c9ab0bSAlex Bennée elif format == "zstd": 83e6c9ab0bSAlex Bennée zstd_uncompress(str(compressed), uncompressed) 84dd66e65fSDaniel P. Berrangé else: 85dd66e65fSDaniel P. Berrangé raise Exception(f"Unknown compression format {format}") 86dd66e65fSDaniel P. Berrangé 87dd66e65fSDaniel P. Berrangé''' 88dd66e65fSDaniel P. Berrangé@params compressed: filename, Asset, or file-like object to guess 89dd66e65fSDaniel P. Berrangé 90dd66e65fSDaniel P. BerrangéGuess the format of @compressed, raising an exception if 91dd66e65fSDaniel P. Berrangéno format can be determined 92dd66e65fSDaniel P. Berrangé''' 93dd66e65fSDaniel P. Berrangédef guess_uncompress_format(compressed): 94dd66e65fSDaniel P. Berrangé if type(compressed) == Asset: 95dd66e65fSDaniel P. Berrangé compressed = urlparse(compressed.url).path 96dd66e65fSDaniel P. Berrangé elif type(compressed) != str: 97dd66e65fSDaniel P. Berrangé raise Exception(f"Unable to guess compression cformat for {compressed}") 98dd66e65fSDaniel P. Berrangé 99dd66e65fSDaniel P. Berrangé (name, ext) = os.path.splitext(compressed) 100dd66e65fSDaniel P. Berrangé if ext == ".xz": 101dd66e65fSDaniel P. Berrangé return "xz" 102dd66e65fSDaniel P. Berrangé elif ext == ".gz": 103dd66e65fSDaniel P. Berrangé return "gz" 104e6c9ab0bSAlex Bennée elif ext in [".zstd", ".zst"]: 105e6c9ab0bSAlex Bennée return 'zstd' 106dd66e65fSDaniel P. Berrangé else: 107dd66e65fSDaniel P. Berrangé raise Exception(f"Unknown compression format for {compressed}") 108