87815216 |
import os
import threading |
326d5ca8 |
import copy |
2820c61a |
from PackageBuildDataGenerator import PackageBuildDataGenerator
from Logger import Logger
from constants import constants |
7418d2bf |
import docker
from CommandUtils import CommandUtils |
2820c61a |
from PackageUtils import PackageUtils |
518d6a6f |
from ToolChainUtils import ToolChainUtils |
d024e640 |
from Scheduler import Scheduler
from ThreadPool import ThreadPool |
45c9260c |
from SpecData import SPECS |
f93ef2b0 |
from StringUtils import StringUtils |
8f56b626 |
from Sandbox import Chroot, Container |
2820c61a |
class PackageManager(object): |
6c9b4b2f |
|
87815216 |
def __init__(self, logName=None, logPath=None, pkgBuildType="chroot"): |
2820c61a |
if logName is None:
logName = "PackageManager"
if logPath is None:
logPath = constants.logPath |
87815216 |
self.logName = logName
self.logPath = logPath |
26b55679 |
self.logLevel = constants.logLevel
self.logger = Logger.getLogger(logName, logPath, constants.logLevel) |
87815216 |
self.mapCyclesToPackageList = {}
self.mapPackageToCycle = {}
self.sortedPackageList = [] |
326d5ca8 |
self.listOfPackagesAlreadyBuilt = set() |
87815216 |
self.pkgBuildType = pkgBuildType |
a2ee40ce |
if self.pkgBuildType == "container":
self.dockerClient = docker.from_env(version="auto") |
6c9b4b2f |
|
326d5ca8 |
def buildToolChain(self):
pkgCount = 0
try:
tUtils = ToolChainUtils()
pkgCount = tUtils.buildCoreToolChainPackages()
except Exception as e:
self.logger.error("Unable to build tool chain")
self.logger.error(e)
raise e
return pkgCount
def buildToolChainPackages(self, buildThreads):
pkgCount = self.buildToolChain()
if self.pkgBuildType == "container":
# Stage 1 build container
#TODO image name constants.buildContainerImageName |
8f56b626 |
if pkgCount > 0 or not self.dockerClient.images.list(constants.buildContainerImage): |
326d5ca8 |
self._createBuildContainer() |
9bc3518e |
self.logger.info("Step 2 : Building stage 2 of the toolchain...")
self.logger.info(constants.listToolChainPackages)
self.logger.info("") |
326d5ca8 |
self._buildGivenPackages(constants.listToolChainPackages, buildThreads) |
9bc3518e |
self.logger.info("The entire toolchain is now available")
self.logger.info(45 * '-')
self.logger.info("") |
326d5ca8 |
if self.pkgBuildType == "container":
# Stage 2 build container
#TODO: rebuild container only if anything in listToolChainPackages was built
self._createBuildContainer()
|
4b1a41cb |
def buildPackages(self, listPackages, buildThreads): |
326d5ca8 |
if constants.rpmCheck:
constants.rpmCheck = False
self.buildToolChainPackages(buildThreads)
self._buildTestPackages(buildThreads)
constants.rpmCheck = True
self._buildGivenPackages(listPackages, buildThreads)
else:
self.buildToolChainPackages(buildThreads) |
9bc3518e |
self.logger.info("Step 3 : Building the following package(s) and dependencies...")
self.logger.info(listPackages)
self.logger.info("") |
326d5ca8 |
self._buildGivenPackages(listPackages, buildThreads) |
26b55679 |
self.logger.info("Package build has been completed")
self.logger.info("") |
326d5ca8 |
def _readPackageBuildData(self, listPackages): |
518d6a6f |
try: |
87815216 |
pkgBuildDataGen = PackageBuildDataGenerator(self.logName, self.logPath)
self.mapCyclesToPackageList, self.mapPackageToCycle, self.sortedPackageList = (
pkgBuildDataGen.getPackageBuildData(listPackages))
except Exception as e:
self.logger.exception(e) |
2820c61a |
self.logger.error("unable to get sorted list")
return False
return True |
6c9b4b2f |
|
f93ef2b0 |
# Returns list of package names which spec file has all subpackages built
# Returns set of package name and version like
# ["name1-vers1", "name2-vers2",..] |
326d5ca8 |
def _readAlreadyAvailablePackages(self):
listAvailablePackages = set() |
87815216 |
pkgUtils = PackageUtils(self.logName, self.logPath) |
f93ef2b0 |
listPackages = SPECS.getData().getListPackages()
for package in listPackages:
for version in SPECS.getData().getVersions(package):
# Mark package available only if all subpackages are available
packageIsAlreadyBuilt=True
listRPMPackages = SPECS.getData().getRPMPackages(package, version)
for rpmPkg in listRPMPackages: |
6e3ba406 |
if pkgUtils.findRPMFileForGivenPackage(rpmPkg, version) is None: |
f93ef2b0 |
packageIsAlreadyBuilt=False
break;
if packageIsAlreadyBuilt:
listAvailablePackages.add(package+"-"+version)
|
26b55679 |
self.logger.debug("List of Already built packages")
self.logger.debug(listAvailablePackages) |
2820c61a |
return listAvailablePackages |
adf248d5 |
|
326d5ca8 |
def _calculateParams(self, listPackages): |
2820c61a |
self.mapCyclesToPackageList.clear()
self.mapPackageToCycle.clear() |
87815216 |
self.sortedPackageList = [] |
6c9b4b2f |
|
f93ef2b0 |
self.listOfPackagesAlreadyBuilt = list(self._readAlreadyAvailablePackages()) |
c04feb5d |
updateBuiltRPMSList = False
while not updateBuiltRPMSList:
updateBuiltRPMSList = True |
f93ef2b0 |
listOfPackagesAlreadyBuilt = self.listOfPackagesAlreadyBuilt |
c04feb5d |
for pkg in listOfPackagesAlreadyBuilt: |
f93ef2b0 |
packageName, packageVersion = StringUtils.splitPackageNameAndVersion(pkg)
listDependentRpmPackages = SPECS.getData().getRequiresAllForPackage(packageName, packageVersion) |
c04feb5d |
needToRebuild = False
for dependentPkg in listDependentRpmPackages:
if dependentPkg not in self.listOfPackagesAlreadyBuilt:
needToRebuild = True
updateBuiltRPMSList = False
if needToRebuild:
self.listOfPackagesAlreadyBuilt.remove(pkg) |
b5e09fac |
|
326d5ca8 |
listPackagesToBuild = copy.copy(listPackages) |
fe747196 |
for pkg in listPackages: |
87815216 |
if (pkg in self.listOfPackagesAlreadyBuilt and
not constants.rpmCheck): |
fe747196 |
listPackagesToBuild.remove(pkg) |
326d5ca8 |
if not self._readPackageBuildData(listPackagesToBuild): |
fe747196 |
return False |
2820c61a |
return True |
b5e09fac |
|
326d5ca8 |
def _buildTestPackages(self, buildThreads): |
b5e09fac |
self.buildToolChain() |
326d5ca8 |
self._buildGivenPackages(constants.listMakeCheckRPMPkgtoInstall, buildThreads) |
b5e09fac |
|
326d5ca8 |
def _initializeThreadPool(self, statusEvent): |
a5e4be9f |
ThreadPool.clear() |
87815216 |
ThreadPool.mapPackageToCycle = self.mapPackageToCycle
ThreadPool.logger = self.logger
ThreadPool.statusEvent = statusEvent
ThreadPool.pkgBuildType = self.pkgBuildType |
b5e09fac |
|
326d5ca8 |
def _initializeScheduler(self, statusEvent): |
26b55679 |
Scheduler.setLog(self.logName, self.logPath, self.logLevel) |
f93ef2b0 |
Scheduler.setParams(self.sortedPackageList, set(self.listOfPackagesAlreadyBuilt)) |
d024e640 |
Scheduler.setEvent(statusEvent) |
87815216 |
Scheduler.stopScheduling = False |
b5e09fac |
|
326d5ca8 |
def _buildGivenPackages(self, listPackages, buildThreads): |
f93ef2b0 |
# Extend listPackages from ["name1", "name2",..] to ["name1-vers1", "name2-vers2",..]
listPackageNamesAndVersions=[]
for pkg in listPackages:
for version in SPECS.getData().getVersions(pkg):
listPackageNamesAndVersions.append(pkg+"-"+version) |
26b55679 |
alreadyBuiltRPMS = self._readAlreadyAvailablePackages()
if alreadyBuiltRPMS:
self.logger.debug("List of already available packages:")
self.logger.debug(alreadyBuiltRPMS)
|
b5e09fac |
if constants.rpmCheck: |
f93ef2b0 |
listMakeCheckPackages=set()
for pkg in listPackages:
version = SPECS.getData().getHighestVersion(pkg)
listMakeCheckPackages.add(pkg+"-"+version)
listPackageNamesAndVersions = (list(set(listPackageNamesAndVersions)|(listMakeCheckPackages-alreadyBuiltRPMS))) |
b5e09fac |
|
f93ef2b0 |
returnVal = self._calculateParams(listPackageNamesAndVersions) |
a5e4be9f |
if not returnVal:
self.logger.error("Unable to set paramaters. Terminating the package manager.") |
895c821e |
raise Exception("Unable to set paramaters") |
b5e09fac |
|
9bc3518e |
listBasePackageNamesAndVersions = list(map(lambda x:SPECS.getData().getBasePkg(x), listPackageNamesAndVersions))
listPackagesToBuild = list((set(listBasePackageNamesAndVersions) - set(alreadyBuiltRPMS)))
if listPackagesToBuild:
self.logger.info("List of packages yet to be built...")
self.logger.info(listPackagesToBuild)
self.logger.info("") |
87815216 |
statusEvent = threading.Event() |
326d5ca8 |
self._initializeScheduler(statusEvent)
self._initializeThreadPool(statusEvent) |
b5e09fac |
|
326d5ca8 |
for i in range(0, buildThreads): |
87815216 |
workerName = "WorkerThread" + str(i) |
b37b4c63 |
ThreadPool.addWorkerThread(workerName)
ThreadPool.startWorkerThread(workerName) |
b5e09fac |
|
d024e640 |
statusEvent.wait() |
87815216 |
Scheduler.stopScheduling = True |
26b55679 |
self.logger.debug("Waiting for all remaining worker threads") |
326d5ca8 |
ThreadPool.join_all() |
b5e09fac |
|
87815216 |
setFailFlag = False
allPackagesBuilt = False |
d024e640 |
if Scheduler.isAnyPackagesFailedToBuild(): |
87815216 |
setFailFlag = True |
b5e09fac |
|
d024e640 |
if Scheduler.isAllPackagesBuilt(): |
87815216 |
allPackagesBuilt = True |
b5e09fac |
|
d024e640 |
if setFailFlag:
self.logger.error("Some of the packages failed:")
self.logger.error(Scheduler.listOfFailedPackages) |
895c821e |
raise Exception("Failed during building package") |
6c9b4b2f |
|
d024e640 |
if not setFailFlag:
if allPackagesBuilt: |
26b55679 |
self.logger.debug("All packages built successfully") |
d024e640 |
else:
self.logger.error("Build stopped unexpectedly.Unknown error.") |
895c821e |
raise Exception("Unknown error") |
b5e09fac |
|
326d5ca8 |
def _createBuildContainer(self): |
26b55679 |
self.logger.debug("Generating photon build container..") |
7418d2bf |
try:
#TODO image name constants.buildContainerImageName |
8f56b626 |
self.dockerClient.images.remove(constants.buildContainerImage, force=True) |
7418d2bf |
except Exception as e:
#TODO - better handling
self.logger.debug("Photon build container image not found.")
# Create toolchain chroot and install toolchain RPMs |
8f56b626 |
chroot = None |
7418d2bf |
try:
#TODO: constants.tcrootname |
8f56b626 |
chroot = Chroot(self.logger)
chroot.create("toolchain-chroot") |
7418d2bf |
tcUtils = ToolChainUtils("toolchain-chroot", self.logPath) |
8f56b626 |
tcUtils.installToolChainRPMS(chroot) |
7418d2bf |
except Exception as e: |
8f56b626 |
if chroot:
chroot.destroy() |
7418d2bf |
raise e |
8f56b626 |
self.logger.debug("createBuildContainer: " + chroot.getPath()) |
7418d2bf |
# Create photon build container using toolchain chroot |
8f56b626 |
chroot.unmountAll() |
7418d2bf |
#TODO: Coalesce logging
cmdUtils = CommandUtils() |
8f56b626 |
cmd = "cd " + chroot.getPath() + " && tar -czf ../tcroot.tar.gz ."
cmdUtils.runCommandInShell(cmd, logfn=self.logger.debug)
cmd = "mv " + chroot.getPath() + "/../tcroot.tar.gz ."
cmdUtils.runCommandInShell(cmd, logfn=self.logger.debug) |
7418d2bf |
#TODO: Container name, docker file name from constants. |
8f56b626 |
self.dockerClient.images.build(tag=constants.buildContainerImage, |
7418d2bf |
path=".",
rm=True,
dockerfile="Dockerfile.photon_build_container")
# Cleanup
cmd = "rm -f ./tcroot.tar.gz" |
8f56b626 |
cmdUtils.runCommandInShell(cmd, logfn=self.logger.debug)
chroot.destroy() |
26b55679 |
self.logger.debug("Photon build container successfully created.") |