Browse code

Priority scheduler for Photon Build

Change-Id: I077ab48c4d2aae7295fec49cef279ba45ce53981
Reviewed-on: http://photon-jenkins.eng.vmware.com:8082/2919
Reviewed-by: Divya Thaluru <dthaluru@vmware.com>
Tested-by: gerrit-photon <photon-checkins@vmware.com>

himkalyan authored on 2017/06/24 01:39:34
Showing 4 changed files
... ...
@@ -63,6 +63,18 @@ else
63 63
 PHOTON_RPMCHECK_FLAGS :=
64 64
 endif
65 65
 
66
+ifeq ($(BUILDDEPS),true)
67
+PUBLISH_BUILD_DEPENDENCIES := -bd True
68
+else
69
+PUBLISH_BUILD_DEPENDENCIES :=
70
+endif
71
+
72
+ifdef WEIGHTS
73
+PACKAGE_WEIGHTS_PATH = -pw $(WEIGHTS)
74
+else
75
+PACKAGE_WEIGHTS_PATH =
76
+endif
77
+
66 78
 TOOLS_BIN := $(SRCROOT)/tools/bin
67 79
 CONTAIN := $(TOOLS_BIN)/contain
68 80
 VIXDISKUTIL := $(TOOLS_BIN)/vixdiskutil
... ...
@@ -111,6 +123,8 @@ packages-micro: check-tools $(PHOTON_STAGE) $(PHOTON_PUBLISH_RPMS) $(PHOTON_SOUR
111 111
                 -n $(PHOTON_BUILD_NUMBER) \
112 112
                 -v $(PHOTON_RELEASE_VERSION) \
113 113
                 $(PHOTON_RPMCHECK_FLAGS) \
114
+		$(PUBLISH_BUILD_DEPENDENCIES) \
115
+		$(PACKAGE_WEIGHTS_PATH) \
114 116
                 -t ${THREADS}
115 117
 
116 118
 live-iso: check-tools $(PHOTON_STAGE) $(PHOTON_PACKAGES_MINIMAL)
... ...
@@ -145,6 +159,8 @@ packages-minimal: check-tools $(PHOTON_STAGE) $(PHOTON_PUBLISH_RPMS) $(PHOTON_SO
145 145
                 -n $(PHOTON_BUILD_NUMBER) \
146 146
                 -v $(PHOTON_RELEASE_VERSION) \
147 147
                 $(PHOTON_RPMCHECK_FLAGS) \
148
+		$(PUBLISH_BUILD_DEPENDENCIES) \
149
+		$(PACKAGE_WEIGHTS_PATH) \
148 150
                 -t ${THREADS}
149 151
 
150 152
 iso: check-tools $(PHOTON_STAGE) $(PHOTON_PACKAGES)
... ...
@@ -228,6 +244,8 @@ packages: check-tools $(PHOTON_STAGE) $(PHOTON_PUBLISH_XRPMS) $(PHOTON_PUBLISH_R
228 228
                 -w $(PHOTON_STAGE)/pkg_info.json \
229 229
                 -g $(PHOTON_DATA_DIR)/pkg_build_options.json \
230 230
                 $(PHOTON_RPMCHECK_FLAGS) \
231
+		$(PUBLISH_BUILD_DEPENDENCIES) \
232
+		$(PACKAGE_WEIGHTS_PATH) \
231 233
                 -t ${THREADS}
232 234
 
233 235
 updated-packages: check-tools $(PHOTON_STAGE) $(PHOTON_PUBLISH_XRPMS) $(PHOTON_PUBLISH_RPMS) $(PHOTON_SOURCES) $(CONTAIN) generate-dep-lists
... ...
@@ -247,6 +265,8 @@ updated-packages: check-tools $(PHOTON_STAGE) $(PHOTON_PUBLISH_XRPMS) $(PHOTON_P
247 247
                 -v $(PHOTON_RELEASE_VERSION) \
248 248
                 -k $(PHOTON_INPUT_RPMS_DIR) \
249 249
                 $(PHOTON_RPMCHECK_FLAGS) \
250
+		$(PUBLISH_BUILD_DEPENDENCIES) \
251
+		$(PACKAGE_WEIGHTS_PATH) \
250 252
                 -t ${THREADS}
251 253
 
252 254
 tool-chain-stage1: check-tools $(PHOTON_STAGE) $(PHOTON_PUBLISH_RPMS) $(PHOTON_SOURCES) $(CONTAIN) generate-dep-lists
... ...
@@ -1,7 +1,9 @@
1
+import json
1 2
 import ThreadPool
2 3
 from constants import constants
3 4
 from Logger import Logger
4
-import threading 
5
+import threading
6
+from Queue import PriorityQueue
5 7
 
6 8
 class Scheduler(object):
7 9
     
... ...
@@ -10,8 +12,13 @@ class Scheduler(object):
10 10
     listOfPackagesToBuild=[]
11 11
     listOfPackagesCurrentlyBuilding=[]
12 12
     sortedList=[]
13
-    listOfPackagesNextToBuild=[]
13
+    listOfPackagesNextToBuild=PriorityQueue()
14 14
     listOfFailedPackages=[]
15
+    alldependencyGraph = {}
16
+    dependencyGraph = {}
17
+    priorityMap = {}
18
+    pkgWeights={}
19
+    isPriorityScheduler=1
15 20
     logger=None
16 21
     event=None
17 22
     stopScheduling=False
... ...
@@ -23,7 +30,109 @@ class Scheduler(object):
23 23
     @staticmethod
24 24
     def setLog(logName,logPath):
25 25
         Scheduler.logger = Logger.getLogger(logName, logPath)    
26
-        
26
+
27
+    @staticmethod
28
+    def getBuildRequiredPackages(package):
29
+        listRequiredRPMPackages = []
30
+        listRequiredRPMPackages.extend(constants.specData.getBuildRequiresForPackage(package))
31
+
32
+        listRequiredPackages = []
33
+
34
+        for pkg in listRequiredRPMPackages:
35
+            basePkg = constants.specData.getSpecName(pkg)
36
+            if basePkg not in listRequiredPackages:
37
+                listRequiredPackages.append(basePkg)
38
+
39
+        return listRequiredPackages
40
+
41
+
42
+    @staticmethod
43
+    def getDependencies(package, parentPackage, k):
44
+
45
+        for node in Scheduler.alldependencyGraph[package].keys():
46
+                Scheduler.getDependencies(node, package, k)
47
+
48
+        if parentPackage == None:
49
+            return
50
+        else:
51
+            for node in Scheduler.alldependencyGraph[package].keys():
52
+                try:
53
+                    Scheduler.alldependencyGraph[parentPackage][node] = max(
54
+                        Scheduler.alldependencyGraph[parentPackage][node],
55
+                        Scheduler.alldependencyGraph[package][node] * k)
56
+                except KeyError:
57
+                    Scheduler.alldependencyGraph[parentPackage][node] = \
58
+                        Scheduler.alldependencyGraph[package][node] * k
59
+
60
+    @staticmethod
61
+    def makeGraph():
62
+        k = 3
63
+        for package in Scheduler.sortedList:
64
+            for child_pkg in Scheduler.dependencyGraph[package].keys():
65
+                Scheduler.getDependencies(child_pkg, package, k)
66
+                for node in Scheduler.alldependencyGraph[child_pkg].keys():
67
+                    try:
68
+                        Scheduler.dependencyGraph[package][node] = max(
69
+                            Scheduler.dependencyGraph[package][node],
70
+                            Scheduler.alldependencyGraph[child_pkg][node] * k)
71
+                    except KeyError:
72
+                        Scheduler.dependencyGraph[package][node] = \
73
+                            Scheduler.alldependencyGraph[child_pkg][node] * k
74
+	if constants.publishBuildDependencies:
75
+	    dependencyLists = {}
76
+	    for package in Scheduler.dependencyGraph.keys():
77
+		dependencyLists[package] = []
78
+		for dependency in Scheduler.dependencyGraph[package].keys():
79
+			dependencyLists[package].append(dependency)
80
+	    graphfile = open(str(constants.logPath) + "/BuildDependencies.json", 'w')
81
+	    graphfile.write(json.dumps(dependencyLists, sort_keys=True, indent=4))
82
+	    graphfile.close()
83
+
84
+    @staticmethod
85
+    def parseWeights():
86
+	Scheduler.pkgWeights.clear()
87
+	weightFile = open(constants.packageWeightsPath, 'r')
88
+	Scheduler.pkgWeights = json.load(weightFile)
89
+	weightFile.close()
90
+
91
+    @staticmethod
92
+    def getWeight(package):
93
+        try:
94
+            return float(Scheduler.pkgWeights[package])
95
+        except KeyError:
96
+            return 0
97
+
98
+
99
+
100
+    @staticmethod
101
+    def setPriorities():
102
+	if constants.packageWeightsPath == None:
103
+            Scheduler.logger.info("Priority Scheduler disabled")
104
+            Scheduler.isPriorityScheduler = 0
105
+	else:
106
+	    Scheduler.parseWeights()
107
+
108
+        for package in Scheduler.sortedList:
109
+            Scheduler.dependencyGraph[package] = {}
110
+            Scheduler.alldependencyGraph[package] = {}
111
+            for child_package in Scheduler.getBuildRequiredPackages(package):
112
+                Scheduler.dependencyGraph[package][child_package] = 1
113
+            for child_package in Scheduler.getRequiredPackages(package):
114
+                Scheduler.alldependencyGraph[package][child_package] = 1
115
+        Scheduler.makeGraph()
116
+        for package in Scheduler.sortedList:
117
+            try:
118
+                Scheduler.priorityMap[package] = Scheduler.getWeight(package)
119
+            except KeyError:
120
+                Scheduler.priorityMap[package] = 0
121
+            for child_pkg in Scheduler.dependencyGraph[package].keys():
122
+                Scheduler.priorityMap[child_pkg] = Scheduler.priorityMap[child_pkg] \
123
+                                                 + (Scheduler.dependencyGraph[package][child_pkg]
124
+                                                    * (Scheduler.getWeight(package)))
125
+        Scheduler.logger.info("set Priorities: Priority of all packages")
126
+        Scheduler.logger.info(Scheduler.priorityMap)
127
+
128
+
27 129
     @staticmethod
28 130
     def setParams(sortedList,listOfAlreadyBuiltPackages):
29 131
         Scheduler.sortedList=sortedList
... ...
@@ -34,6 +143,7 @@ class Scheduler(object):
34 34
         Scheduler.listOfPackagesCurrentlyBuilding=[]
35 35
         Scheduler.listOfPackagesNextToBuild=[]
36 36
         Scheduler.listOfFailedPackages=[]
37
+        Scheduler.setPriorities()
37 38
         
38 39
     @staticmethod
39 40
     def getRequiredPackages(package):
... ...
@@ -52,7 +162,7 @@ class Scheduler(object):
52 52
     
53 53
     @staticmethod
54 54
     def __getListNextPackagesReadyToBuild():
55
-        listOfPackagesNextToBuild=[]
55
+        listOfPackagesNextToBuild=PriorityQueue()
56 56
         Scheduler.logger.info("Checking for next possible packages to build")
57 57
         for pkg in Scheduler.listOfPackagesToBuild:
58 58
             if pkg in Scheduler.listOfPackagesCurrentlyBuilding:
... ...
@@ -67,37 +177,49 @@ class Scheduler(object):
67 67
                     Scheduler.logger.info(reqPkg+" is not available. So we cannot build "+ pkg +" at this moment.")
68 68
                     break
69 69
             if canBuild:
70
-                listOfPackagesNextToBuild.append(pkg)
70
+                listOfPackagesNextToBuild.put((-Scheduler.priorityMap[pkg], pkg))
71 71
                 Scheduler.logger.info("Adding "+ pkg +" to the schedule list")
72 72
         return listOfPackagesNextToBuild
73
-    
73
+
74 74
     @staticmethod
75 75
     def getNextPackageToBuild():
76 76
         Scheduler.logger.info("Waiting to acquire scheduler lock")
77 77
         Scheduler.lock.acquire()
78
-        
78
+
79 79
         if Scheduler.stopScheduling:
80 80
             Scheduler.logger.info("Released scheduler lock")
81 81
             Scheduler.lock.release()
82 82
             return None
83
-        
83
+
84 84
         if len(Scheduler.listOfPackagesToBuild) == 0:
85 85
             if Scheduler.event is not None:
86 86
                 Scheduler.event.set()
87
-            
88
-        if len(Scheduler.listOfPackagesNextToBuild) == 0:
89
-            listOfPackagesNextToBuild=Scheduler.__getListNextPackagesReadyToBuild()
90
-            Scheduler.listOfPackagesNextToBuild=listOfPackagesNextToBuild
91
-            
92
-        if len(Scheduler.listOfPackagesNextToBuild) == 0:
87
+
88
+        try:
89
+            if Scheduler.listOfPackagesNextToBuild.qsize() == 0:
90
+                listOfPackagesNextToBuild = Scheduler.__getListNextPackagesReadyToBuild()
91
+                Scheduler.listOfPackagesNextToBuild = listOfPackagesNextToBuild
92
+        except:
93
+            if len(Scheduler.listOfPackagesNextToBuild) == 0:
94
+                listOfPackagesNextToBuild = Scheduler.__getListNextPackagesReadyToBuild()
95
+                Scheduler.listOfPackagesNextToBuild = listOfPackagesNextToBuild
96
+
97
+        if Scheduler.listOfPackagesNextToBuild.qsize() == 0:
93 98
             Scheduler.logger.info("Released scheduler lock")
94 99
             Scheduler.lock.release()
95 100
             return None
96
-        
97
-        package=Scheduler.listOfPackagesNextToBuild.pop(0)
98
-        
99
-        if len(Scheduler.listOfPackagesNextToBuild) > 0:
100
-            ThreadPool.ThreadPool.activateWorkerThreads(len(Scheduler.listOfPackagesNextToBuild))
101
+
102
+        packageTup=Scheduler.listOfPackagesNextToBuild.get()
103
+
104
+        if packageTup[0] == 0 and Scheduler.isPriorityScheduler == 1:
105
+            listOfPackagesNextToBuild = Scheduler.__getListNextPackagesReadyToBuild()
106
+            Scheduler.listOfPackagesNextToBuild = listOfPackagesNextToBuild
107
+            packageTup = Scheduler.listOfPackagesNextToBuild.get()
108
+
109
+        package = packageTup[1]
110
+        Scheduler.logger.info("PackagesNextToBuild " + str(packageTup))
111
+        if Scheduler.listOfPackagesNextToBuild.qsize() > 0:
112
+            ThreadPool.ThreadPool.activateWorkerThreads(Scheduler.listOfPackagesNextToBuild.qsize())
101 113
         Scheduler.logger.info("Released scheduler lock")
102 114
         Scheduler.lock.release()
103 115
         Scheduler.listOfPackagesCurrentlyBuilding.append(package)
... ...
@@ -130,5 +252,4 @@ class Scheduler(object):
130 130
         if len(Scheduler.listOfFailedPackages) != 0:
131 131
             return True
132 132
         return False
133
-        
134
-        
135 133
\ No newline at end of file
134
+
... ...
@@ -38,7 +38,8 @@ def main():
38 38
     parser.add_argument("-w",  "--pkginfo-file",  dest="pkgInfoFile",  default="../../stage/pkg_info.json")
39 39
     parser.add_argument("-g",  "--pkg-build-option-file",  dest="pkgBuildOptionFile",  default="../../common/data/pkg_build_options.json")
40 40
     parser.add_argument("-q",  "--rpmcheck-stop-on-error", dest="rpmCheckStopOnError",  default=False, action ="store_true")
41
-
41
+    parser.add_argument("-bd", "--publish-build-dependencies", dest="publishBuildDependencies", default=False)
42
+    parser.add_argument("-pw", "--package-weights-path", dest="packageWeightsPath", default=None)
42 43
     parser.add_argument("-y",  "--generate-pkg-yaml-files",  dest="generatePkgYamlFiles",  default=False, action ="store_true")
43 44
     parser.add_argument("-j",  "--pkg-yaml-dir-path",  dest="pkgYamlDirPath",  default="../../stage/")
44 45
     parser.add_argument("-f",  "--pkg-blacklist-file",  dest="pkgBlacklistFile",  default=None)
... ...
@@ -49,7 +50,6 @@ def main():
49 49
         cmdUtils.runCommandInShell("mkdir -p "+options.logPath)
50 50
 
51 51
     logger=Logger.getLogger(options.logPath+"/Main")
52
-
53 52
     errorFlag=False
54 53
     package = None
55 54
     pkgInfoJsonFile = options.pkgInfoFile
... ...
@@ -84,6 +84,10 @@ def main():
84 84
         logger.error("Given input RPMS Path is not a directory:"+options.inputRPMSPath)
85 85
         errorFlag = True
86 86
 
87
+    if options.packageWeightsPath is not None and not os.path.isfile(options.packageWeightsPath):
88
+        logger.error("Given input Weights file is not a file:"+options.packageWeightsPath)
89
+        errorFlag = True
90
+
87 91
     if options.generatePkgYamlFiles:
88 92
         if options.pkgBlacklistFile is not None and options.pkgBlacklistFile != "" and not os.path.isfile(options.pkgBlacklistFile):
89 93
             logger.error("Given package blacklist file is not valid:"+options.pkgBlacklistFile)
... ...
@@ -152,7 +156,6 @@ def main():
152 152
         # print stacktrace
153 153
         traceback.print_exc()
154 154
         sys.exit(1)
155
-
156 155
     sys.exit(0)
157 156
 
158 157
 def buildPackagesList(csvFilename):
... ...
@@ -20,7 +20,8 @@ class constants(object):
20 20
     rpmCheck=False
21 21
     sourceRpmPath=""
22 22
     noDepsPackageList=["texinfo","bzip2","gettext","nspr","xz","bison","go"]
23
-
23
+    publishBuildDependencies=False
24
+    packageWeightsPath=None
24 25
     # These packages will be built in first order as build-core-toolchain stage
25 26
     listCoreToolChainPackages=[
26 27
         "filesystem",
... ...
@@ -328,6 +329,8 @@ class constants(object):
328 328
         constants.testForceRPMS=[]
329 329
         constants.rpmCheck = options.rpmCheck
330 330
         constants.rpmCheckStopOnError = options.rpmCheckStopOnError
331
+	constants.publishBuildDependencies=options.publishBuildDependencies
332
+	constants.packageWeightsPath=options.packageWeightsPath
331 333
         if constants.rpmCheck:
332 334
             constants.testLogger=Logger.getLogger("MakeCheckTest",constants.logPath)
333 335