Bo Lv | 9edb699 | 2021-09-06 11:31:00 +0800 | [diff] [blame] | 1 | #!/usr/bin/env python3 |
| 2 | |
| 3 | import sys |
| 4 | import os |
| 5 | import re |
| 6 | import requests |
| 7 | from requests.auth import HTTPBasicAuth |
| 8 | from urllib.parse import urljoin |
| 9 | import urllib.request |
| 10 | import json |
| 11 | import time |
| 12 | from os.path import expanduser |
| 13 | import jenkins |
| 14 | |
| 15 | |
Hangyu Li | b97619c | 2022-08-01 18:07:35 +0800 | [diff] [blame] | 16 | serverRootUrl = "https://jenkins-sh.amlogic.com/job/Security/job/" |
Bo Lv | 9edb699 | 2021-09-06 11:31:00 +0800 | [diff] [blame] | 17 | homeConfigFilePath = "~/.sign.cfg" |
Hangyu Li | 505e5eb | 2023-03-28 15:54:19 +0800 | [diff] [blame] | 18 | types = ["ta", "vmxta", "irdetota", "bl32", "bl31", "bl2", "bl2e", "bl2x", "bl40", "aucpufw", "vdecfw"] |
Hangyu Li | 0d2e705 | 2022-11-02 15:26:11 +0800 | [diff] [blame] | 19 | casProviders = ["", "VMX", "nagra", "nagra-dev", "vo-dev", "vo", "gs-dev", "gs", "irdeto"] |
Bo Lv | 9edb699 | 2021-09-06 11:31:00 +0800 | [diff] [blame] | 20 | ddrTypes = ["ddr4", "lpddr4", "ddr3", "lpddr3", "lpddr4_lpddr5"] |
| 21 | chipVariants = ["general", "nocs-jts-ap", "nocs-prod"] |
| 22 | |
| 23 | user = "" |
| 24 | password = "" |
| 25 | auth = None |
| 26 | server = None |
| 27 | |
| 28 | |
| 29 | def init(): |
| 30 | global user |
| 31 | global password |
| 32 | global auth |
| 33 | global server |
| 34 | expandedHomeConfigFilePath = expanduser(homeConfigFilePath) |
| 35 | configPyPath = os.path.join(sys.path[0], "config.py") |
| 36 | if os.path.exists(expandedHomeConfigFilePath): |
| 37 | configFilePath = expandedHomeConfigFilePath |
| 38 | elif os.path.exists(configPyPath): |
| 39 | configFilePath = configPyPath |
| 40 | else: |
| 41 | print( |
| 42 | "Can't find configuration file. Please create configuration file .sign.cfg at your home directory." |
| 43 | ) |
| 44 | exit(1) |
| 45 | |
| 46 | with open(configFilePath, "r") as configFile: |
| 47 | while True: |
| 48 | line = configFile.readline() |
| 49 | if not line: |
| 50 | break |
| 51 | words = line.split("=") |
| 52 | if words[0].strip() == "user": |
| 53 | user = words[1].strip().replace("'", "") |
| 54 | elif words[0].strip() == "password": |
| 55 | password = words[1].strip().replace("'", "") |
| 56 | auth = HTTPBasicAuth(user, password) |
| 57 | server = jenkins.Jenkins( |
| 58 | "https://jenkins-sh.amlogic.com", username=user, password=password |
| 59 | ) |
| 60 | |
| 61 | |
| 62 | def get_args(): |
| 63 | from argparse import ArgumentParser |
| 64 | |
| 65 | parser = ArgumentParser() |
| 66 | |
| 67 | parser.add_argument("--in", dest="inputFilePath", required=True, help="input file") |
| 68 | parser.add_argument( |
| 69 | "--chipAcsFile", dest="chipAcsFilePath", default="null", help="chip acs file" |
| 70 | ) |
| 71 | parser.add_argument( |
| 72 | "--out", dest="outFilePath", type=str, default="", help="output signed file" |
| 73 | ) |
| 74 | parser.add_argument( |
| 75 | "-v", "--version", action="version", version="%(prog)s 1.0", help="version" |
| 76 | ) |
| 77 | parser.add_argument("--type", choices=types, default=types[0], required=True) |
| 78 | parser.add_argument("--chip", type=str) |
| 79 | parser.add_argument("--taVersion", type=int, default=0) |
Hangyu Li | 4d44bba | 2022-05-11 14:33:08 +0800 | [diff] [blame] | 80 | parser.add_argument("--marketId", type=str, default="null") |
Bo Lv | 9edb699 | 2021-09-06 11:31:00 +0800 | [diff] [blame] | 81 | parser.add_argument("--casProvider", choices=casProviders, default=casProviders[0]) |
| 82 | parser.add_argument("--ddrType", type=str, default=ddrTypes[0]) |
| 83 | parser.add_argument("--chipVariant", choices=chipVariants, default=chipVariants[0]) |
| 84 | parser.add_argument("--keyType", type=str, dest="keyType", default="dev-keys") |
Tao Zeng | 4c3f74e | 2021-09-26 14:09:13 +0800 | [diff] [blame] | 85 | parser.add_argument("--extraArgs", type=str, default="") |
Pengguang Zhu | 899a8de | 2021-09-17 15:48:28 +0800 | [diff] [blame] | 86 | parser.add_argument("--testService", type=int, default=0) |
Bo Lv | 9edb699 | 2021-09-06 11:31:00 +0800 | [diff] [blame] | 87 | |
| 88 | return parser.parse_args() |
| 89 | |
| 90 | |
| 91 | def getLastBuildNumber(rootJobUrl): |
| 92 | url = urljoin(rootJobUrl, "lastBuild/buildNumber") |
| 93 | |
| 94 | response = requests.get(url, auth=auth) |
| 95 | |
| 96 | if response.status_code == 200: |
| 97 | return response.text |
| 98 | else: |
| 99 | print( |
| 100 | "Fail to get last build number due to the error " |
| 101 | + str(response.status_code) |
| 102 | ) |
| 103 | return 0 |
| 104 | |
| 105 | |
| 106 | def getJobRootUrl(type): |
| 107 | if type == "ta": |
Hangyu Li | b97619c | 2022-08-01 18:07:35 +0800 | [diff] [blame] | 108 | return urljoin(serverRootUrl, "Signing/job/Sign_TA/") |
| 109 | elif type == "vmxta": |
| 110 | return urljoin(serverRootUrl, "CAS/job/VMX/job/VMX_TA_Sign/") |
Hangyu Li | 505e5eb | 2023-03-28 15:54:19 +0800 | [diff] [blame] | 111 | elif type == "irdetota": |
| 112 | return urljoin(serverRootUrl, "Signing/job/Sign_Irdeto_TA_for_s905c1/") |
Bo Lv | 9edb699 | 2021-09-06 11:31:00 +0800 | [diff] [blame] | 113 | elif type == "bl31": |
Hangyu Li | b97619c | 2022-08-01 18:07:35 +0800 | [diff] [blame] | 114 | return urljoin(serverRootUrl, "Signing/job/Sign_Bl31/") |
Bo Lv | 9edb699 | 2021-09-06 11:31:00 +0800 | [diff] [blame] | 115 | elif type == "bl2": |
Hangyu Li | b97619c | 2022-08-01 18:07:35 +0800 | [diff] [blame] | 116 | return urljoin(serverRootUrl, "Signing/job/Sign_Bl2/") |
Bo Lv | 9edb699 | 2021-09-06 11:31:00 +0800 | [diff] [blame] | 117 | elif type == "bl32": |
Hangyu Li | b97619c | 2022-08-01 18:07:35 +0800 | [diff] [blame] | 118 | return urljoin(serverRootUrl, "Signing/job/Sign_Bl32/") |
Hangyu Li | 229ddee | 2022-03-24 13:57:51 +0800 | [diff] [blame] | 119 | elif type == "aucpufw": |
Hangyu Li | b97619c | 2022-08-01 18:07:35 +0800 | [diff] [blame] | 120 | return urljoin(serverRootUrl, "Signing/job/Sign_AUCPU_FW/") |
Hangyu Li | 22720d5 | 2022-03-29 11:07:20 +0800 | [diff] [blame] | 121 | elif type == "vdecfw": |
Hangyu Li | b97619c | 2022-08-01 18:07:35 +0800 | [diff] [blame] | 122 | return urljoin(serverRootUrl, "Signing/job/Sign_VDEC_FW/") |
Bo Lv | 9edb699 | 2021-09-06 11:31:00 +0800 | [diff] [blame] | 123 | else: # bl2e, bl2x, bl40 |
Hangyu Li | b97619c | 2022-08-01 18:07:35 +0800 | [diff] [blame] | 124 | return urljoin(serverRootUrl, "Signing/job/Sign_Bl2e_Bl2x_Bl40/") |
Bo Lv | 9edb699 | 2021-09-06 11:31:00 +0800 | [diff] [blame] | 125 | |
| 126 | |
| 127 | def getJobName(type): |
| 128 | if type == "ta": |
| 129 | return "Sign_TA" |
Hangyu Li | 505e5eb | 2023-03-28 15:54:19 +0800 | [diff] [blame] | 130 | elif type == "vmxta": |
| 131 | return "Sign_VMX_TA" |
| 132 | elif type == "irdetota": |
| 133 | return "Sign_Irdeto_TA_for_s905c1" |
Bo Lv | 9edb699 | 2021-09-06 11:31:00 +0800 | [diff] [blame] | 134 | elif type == "bl31": |
| 135 | return "Sign_Bl31" |
| 136 | elif type == "bl2": |
| 137 | return "Sign_Bl2" |
| 138 | elif type == "bl32": |
| 139 | return "Sign_Bl32" |
Hangyu Li | 229ddee | 2022-03-24 13:57:51 +0800 | [diff] [blame] | 140 | elif type == "aucpufw": |
| 141 | return "Sign_AUCPU_FW" |
Hangyu Li | 22720d5 | 2022-03-29 11:07:20 +0800 | [diff] [blame] | 142 | elif type == "vdecfw": |
| 143 | return "Sign_VDEC_FW" |
Bo Lv | 9edb699 | 2021-09-06 11:31:00 +0800 | [diff] [blame] | 144 | else: # bl2e, bl2x, bl40 |
| 145 | return "Sign_Bl2e_Bl2x_Bl40" |
| 146 | |
| 147 | |
| 148 | def submitSignJob( |
| 149 | type, |
| 150 | chipType, |
| 151 | inputFilePath, |
| 152 | chipAcsFilePath, |
| 153 | taVersion="0", |
Hangyu Li | 4d44bba | 2022-05-11 14:33:08 +0800 | [diff] [blame] | 154 | marketId="", |
Bo Lv | 9edb699 | 2021-09-06 11:31:00 +0800 | [diff] [blame] | 155 | casProvider="", |
| 156 | chipVariant="", |
| 157 | ddrType="", |
| 158 | keyType="dev-keys", |
Tao Zeng | 4c3f74e | 2021-09-26 14:09:13 +0800 | [diff] [blame] | 159 | extraArgs="", |
Pengguang Zhu | 899a8de | 2021-09-17 15:48:28 +0800 | [diff] [blame] | 160 | testService=0, |
Bo Lv | 9edb699 | 2021-09-06 11:31:00 +0800 | [diff] [blame] | 161 | ): |
| 162 | |
| 163 | fileName = os.path.basename(inputFilePath) |
| 164 | fileParameter = "file" |
| 165 | uploadFile = { |
| 166 | fileParameter: (fileName, open(inputFilePath, "rb")), |
| 167 | } |
| 168 | url = getJobRootUrl(type) + "buildWithParameters" |
Hangyu Li | 505e5eb | 2023-03-28 15:54:19 +0800 | [diff] [blame] | 169 | if type == "ta" or type == "vmxta" or type == "irdetota": |
Bo Lv | 9edb699 | 2021-09-06 11:31:00 +0800 | [diff] [blame] | 170 | data = { |
| 171 | "chip_part_number": chipType, |
| 172 | "ta_version": taVersion, |
Hangyu Li | 4d44bba | 2022-05-11 14:33:08 +0800 | [diff] [blame] | 173 | "market_id": marketId, |
Pengguang Zhu | 899a8de | 2021-09-17 15:48:28 +0800 | [diff] [blame] | 174 | "testService": testService, |
Bo Lv | 9edb699 | 2021-09-06 11:31:00 +0800 | [diff] [blame] | 175 | } |
| 176 | elif type == "bl32": |
| 177 | |
| 178 | data = { |
| 179 | "chipPartNumber": chipType, |
| 180 | "casProvider": casProvider, |
| 181 | "keyType": keyType, |
Pengguang Zhu | 899a8de | 2021-09-17 15:48:28 +0800 | [diff] [blame] | 182 | "testService": testService, |
Bo Lv | 9edb699 | 2021-09-06 11:31:00 +0800 | [diff] [blame] | 183 | } |
| 184 | |
| 185 | elif type == "bl2": |
| 186 | chipAcsfileName = os.path.basename(chipAcsFilePath) |
| 187 | uploadFile = { |
| 188 | fileParameter: (fileName, open(inputFilePath, "rb")), |
| 189 | "chipAcsFile": (chipAcsfileName, open(chipAcsFilePath, "rb")), |
| 190 | } |
| 191 | data = { |
| 192 | "chipPartNumber": chipType, |
| 193 | "chipVariant": chipVariant, |
| 194 | "ddrType": ddrType, |
| 195 | "keyType": keyType, |
Pengguang Zhu | 899a8de | 2021-09-17 15:48:28 +0800 | [diff] [blame] | 196 | "testService": testService, |
Bo Lv | 9edb699 | 2021-09-06 11:31:00 +0800 | [diff] [blame] | 197 | } |
Hangyu Li | 22720d5 | 2022-03-29 11:07:20 +0800 | [diff] [blame] | 198 | else: # bl2e, bl2x, bl31, bl40, aucpufw, vdecfw |
Pengguang Zhu | 899a8de | 2021-09-17 15:48:28 +0800 | [diff] [blame] | 199 | data = { |
| 200 | "chipPartNumber": chipType, |
| 201 | "keyType": keyType, |
Tao Zeng | 4c3f74e | 2021-09-26 14:09:13 +0800 | [diff] [blame] | 202 | "extraArgs": extraArgs, |
Pengguang Zhu | 899a8de | 2021-09-17 15:48:28 +0800 | [diff] [blame] | 203 | "testService": testService, |
| 204 | } |
Bo Lv | 9edb699 | 2021-09-06 11:31:00 +0800 | [diff] [blame] | 205 | |
| 206 | response = requests.post(url, auth=auth, data=data, files=uploadFile) |
| 207 | |
| 208 | if response.status_code == 201: |
| 209 | print("Sumbit signing job successfully, please wait...") |
| 210 | |
| 211 | else: |
| 212 | print( |
| 213 | "Fail to start signing job due to the error: " + str(response.status_code) |
| 214 | ) |
| 215 | exit(1) |
| 216 | |
| 217 | |
| 218 | def queryBuildStatus(rootJobUrl, buildNumber): |
| 219 | url = rootJobUrl + str(buildNumber) + "/api/json?tree=building" |
| 220 | |
| 221 | response = requests.get(url, auth=auth) |
| 222 | |
| 223 | if response.status_code == 200: |
| 224 | result = json.loads(response.text) |
| 225 | return str(result["building"]) |
| 226 | else: |
| 227 | return "NotStart" |
| 228 | |
Hangyu Li | 4fac3cd | 2023-07-03 17:39:34 +0800 | [diff] [blame] | 229 | def queryBuildUser(rootJobUrl, buildNumber): |
| 230 | url = rootJobUrl + str(buildNumber) + "/api/json?tree=actions[causes[userId]]" |
| 231 | |
| 232 | response = requests.get(url, auth=auth) |
| 233 | |
| 234 | if response.status_code == 200: |
| 235 | result = json.loads(response.text) |
| 236 | return str(result["actions"][1]["causes"][0]["userId"]) |
| 237 | else: |
| 238 | return "Error" |
Bo Lv | 9edb699 | 2021-09-06 11:31:00 +0800 | [diff] [blame] | 239 | |
Hangyu Li | 56e85b6 | 2023-09-28 16:08:09 +0800 | [diff] [blame] | 240 | def checkBuildParameter(rootJobUrl, buildNumber, argsChip, argsCasProvider, argsKeyType): |
| 241 | url = rootJobUrl + str(buildNumber) + "/api/json" |
| 242 | job = rootJobUrl + str(buildNumber) |
| 243 | |
| 244 | response = requests.get(url, auth=auth) |
| 245 | data = response.json() |
| 246 | |
| 247 | # parse JSON data to get chipPartNumber, casProvider, keyType |
| 248 | actions = data.get("actions", []) |
| 249 | for action in actions: |
| 250 | parameters = action.get("parameters", []) |
| 251 | for parameter in parameters: |
| 252 | if parameter.get("_class") == "hudson.model.StringParameterValue": |
| 253 | if parameter.get("name") == "chipPartNumber": |
| 254 | chipPartNumber = parameter.get("value") |
| 255 | if chipPartNumber != argsChip: |
| 256 | print("The jenkins Build job %s chip value doesn't match with yours." % (job)) |
| 257 | return False |
| 258 | |
| 259 | if parameter.get("name") == "casProvider": |
| 260 | casProvider = parameter.get("value") |
| 261 | if casProvider != argsCasProvider: |
| 262 | print("The jenkins Build job %s casProvider value doesn't match with yours." % (job)) |
| 263 | return False |
| 264 | |
| 265 | if parameter.get("name") == "keyType": |
| 266 | keyType = parameter.get("value") |
| 267 | if keyType != argsKeyType: |
| 268 | print("The jenkins Build job %s keyType value doesn't match with yours." % (job)) |
| 269 | return False |
| 270 | |
| 271 | return True |
| 272 | |
Bo Lv | 9edb699 | 2021-09-06 11:31:00 +0800 | [diff] [blame] | 273 | def downloadSignedFile(rootJobUrl, buildNumber, inFileDir="", specifiedOutFilePath=""): |
| 274 | |
| 275 | url = rootJobUrl + str(buildNumber) + "/api/json?tree=artifacts[relativePath]" |
| 276 | |
| 277 | response = requests.get(url, auth=auth) |
| 278 | |
| 279 | if response.status_code == 200: |
| 280 | result = json.loads(response.text) |
| 281 | if len(result["artifacts"]) == 0: |
| 282 | print("Fail to build, please check jenkins log for detailed error") |
| 283 | exit(1) |
| 284 | relativePath = result["artifacts"][0]["relativePath"] |
| 285 | # http://127.0.0.1:8080/job/Sign_Bl31/46/artifact/46/output/bl31-payload.bin.signed |
| 286 | downloadUrl = rootJobUrl + str(buildNumber) + "/artifact/" + "/" + relativePath |
| 287 | if specifiedOutFilePath == "": |
| 288 | outFilePath = os.path.join(inFileDir, os.path.basename(relativePath)) |
| 289 | else: |
| 290 | outFilePath = specifiedOutFilePath |
| 291 | r = requests.get(downloadUrl, auth=auth) |
| 292 | with open(outFilePath, "wb") as f: |
| 293 | f.write(r.content) |
| 294 | print("Download the signed file at " + outFilePath) |
| 295 | return 0 |
| 296 | else: |
| 297 | print("Fail to download the signed file") |
| 298 | exit(1) |
| 299 | return 1 |
| 300 | |
| 301 | |
| 302 | def waitForSubmit(type): |
| 303 | jobName = getJobName(type) |
| 304 | |
| 305 | while True: |
| 306 | queues = server.get_queue_info() |
| 307 | inQueue = False |
| 308 | if queues: |
| 309 | for queue_job_info in queues: |
| 310 | if queue_job_info["task"].get("name", "") == jobName: |
| 311 | inQueue = True |
| 312 | break |
| 313 | if inQueue: |
| 314 | time.sleep(1) |
| 315 | print( |
| 316 | "Otherone is signing same firmware as you request. Please wait them to complete." |
| 317 | ) |
| 318 | else: |
| 319 | print("It is your turn to submit your signing job now.") |
| 320 | break |
| 321 | |
| 322 | |
| 323 | def main(): |
| 324 | print(sys.argv) |
| 325 | init() |
| 326 | args = get_args() |
| 327 | |
| 328 | rootJobUrl = getJobRootUrl(args.type) |
| 329 | |
| 330 | waitForSubmit(args.type) |
| 331 | lastBuildNumber = getLastBuildNumber(rootJobUrl) |
| 332 | submitSignJob( |
| 333 | type=args.type, |
| 334 | chipType=args.chip, |
| 335 | inputFilePath=args.inputFilePath, |
| 336 | chipAcsFilePath=args.chipAcsFilePath, |
| 337 | taVersion=args.taVersion, |
Hangyu Li | 4d44bba | 2022-05-11 14:33:08 +0800 | [diff] [blame] | 338 | marketId=args.marketId, |
Bo Lv | 9edb699 | 2021-09-06 11:31:00 +0800 | [diff] [blame] | 339 | casProvider=args.casProvider, |
| 340 | chipVariant=args.chipVariant, |
| 341 | ddrType=args.ddrType, |
| 342 | keyType=args.keyType, |
Tao Zeng | 4c3f74e | 2021-09-26 14:09:13 +0800 | [diff] [blame] | 343 | extraArgs=args.extraArgs, |
Pengguang Zhu | 899a8de | 2021-09-17 15:48:28 +0800 | [diff] [blame] | 344 | testService=args.testService, |
Bo Lv | 9edb699 | 2021-09-06 11:31:00 +0800 | [diff] [blame] | 345 | ) |
| 346 | |
| 347 | buildNumber = int(lastBuildNumber) + 1 |
| 348 | print("The jenkins build number: " + str(buildNumber)) |
| 349 | while True: |
| 350 | time.sleep(1) |
| 351 | building = queryBuildStatus(rootJobUrl, buildNumber) |
| 352 | print("Building Status= " + str(building)) |
| 353 | if building == "False": |
Hangyu Li | 4fac3cd | 2023-07-03 17:39:34 +0800 | [diff] [blame] | 354 | userId = queryBuildUser(rootJobUrl, buildNumber) |
| 355 | if userId.lower() != user.lower(): |
| 356 | print("The jenkins Build number user name doesn't match yours.It may be caused by too many signing requests submit to the same job.") |
| 357 | print("Please wait a moment, and try again.") |
| 358 | exit(1) |
| 359 | |
Hangyu Li | 56e85b6 | 2023-09-28 16:08:09 +0800 | [diff] [blame] | 360 | status = checkBuildParameter(rootJobUrl, buildNumber, args.chip, args.casProvider, args.keyType) |
| 361 | if status == False: |
| 362 | print("It may be caused by too many signing requests submit to the same job.") |
| 363 | print("Please wait a moment, and try again.") |
| 364 | exit(1) |
| 365 | |
Bo Lv | 9edb699 | 2021-09-06 11:31:00 +0800 | [diff] [blame] | 366 | print("Build is done. Will start to download the signed file") |
| 367 | break |
Hangyu Li | 4fac3cd | 2023-07-03 17:39:34 +0800 | [diff] [blame] | 368 | |
Bo Lv | 9edb699 | 2021-09-06 11:31:00 +0800 | [diff] [blame] | 369 | inputFileDir = os.path.dirname(args.inputFilePath) |
| 370 | downloadSignedFile(rootJobUrl, buildNumber, inputFileDir, args.outFilePath) |
| 371 | |
| 372 | |
| 373 | if __name__ == "__main__": |
| 374 | main() |