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