FEX/Scripts/CI_FetchRootFS.py
Ryan Houdek 121f0a2c6c CI: FetchRootFS More robust rootfs fetching
Permissions mean we need to delete the folder before extracting.
On error make sure to delete the image file as well to ensure it reruns
everything.
2022-09-14 13:18:24 -07:00

193 lines
5.7 KiB
Python
Executable File

#!/usr/bin/python3
import xxhash
import hashlib
import sys
import os
import shutil
import subprocess
def GetDistroInfo():
DistroName = "Unknown"
DistroVersion = "Unknown"
with open("/etc/lsb-release", 'r') as f:
while True:
Line = f.readline()
if not Line:
break
Split = Line.split("=")
if Split[0] == "DISTRIB_ID":
DistroName = Split[1].lower().rstrip()
if Split[0] == "DISTRIB_RELEASE":
DistroVersion = Split[1].rstrip()
return [DistroName, DistroVersion]
def FindBestImageFit(Distro, links_file):
CurrentFitSize = 0
BestFitDistro = None
BestFitDistroVersion = None
BestFitReadableName = None
BestFitImagePath = None
BestFitHash = None
with open(links_file, 'r') as f:
while True:
# Order:
# Distro Name
# Distro Version
# User readable name
# File Path
# Hash
DistroName = f.readline().strip()
if not DistroName:
break
DistroVersion = f.readline().strip()
DistroReadableName = f.readline().strip()
DistroImagePath = f.readline().strip()
DistroHash = f.readline().strip()
FitRate = 0
if (DistroName == Distro[0] or
DistroName == None):
FitRate += 1
if (DistroVersion == Distro[1] or
DistroVersion == None):
FitRate += 1
if FitRate > CurrentFitSize:
CurrentFitSize = FitRate
BestFitDistro = DistroName
BestFitDistroVersion = DistroVersion
BestFitReadableName = DistroReadableName
BestFitImagePath = DistroImagePath
BestFitHash = DistroHash
return [BestFitDistro, BestFitDistroVersion, BestFitReadableName, BestFitImagePath, int(BestFitHash, 16)]
def HashFile(file):
# 32MB buffer size
BUFFER_SIZE = 32 * 1024 * 1024
x = xxhash.xxh3_64(seed=0)
b = bytearray(BUFFER_SIZE)
mv = memoryview(b)
with open(file, 'rb') as f:
while n := f.readinto(mv):
x.update(mv[:n])
return int.from_bytes(x.digest(), "big")
def RemoveRootFSFolder(RootFSPath):
print("Removing previous rootfs extraction before copying")
shutil.rmtree(RootFSPath, ignore_errors = True)
# Recreate the folder
os.makedirs(RootFSPath)
def CheckFilesystemForFS(RootFSMountPath, RootFSPath, DistroFit):
# Check if rootfs mount path exists
if (not os.path.exists(RootFSMountPath) or
not os.path.isdir(RootFSMountPath)):
print("RootFS mount path is wrong")
return False
# Check if rootfs path exists
if (not os.path.exists(RootFSPath) or
not os.path.isdir(RootFSPath)):
# Create this directory
os.makedirs(RootFSPath)
# Check if rootfs path exists
if not os.path.isdir(RootFSPath):
print("RootFS path is not a directory")
return False
# Check rootfs folder for image, copy and extract as necessary
MountRootFSImagePath = RootFSMountPath + DistroFit[3]
RootFSImagePath = RootFSPath + "/" + os.path.basename(DistroFit[3])
NeedsExtraction = False
PreviouslyExistingRootFS = False
if not os.path.exists(MountRootFSImagePath):
print("Image {} doesn't exist".format(MountRootFSImagePath))
return False
if not os.path.exists(RootFSImagePath):
# Copy over
print("RootFS image doesn't exist. Copying")
RemoveRootFSFolder(RootFSPath)
shutil.copyfile(MountRootFSImagePath, RootFSImagePath)
NeedsExtraction = True
# Check if the image needs to be extracted
if not os.path.exists(RootFSPath + "/usr"):
NeedsExtraction = True
else:
PreviouslyExistingRootFS = True
# Now hash the image
RootFSHash = HashFile(RootFSImagePath)
if RootFSHash != DistroFit[4]:
print("Hash {} did not match {}, copying new image".format(hex(RootFSHash), hex(DistroFit[4])))
if PreviouslyExistingRootFS:
RemoveRootFSFolder(RootFSPath)
shutil.copyfile(MountRootFSImagePath, RootFSImagePath)
NeedsExtraction = True
if NeedsExtraction:
print("Extracting rootfs")
CmdResult = subprocess.call(["unsquashfs", "-f", "-d", RootFSPath, RootFSImagePath])
if CmdResult != 0:
print("Couldn't extract squashfs. Removing image file to be safe")
os.remove(RootFSImagePath)
return False
if not os.path.exists(RootFSPath + "/usr"):
print("Couldn't extract squashfs. Removing image file to be safe")
os.remove(RootFSImagePath)
return False
print("RootFS successfully checked and extracted")
return True
def main():
if sys.version_info[0] < 3:
logging.critical ("Python 3 or a more recent version is required.")
FEX_ROOTFS_MOUNT = os.getenv("FEX_ROOTFS_MOUNT")
FEX_ROOTFS_PATH = os.getenv("FEX_ROOTFS_PATH")
if FEX_ROOTFS_MOUNT == None:
print("Need FEX_ROOTFS_MOUNT set")
sys.exit(1)
if FEX_ROOTFS_PATH == None:
print("Need FEX_ROOTFS_PATH set")
sys.exit(1)
if shutil.which("unsquashfs") is None:
print("CI system didn't have unsquashfs installed")
sys.exit(1)
Distro = GetDistroInfo()
DistroFit = FindBestImageFit(Distro, FEX_ROOTFS_MOUNT + "/RootFS_links.txt")
if CheckFilesystemForFS(FEX_ROOTFS_MOUNT, FEX_ROOTFS_PATH, DistroFit) == False:
print("Couldn't load filesystem rootfs")
sys.exit(1)
return 0
if __name__ == "__main__":
# execute only if run as a script
sys.exit(main())