1# encoding=utf-8 2 3""" 4====================================================================================== 5copyright (C) 2018-2019, Huawei Technologies Co. 6======================================================================================== 7@文件名称: Download.py 8@描述: 文件下载相关动作 9@作者: lwx587017 10@生成日期: 20181019 11====================================================================================== 12""" 13import codecs 14import os 15import shutil 16import urllib 17import time 18import sys 19import re 20import datetime 21import subprocess 22import platform 23import traceback 24import requests 25import json 26import threading 27import zipfile 28 29# from util.config import * 30from util.log_info import logger 31from util.time_info import get_now_time_str_info, timeout 32 33current_path = os.path.dirname(os.path.dirname(os.path.abspath(os.path.dirname(__file__)))) 34BitCometjar = os.path.join(current_path, "Resource", "thirdtools", "TTorrentFull.jar") 35from aw.Common.Constant import CONSTANT 36 37from aw.Common.Common import subProcessPopen, copyFile 38 39Aria2cbin = CONSTANT.SysTool.ARIA2CBIN 40 41 42def downloadByBitComet(source_path, download_dir, os_method): 43 ''' 44 #=================================================================================== 45 # @Method: downloadByBitComet(source_path, dest_path) 46 # @Precondition: none 47 # @Func: 通过bt下载,将资源文件下载到本地 48 # @PostStatus: none 49 # @Param: source_path:资源文件路径 50 # download_dir:文件下载到本地的文件夹路径 51 # os_method:绑定操作系统用到公共方法的对象 52 # @eg: downloadByBitComet("xxxx", "D:\\local\\image", os_method) 53 # @return: True or Flase 54 #=================================================================================== 55 ''' 56 server_ip = "100.104.224.115" 57 server_port = "2976" 58 source_path = source_path.replace(' ', '%20') 59 torrent_exist_url = "http://" + server_ip + ":" + server_port + "/centerDistributeService/BtCenter/addTaskDistribute?path=" \ 60 + source_path 61 logger.info(torrent_exist_url) 62 version_path, destdir = os.path.split(download_dir) 63 64 exist_log = os.path.join(version_path, destdir + "_exist.log") 65 ret = False 66 for _ in range(600): 67 urllib.request.urlretrieve(torrent_exist_url, exist_log) 68 if os.path.isfile(exist_log): 69 open_file = codecs.open(exist_log, "r", "utf-8") 70 content = open_file.readline() 71 if str(content).find("\"status\":\"done\"") != -1: 72 ret = True 73 logger.info("The torrent of %s created seccessfully" % source_path) 74 break 75 else: 76 time.sleep(6) 77 logger.info("The torrent of %s is creating , please wait for a moment" % source_path) 78 continue 79 logger.info(ret) 80 if not ret: 81 logger.info("The torrent of %s created failed" % source_path) 82 return False 83 torrent_down_url = "http://" + server_ip + ":" + server_port + "/centerDistributeService/BtCenter/seed?path=" \ 84 + source_path 85 download_torrent = os.path.join(version_path, destdir + ".torrent") 86 for _ in range(3): 87 urllib.request.urlretrieve(torrent_down_url, download_torrent) 88 if os.path.isfile(download_torrent): 89 ret = False 90 logger.info("The torrent of %s downloaded successed" % source_path) 91 break 92 else: 93 time.sleep(3) 94 continue 95 if ret: 96 logger.info("The torrent of %s downloaded failed" % source_path) 97 return False 98 99 if os.path.isfile(Aria2cbin): 100 BT_Command = "aria2c -l - -d %s --enable-dht=false --seed-time=0 --log-level=warn \ 101 --max-download-limit=50M --bt-stop-timeout=120 %s" % (version_path, download_torrent) 102 else: 103 BT_Command = "java -cp %s com.turn.ttorrent.demo.Downloader %s %s" % ( 104 BitCometjar, download_torrent, version_path) 105 result = os.system(BT_Command) 106 if result: 107 logger.error("BT-Downloader returns: %s, download failed." % result) 108 return False 109 for ld in os.listdir(version_path): 110 if len(ld) == 32 and os.path.isdir(os.path.join(version_path, ld)): 111 copyDirectory(os.path.join(version_path, ld), download_dir, os_method) 112 shutil.rmtree(os.path.join(version_path, ld), True) 113 return True 114 115 116def waitotherdown(suc_mark): 117 ''' 118 #=================================================================================== 119 # @Method: waitotherdown(suc_mark) 120 # @Precondition: none 121 # @Func: 如果版本相同且此版本没下载成功过,会等待正在下载的任务去下载, 122 # 等待时长2小时,若超过2小时正在下载的任务还未下载成功,则自行下载 123 # @PostStatus: none 124 # @Param: suc_mark:成功标志文件的路径 125 # @eg: waitotherdown("xxx\\suc.txt") 126 # @return: True or Flase 127 #=================================================================================== 128 ''' 129 logger.info("wait other process download") 130 for i in range(240): 131 checktime = 5 132 time.sleep(checktime) 133 if os.path.isfile(suc_mark): 134 logger.info("%s exists. Package has been download successfully" \ 135 " by other process." % suc_mark) 136 break 137 if i == 80: 138 logger.error("wait other process download timeout") 139 return False 140 return True 141 142 143def isDownLoadSuccess(download_dir, suc_mark, failed_mark): 144 ''' 145 #=================================================================================== 146 # @Method: isDownLoadSuccess(download_dir, suc_mark, failed_mark) 147 # @Precondition: none 148 # @Func: 下载文件前,判断此文件是否已下载成功或失败,有下载失败标志,则重新下载 149 # ,若此文件未下载过,则创建文件夹重新下载 150 # @PostStatus: none 151 # @Param: download_dir:文件下载到本地的文件夹路径路径 152 # suc_mark:成功标志文件的路径 153 # @eg: isDownLoadSuccess("xxx", "xxx\\suc.txt", "xxx\\fail.txt") 154 # @return: True or Flase 155 #=================================================================================== 156 ''' 157 if os.path.isfile(suc_mark): 158 logger.info("%s has been download by other process." % download_dir) 159 return True 160 elif os.path.isfile(failed_mark): 161 logger.info("%s exists. Other process has download package and failed. It will download by self" % failed_mark) 162 os.remove(failed_mark) 163 else: 164 try: 165 # 如果当前版本的目录存在,则认为版本已经下载 166 if not os.path.isdir(download_dir): 167 logger.debug("mkdir %s" % download_dir) 168 os.makedirs(download_dir) 169 else: 170 logger.debug("%s is already downloading by other process,please wait for a moment!" % download_dir) 171 ret = waitotherdown(suc_mark) 172 return ret 173 except Exception as e: 174 logger.error("make dir fail with %s" % e) 175 shutil.rmtree(download_dir) 176 177 return False 178 179 180def saveVersion(save_path_file, versionsavepath): 181 ''' 182 #=================================================================================== 183 # @Method: saveVersion(save_path_file, versionsavepath) 184 # @Precondition: none 185 # @Func: 此方法用于文件下载,如果文件下载成功,就缓存版本记录到record/xxx.txt中, 186 # 格式:/data/MobileUpgrade/local_img/1663de89-4a0b-5b26-bfad-5d2fd7bf0740 187 # @PostStatus: none 188 # @Param: save_path_file:文件名称,例5c0d84a3a51fff875f8ed4de16791b1c7d3d2cfe.txt 189 # versionsavepath:本地存储路径 190 # @eg: saveVersion(save_path_file, versionsavepath) 191 # @return: None 192 #=================================================================================== 193 ''' 194 savepath = os.path.dirname(save_path_file) 195 if not os.path.isdir(savepath): 196 logger.debug("mkdir %s" % savepath) 197 os.makedirs(savepath) 198 if os.path.isfile(save_path_file): 199 logger.info("%s exists." % save_path_file) 200 else: 201 logger.info("save_path_file: %s" % save_path_file) 202 with open(save_path_file, "a") as f: 203 f.write(versionsavepath) 204 205 206def downlaodByDownloadTool(dir_path, version_type, upgrade_type, pbiid): 207 ''' 208 #=================================================================================== 209 # @Method: downlaodByDownloadTool(dir_path, version_type, upgrade_type, pbiid) 210 # @Precondition: none 211 # @Func: 用下载工具提供的jar包进行下载,需要传入版本的pbiid,升级类型,版本类型, 212 # 具体使用方法可咨询wwx515469 213 # @PostStatus: none 214 # @Param: dir_path:本地储存版本的路径 215 # version_type:版本的类型,如odex,dex,no_log 216 # upgrade_type:升级的类型,如FASTBOOT,SD 217 # pbiid:版本的pbiid号 218 # os_method:绑定操作系统用到公共方法的对象 219 # @eg: downlaodByDownloadTool(dir_path, version_type, upgrade_type, pbiid, os_method) 220 # @return: None 221 #=================================================================================== 222 ''' 223 upgrade_type = upgrade_type.upper() 224 jar_path = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(os.path.dirname(__file__)))), "resource", 225 "thirdtools", "downloadTool.jar") 226 os_name = platform.system() 227 logger.debug("copy res to dir_path") 228 res_path = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(os.path.dirname(__file__)))), "resource", 229 "thirdtools", "res") 230 local_res_path = os.path.join(dir_path, "res") 231 if not os.path.isdir(local_res_path): 232 os.makedirs(local_res_path) 233 if os_method.copyDirectory(res_path, local_res_path): 234 logger.info("copy res to the local image path sucess") 235 else: 236 logger.info("copy res to the local image path fail") 237 return False 238 if (os_name == "Linux"): 239 jar_cmd = "cd %s & java -jar %s %s %s:%s" % (dir_path, jar_path, pbiid, upgrade_type, version_type) 240 else: 241 jar_cmd = "D: & cd %s & java -jar %s %s %s:%s" % (dir_path, jar_path, pbiid, upgrade_type, version_type) 242 logger.info("download by download.jar,cmd is %s" % jar_cmd) 243 ret = subprocess.getoutput(jar_cmd) 244 logger.info("the result is %s after excute downloadTool.jar" % ret) 245 if "download success" in ret: 246 list_jardir = os.listdir(dir_path) 247 for dir in list_jardir: 248 jardir = os.path.join(dir_path, dir) 249 if os.path.isdir(jardir) and jardir != "res": 250 os.rename(jardir, os.path.join(dir_path, "jarimg")) 251 break 252 logger.info("download success by downloadjar") 253 return True 254 else: 255 if "failreason" in ret: 256 com = re.search("failreason\((.*)\)", ret) 257 failreason = com.group(1) 258 logger.info("download fail by downloadjar,failreson is %s" % failreason) 259 else: 260 logger.info("download fail by downloadjar,and jartool not support failreason") 261 return False 262 263 264def downloadLinkFileFromHttp(link, path): 265 ''' 266 #=================================================================================== 267 # @Method: downloadLinkFileFromHttp(link, path) 268 # @Precondition: none 269 # @Func: 通过http下载资源文件到本地 270 # @PostStatus: none 271 # @Param: link:资源文件路径 272 # path:本地文件路径 273 # @eg: downloadLinkFileFromHttp("http:xxx", "D:\local_img") 274 # @return: True or False 275 #=================================================================================== 276 ''' 277 file_name = link.split('/')[-1] 278 destination_file = os.path.join(path, file_name) 279 logger.info('file_name is %s' % file_name) 280 logger.info('destination_file is %s' % destination_file) 281 if not os.path.isdir(path): 282 os.makedirs(path) 283 if not os.path.isfile(destination_file): 284 downloadFileFromHttp(link, destination_file) 285 286 if not os.path.isfile(destination_file): 287 logger.error('download link file error') 288 return False 289 else: 290 return True 291 292 293def downloadFileFromHttp(file_link, destination_file): 294 ''' 295 #=================================================================================== 296 # @Method: downloadFileFromHttp(file_link, destination_file) 297 # @Precondition: none 298 # @Func: 通过http执行下载动作 299 # @PostStatus: none 300 # @Param: file_link:资源文件路径 301 # destination_file:本地文件路径 302 # @eg: destination_file("http:xxx", "D:\local_img") 303 # @return: None 304 #=================================================================================== 305 ''' 306 import urllib 307 logger.info("download file from %s to %s" % (file_link, destination_file)) 308 urllib.request.urlretrieve(file_link, destination_file, scheduleDownloadPercent) 309 310 311def scheduleDownloadPercent(downloadsize, blocksize, filesize): 312 ''' 313 #=================================================================================== 314 # @Method: scheduleDownloadPercent(downloadsize, blocksize, filesize) 315 # @Precondition: none 316 # @Func: 打印当前下载的进度,用百分比形式显示 317 # @PostStatus: none 318 # @Param: downloadsize:已下载文件的大小 319 # blocksize:下载块数 320 # filesize:总文件大小 321 # @eg: scheduleDownloadPercent(xxx,2,xxx ) 322 # @return: None 323 #=================================================================================== 324 ''' 325 percent = 100.0 * downloadsize * blocksize / filesize 326 if percent >= 100: 327 percent = 100 328 downloadpercent = 100 329 percent = float("%.4f" % percent) 330 if percent % 1 == 0: 331 logger.info("file download percent:%.0f%%" % percent) 332 333 334def downloadByCopyWindows(source_path, download_dir, is_file): 335 ''' 336 #=================================================================================== 337 # @Method: excutedown(source_path, download_dir, is_file, params) 338 # @Precondition: none 339 # @Func: 执行下载动作 340 # @PostStatus: none 341 # @Param: source_path:资源文件路径 342 # download_dir:文件下载到本地的文件夹路径 343 # is_file:是否是文件 344 # @eg: excutedown("xxxx", "D:\\local\\image", Flase) 345 # @return: True or Flase 346 #=================================================================================== 347 ''' 348 try: 349 if not is_file: 350 for f in os.listdir(source_path): 351 file_name = os.path.join(source_path, f) 352 child_path = os.path.join(download_dir, f) 353 if os.path.isfile(file_name): 354 copyFile(file_name, child_path, 32 * 1024) 355 if not os.path.isfile(child_path): 356 logger.error("Copy %s to %s failed" % (file_name, child_path)) 357 return False 358 logger.info("Copy %s to %s successful" % (file_name, child_path)) 359 else: 360 if not os.path.isdir(child_path): 361 logger.info("Mkdir %s successful" % child_path) 362 os.makedirs(child_path) 363 ret = downloadByCopyWindows(file_name, child_path, False) 364 if not ret: 365 return False 366 else: 367 copyFile(source_path, download_dir) 368 return True 369 except Exception as e: 370 logger.error(e) 371 return False 372 return True 373 374 375def downloadByCopyLinux(source_path, download_dir, is_file): 376 ''' 377 #=================================================================================== 378 # @Method: excutedown(source_path, download_dir, is_file, params) 379 # @Precondition: none 380 # @Func: 执行下载动作 381 # @PostStatus: none 382 # @Param: source_path:资源文件路径 383 # download_dir:文件下载到本地的文件夹路径 384 # is_file:是否是文件 385 # @eg: excutedown("xxxx", "D:\\local\\image", Flase) 386 # @return: True or Flase 387 #=================================================================================== 388 ''' 389 try: 390 region = "" 391 source_path = source_path.replace("\\", "/") 392 temp = source_path.split("/") 393 host, share, path, file = '', '', '', '' 394 if is_file: 395 file = temp.pop() 396 else: 397 file = '*' 398 for i in temp: 399 if i: 400 if not host: 401 host = i 402 elif not share: 403 share = i 404 else: 405 path = os.path.join(path, i) 406 else: 407 continue 408 cmd = ("smbclient //%s/%s" % (host, share) + " -U CHINA/" 409 + CONSTANT.OSType.ACCOUNT 410 + "%'" 411 + CONSTANT.OSType.PASSWORD 412 + "' -c 'cd \"%s\"'" % path) 413 result = subProcessPopen(cmd) 414 if not result["ret"]: 415 logger.error("invalid samba path.") 416 return False 417 cmd = ("smbclient //%s/%s" % (host, share) + " -U CHINA/" 418 + CONSTANT.OSType.ACCOUNT 419 + "%'" 420 + CONSTANT.OSType.PASSWORD) 421 if region == "green": 422 cmd = cmd + "' -m SMB2 -c 'cd \"%s\";prompt OFF;recurse ON;lcd %s;mget %s'" % (path, download_dir, file) 423 else: 424 cmd = cmd + "' -c 'cd \"%s\";prompt OFF;recurse ON;lcd %s;mget %s'" % (path, download_dir, file) 425 logger.debug(cmd) 426 result = subProcessPopen(cmd) 427 if ("connect failed" in str(result["out"]) or "NT_STATUS_IO_TIMEOUT" in str(result["out"])) or ( 428 not result["ret"]): 429 return False 430 else: 431 return True 432 except Exception: 433 traceback.print_exc() 434 return False 435 436 437def downloadByCopy(source_path, download_dir, is_file): 438 os_name = platform.system() 439 if os_name == "Windows": 440 return downloadByCopyWindows(source_path, download_dir, is_file) 441 if os_name == "Linux": 442 return downloadByCopyLinux(source_path, download_dir, is_file) 443 444 445@timeout(240) 446def getToken(domin_name, name, password): 447 ''' 448 #=================================================================================== 449 # @Method: getToken(domin_name, name, passwd, proxy) 450 # @Precondition: none 451 # @Func: 通过http下载蓝区文件 452 # @PostStatus: none 453 # @Param: domin_name: 帐号名 454 # username:用户名 455 # password:密码 456 # 457 # @eg: getToken("xxx", "xxx", "xxx") 458 # @return: None 459 #=================================================================================== 460 ''' 461 body_1 = { 462 "auth": { 463 "identity": { 464 "methods": [ 465 "password" 466 ], 467 "password": { 468 "user": { 469 "domain": { 470 "name": domin_name 471 }, 472 "name": name, 473 "password": password 474 } 475 } 476 }, 477 "scope": { 478 "project": { 479 "name": "cn-north-4" 480 } 481 } 482 } 483 } 484 header_1 = {"content-type": "application/json;charset=UTF-8"} 485 resp = requests.request("POST", "https://iam.myhuaweicloud.com/v3/auth/tokens", data=json.dumps(body_1), 486 headers=header_1) 487 # print("auth result:", resp.status_code, resp.headers) 488 return resp.headers.get("X-Subject-Token") 489 490 491def downloadFileFromDevCloud(link, destination_file, start, end): 492 ''' 493 #=================================================================================== 494 # @Method: downloadFileFromDevCloud(link, username, password, path) 495 # @Precondition: none 496 # @Func: 通过http下载蓝区文件 497 # @PostStatus: none 498 # @Param: link:资源文件路径 499 # username:用户名 500 # password:密码 501 # path:本地文件路径 502 # @eg: downloadFileFromDevCloud("http:xxx", "xxx", "xxx","D:\local_img") 503 # @return: None 504 #=================================================================================== 505 ''' 506 fd = open(destination_file, 'wb+') 507 dname = decrypt(CONSTANT.Key.DEV_DNAME, CONSTANT.Key.DEV_KEY) 508 pw = decrypt(CONSTANT.Key.DEV_PASSWD, CONSTANT.Key.DEV_KEY) 509 try: 510 try: 511 token = getToken("hwstaff_harmonyos_ci", dname, pw) 512 logger.info("get token end") 513 except Exception as f: 514 logger.error(f) 515 time.sleep(3) 516 logger.info("get token again") 517 token = getToken("hwstaff_harmonyos_ci", dname, pw) 518 logger.info("get token again end") 519 heard = {'Range': 'Bytes=%s-%s' % (start, end), "content-type": "application/json;charset=UTF-8", 520 "X-Language": "zh-cn", "X-Auth-Token": token} 521 try: 522 res = requests.get(link, headers=heard, stream=True, timeout=240) 523 logger.info("connect status: %s" % res.status_code) 524 except Exception as p: 525 logger.error(p) 526 time.sleep(3) 527 logger.info("connect again!") 528 res = requests.get(link, headers=heard, stream=True, timeout=240) 529 logger.info("connect again status: %s" % res.status_code) 530 fd.seek(start) 531 fd.write(res.content) 532 if res.status_code == 206 or res.status_code == 200: 533 if not os.path.isfile(destination_file): 534 logger.error('download link file and write file error') 535 return False 536 else: 537 logger.error("downloadFromDevCloud fail, error: %s" % res) 538 return False 539 except Exception as e: 540 traceback.print_exc() 541 logger.error(e) 542 543 544def decrypt(text, key): 545 from Crypto.Cipher import AES 546 from binascii import a2b_hex 547 cryptor = AES.new(key.encode('utf-8'), AES.MODE_CBC, b'0000000000000000') 548 plain_text = cryptor.decrypt(a2b_hex(text)) 549 return bytes.decode(plain_text).rstrip('\0') 550 551 552def run_download(link, path): 553 try: 554 num = 3 555 r = requests.head(link) 556 total = int(r.headers['Content-Length']) 557 logger.info('total is %s' % total) 558 ranges = [] 559 offset = int(total / num) 560 for i in range(num): 561 if i == num - 1: 562 ranges.append((i * offset, '')) 563 else: 564 ranges.append((i * offset, (i + 1) * offset)) 565 # 保存文件打开对象 566 if "file_id=" in link: 567 if link.endswith(".tar.gz"): 568 file_name = link.split('=')[-1] 569 else: 570 file_name = "out.tar.gz" 571 else: 572 file_name = link.split('/')[-1] 573 destination_file = os.path.join(path, file_name) 574 logger.info('file_name is %s' % file_name) 575 logger.info('destination_file is %s' % destination_file) 576 if not os.path.isdir(path): 577 os.makedirs(path) 578 if os.path.isfile(destination_file): 579 logger.info('%s is exit' % destination_file) 580 return True 581 # 保存文件打开对象 582 logger.info("downloadFileFromDevCloud start") 583 logger.info(link) 584 thread_list = [] 585 # 一个数字,用来标记打印每个线程 586 n = 0 587 for ran in ranges: 588 start, end = ran 589 # 打印信息 590 logger.info('thread %d start:%s,end:%s' % (n, start, end)) 591 n += 1 592 # 创建线程 传参,处理函数为download 593 thread = threading.Thread(target=downloadFileFromDevCloud, args=(link, destination_file, start, end)) 594 # 启动 595 thread.start() 596 thread_list.append(thread) 597 for i in thread_list: 598 # 设置等待 599 i.join() 600 if os.path.isfile(destination_file): 601 logger.info('%s is exit' % destination_file) 602 return True 603 return False 604 except Exception as p: 605 logger.error(p) 606 return False