1#!/usr/bin/env python3 2# coding=utf-8 3 4# 5# Copyright (c) 2022 Huawei Device Co., Ltd. 6# Licensed under the Apache License, Version 2.0 (the "License"); 7# you may not use this file except in compliance with the License. 8# You may obtain a copy of the License at 9# 10# http://www.apache.org/licenses/LICENSE-2.0 11# 12# Unless required by applicable law or agreed to in writing, software 13# distributed under the License is distributed on an "AS IS" BASIS, 14# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15# See the License for the specific language governing permissions and 16# limitations under the License. 17# 18 19import copy 20import os 21import re 22import shutil 23import socket 24import sys 25import time 26import platform 27import argparse 28import subprocess 29import signal 30import uuid 31import json 32import stat 33from datetime import timezone 34from datetime import timedelta 35from datetime import datetime 36from tempfile import NamedTemporaryFile 37 38from _core.executor.listener import SuiteResult 39from _core.driver.parser_lite import ShellHandler 40from _core.exception import ParamError 41from _core.exception import ExecuteTerminate 42from _core.logger import platform_logger 43from _core.report.suite_reporter import SuiteReporter 44from _core.plugin import get_plugin 45from _core.plugin import Plugin 46from _core.constants import ModeType 47from _core.constants import ConfigConst 48 49LOG = platform_logger("Utils") 50 51 52def get_filename_extension(file_path): 53 _, fullname = os.path.split(file_path) 54 filename, ext = os.path.splitext(fullname) 55 return filename, ext 56 57 58def unique_id(type_name, value): 59 return "{}_{}_{:0>8}".format(type_name, value, 60 str(uuid.uuid1()).split("-")[0]) 61 62 63def start_standing_subprocess(cmd, pipe=subprocess.PIPE, return_result=False): 64 """Starts a non-blocking subprocess that is going to continue running after 65 this function returns. 66 67 A subprocess group is actually started by setting sid, so we can kill all 68 the processes spun out from the subprocess when stopping it. This is 69 necessary in case users pass in pipe commands. 70 71 Args: 72 cmd: Command to start the subprocess with. 73 pipe: pipe to get execution result 74 return_result: return execution result or not 75 76 Returns: 77 The subprocess that got started. 78 """ 79 sys_type = platform.system() 80 process = subprocess.Popen(cmd, stdout=pipe, shell=False, 81 preexec_fn=None if sys_type == "Windows" 82 else os.setsid) 83 if not return_result: 84 return process 85 else: 86 rev = process.stdout.read() 87 return rev.decode("utf-8").strip() 88 89 90def stop_standing_subprocess(process): 91 """Stops a subprocess started by start_standing_subprocess. 92 93 Catches and ignores the PermissionError which only happens on Macs. 94 95 Args: 96 process: Subprocess to terminate. 97 """ 98 try: 99 sys_type = platform.system() 100 signal_value = signal.SIGINT if sys_type == "Windows" \ 101 else signal.SIGTERM 102 os.kill(process.pid, signal_value) 103 except (PermissionError, AttributeError, FileNotFoundError, # pylint:disable=undefined-variable 104 SystemError) as error: 105 LOG.error("Stop standing subprocess error '%s'" % error) 106 107 108def get_decode(stream): 109 if not isinstance(stream, str) and not isinstance(stream, bytes): 110 ret = str(stream) 111 else: 112 try: 113 ret = stream.decode("utf-8", errors="ignore") 114 except (ValueError, AttributeError, TypeError) as _: 115 ret = str(stream) 116 return ret 117 118 119def is_proc_running(pid, name=None): 120 if platform.system() == "Windows": 121 pid = "{}.exe".format(pid) 122 proc_sub = subprocess.Popen(["C:\\Windows\\System32\\tasklist"], 123 stdout=subprocess.PIPE, 124 shell=False) 125 proc = subprocess.Popen(["C:\\Windows\\System32\\findstr", "/B", "%s" % pid], 126 stdin=proc_sub.stdout, 127 stdout=subprocess.PIPE, shell=False) 128 elif platform.system() == "Linux": 129 # /bin/ps -ef | /bin/grep -v grep | /bin/grep -w pid 130 proc_sub = subprocess.Popen(["/bin/ps", "-ef"], 131 stdout=subprocess.PIPE, 132 shell=False) 133 proc_v_sub = subprocess.Popen(["/bin/grep", "-v", "grep"], 134 stdin=proc_sub.stdout, 135 stdout=subprocess.PIPE, 136 shell=False) 137 proc = subprocess.Popen(["/bin/grep", "-w", "%s" % pid], 138 stdin=proc_v_sub.stdout, 139 stdout=subprocess.PIPE, shell=False) 140 elif platform.system() == "Darwin": 141 proc_sub = subprocess.Popen(["/bin/ps", "-ef"], 142 stdout=subprocess.PIPE, 143 shell=False) 144 proc_v_sub = subprocess.Popen(["/usr/bin/grep", "-v", "grep"], 145 stdin=proc_sub.stdout, 146 stdout=subprocess.PIPE, 147 shell=False) 148 proc = subprocess.Popen(["/usr/bin/grep", "-w", "%s" % pid], 149 stdin=proc_v_sub.stdout, 150 stdout=subprocess.PIPE, shell=False) 151 else: 152 raise Exception("Unknown system environment") 153 154 (out, _) = proc.communicate(timeout=60) 155 out = get_decode(out).strip() 156 LOG.debug("Check %s proc running output: %s", pid, out) 157 if out == "": 158 return False 159 else: 160 return True if name is None else out.find(name) != -1 161 162 163def exec_cmd(cmd, timeout=5 * 60, error_print=True, join_result=False, redirect=False): 164 """ 165 Executes commands in a new shell. Directing stderr to PIPE. 166 167 This is fastboot's own exe_cmd because of its peculiar way of writing 168 non-error info to stderr. 169 170 Args: 171 cmd: A sequence of commands and arguments. 172 timeout: timeout for exe cmd. 173 error_print: print error output or not. 174 join_result: join error and out 175 redirect: redirect output 176 Returns: 177 The output of the command run. 178 """ 179 # PIPE本身可容纳的量比较小,所以程序会卡死,所以一大堆内容输出过来的时候,会导致PIPE不足够处理这些内容,因此需要将输出内容定位到其他地方,例如临时文件等 180 import tempfile 181 out_temp = tempfile.SpooledTemporaryFile(max_size=10 * 1000) 182 fileno = out_temp.fileno() 183 184 sys_type = platform.system() 185 if sys_type == "Linux" or sys_type == "Darwin": 186 if redirect: 187 proc = subprocess.Popen(cmd, stdout=fileno, 188 stderr=fileno, shell=False, 189 preexec_fn=os.setsid) 190 else: 191 proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, 192 stderr=subprocess.PIPE, shell=False, 193 preexec_fn=os.setsid) 194 else: 195 if redirect: 196 proc = subprocess.Popen(cmd, stdout=fileno, 197 stderr=fileno, shell=False) 198 else: 199 proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, 200 stderr=subprocess.PIPE, shell=False) 201 try: 202 (out, err) = proc.communicate(timeout=timeout) 203 err = get_decode(err).strip() 204 out = get_decode(out).strip() 205 if err and error_print: 206 LOG.exception(err, exc_info=False) 207 if join_result: 208 return "%s\n %s" % (out, err) if err else out 209 else: 210 return err if err else out 211 212 except (TimeoutError, KeyboardInterrupt, AttributeError, ValueError, # pylint:disable=undefined-variable 213 EOFError, IOError) as _: 214 sys_type = platform.system() 215 if sys_type == "Linux" or sys_type == "Darwin": 216 os.killpg(proc.pid, signal.SIGTERM) 217 else: 218 os.kill(proc.pid, signal.SIGINT) 219 raise 220 221 222def create_dir(path): 223 """Creates a directory if it does not exist already. 224 225 Args: 226 path: The path of the directory to create. 227 """ 228 full_path = os.path.abspath(os.path.expanduser(path)) 229 if not os.path.exists(full_path): 230 os.makedirs(full_path, exist_ok=True) 231 232 233def get_config_value(key, config_dict, is_list=True, default=None): 234 """Get corresponding values for key in config_dict 235 236 Args: 237 key: target key in config_dict 238 config_dict: dictionary that store values 239 is_list: decide return values is list type or not 240 default: if key not in config_dict, default value will be returned 241 242 Returns: 243 corresponding values for key 244 """ 245 if not isinstance(config_dict, dict): 246 return default 247 248 value = config_dict.get(key, None) 249 if isinstance(value, bool): 250 return value 251 252 if value is None: 253 if default is not None: 254 return default 255 return [] if is_list else "" 256 257 if isinstance(value, list): 258 return value if is_list else value[0] 259 return [value] if is_list else value 260 261 262def get_file_absolute_path(input_name, paths=None, alt_dir=None): 263 """Find absolute path for input_name 264 265 Args: 266 input_name: the target file to search 267 paths: path list for searching input_name 268 alt_dir: extra dir that appended to paths 269 270 Returns: 271 absolute path for input_name 272 """ 273 LOG.debug("Input name:{}, paths:{}, alt dir:{}". 274 format(input_name, paths, alt_dir)) 275 input_name = str(input_name) 276 abs_paths = set(paths) if paths else set() 277 _update_paths(abs_paths) 278 279 _inputs = [input_name] 280 if input_name.startswith("resource/"): 281 _inputs.append(input_name.replace("resource/", "", 1)) 282 elif input_name.startswith("testcases/"): 283 _inputs.append(input_name.replace("testcases/", "", 1)) 284 elif input_name.startswith("resource\\"): 285 _inputs.append(input_name.replace("resource\\", "", 1)) 286 elif input_name.startswith("testcases\\"): 287 _inputs.append(input_name.replace("testcases\\", "", 1)) 288 289 for _input in _inputs: 290 for path in abs_paths: 291 if alt_dir: 292 file_path = os.path.join(path, alt_dir, _input) 293 if os.path.exists(file_path): 294 return os.path.abspath(file_path) 295 296 file_path = os.path.join(path, _input) 297 if os.path.exists(file_path): 298 return os.path.abspath(file_path) 299 300 err_msg = "The file {} does not exist".format(input_name) 301 if check_mode(ModeType.decc): 302 LOG.error(err_msg, error_no="00109") 303 err_msg = "Load Error[00109]" 304 305 if alt_dir: 306 LOG.debug("Alt dir is %s" % alt_dir) 307 LOG.debug("Paths is:") 308 for path in abs_paths: 309 LOG.debug(path) 310 raise ParamError(err_msg, error_no="00109") 311 312 313def _update_paths(paths): 314 from xdevice import Variables 315 resource_dir = "resource" 316 testcases_dir = "testcases" 317 318 need_add_path = set() 319 for path in paths: 320 if not os.path.exists(path): 321 continue 322 head, tail = os.path.split(path) 323 if not tail: 324 head, tail = os.path.split(head) 325 if tail in [resource_dir, testcases_dir]: 326 need_add_path.add(head) 327 paths.update(need_add_path) 328 329 inner_dir = os.path.abspath(os.path.join(Variables.exec_dir, 330 testcases_dir)) 331 top_inner_dir = os.path.abspath(os.path.join(Variables.top_dir, 332 testcases_dir)) 333 res_dir = os.path.abspath(os.path.join(Variables.exec_dir, resource_dir)) 334 top_res_dir = os.path.abspath(os.path.join(Variables.top_dir, 335 resource_dir)) 336 paths.update([inner_dir, res_dir, top_inner_dir, top_res_dir, 337 Variables.exec_dir, Variables.top_dir]) 338 339 340def modify_props(device, local_prop_file, target_prop_file, new_props): 341 """To change the props if need 342 Args: 343 device: the device to modify props 344 local_prop_file : the local file to save the old props 345 target_prop_file : the target prop file to change 346 new_props : the new props 347 Returns: 348 True : prop file changed 349 False : prop file no need to change 350 """ 351 is_changed = False 352 device.pull_file(target_prop_file, local_prop_file) 353 old_props = {} 354 changed_prop_key = [] 355 lines = [] 356 flags = os.O_RDONLY 357 modes = stat.S_IWUSR | stat.S_IRUSR 358 with os.fdopen(os.open(local_prop_file, flags, modes), "r") as old_file: 359 lines = old_file.readlines() 360 if lines: 361 lines[-1] = lines[-1] + '\n' 362 for line in lines: 363 line = line.strip() 364 if not line.startswith("#") and line.find("=") > 0: 365 key_value = line.split("=") 366 if len(key_value) == 2: 367 old_props[line.split("=")[0]] = line.split("=")[1] 368 369 for key, value in new_props.items(): 370 if key not in old_props.keys(): 371 lines.append("".join([key, "=", value, '\n'])) 372 is_changed = True 373 elif old_props.get(key) != value: 374 changed_prop_key.append(key) 375 is_changed = True 376 377 if is_changed: 378 local_temp_prop_file = NamedTemporaryFile(mode='w', prefix='build', 379 suffix='.tmp', delete=False) 380 for index, line in enumerate(lines): 381 if not line.startswith("#") and line.find("=") > 0: 382 key = line.split("=")[0] 383 if key in changed_prop_key: 384 lines[index] = "".join([key, "=", new_props[key], '\n']) 385 local_temp_prop_file.writelines(lines) 386 local_temp_prop_file.close() 387 device.push_file(local_temp_prop_file.name, target_prop_file) 388 device.execute_shell_command(" ".join(["chmod 644", target_prop_file])) 389 LOG.info("Changed the system property as required successfully") 390 os.remove(local_temp_prop_file.name) 391 392 return is_changed 393 394 395def get_device_log_file(report_path, serial=None, log_name="device_log", 396 device_name="", module_name=None): 397 from xdevice import Variables 398 # new a module folder to save log 399 if module_name: 400 log_path = os.path.join(report_path, Variables.report_vars.log_dir, module_name) 401 else: 402 log_path = os.path.join(report_path, Variables.report_vars.log_dir) 403 os.makedirs(log_path, exist_ok=True) 404 405 serial = serial or time.time_ns() 406 if device_name: 407 serial = "%s_%s" % (device_name, serial) 408 device_file_name = "{}_{}.log".format(log_name, str(serial).replace( 409 ":", "_")) 410 device_log_file = os.path.join(log_path, device_file_name) 411 LOG.info("Generate device log file: %s", device_log_file) 412 return device_log_file 413 414 415def check_result_report(report_root_dir, report_file, error_message="", 416 report_name="", module_name="", **kwargs): 417 """ 418 Check whether report_file exits or not. If report_file is not exist, 419 create empty report with error_message under report_root_dir 420 """ 421 422 if os.path.exists(report_file): 423 return report_file 424 report_dir = os.path.dirname(report_file) 425 if os.path.isabs(report_dir): 426 result_dir = report_dir 427 else: 428 result_dir = os.path.join(report_root_dir, "result", report_dir) 429 os.makedirs(result_dir, exist_ok=True) 430 if check_mode(ModeType.decc): 431 LOG.error("Report not exist, create empty report") 432 else: 433 LOG.error("Report %s not exist, create empty report under %s" % ( 434 report_file, result_dir)) 435 436 suite_name = report_name 437 if not suite_name: 438 suite_name, _ = get_filename_extension(report_file) 439 suite_result = SuiteResult() 440 suite_result.suite_name = suite_name 441 suite_result.stacktrace = error_message 442 if module_name: 443 suite_name = module_name 444 suite_reporter = SuiteReporter( 445 [(suite_result, [])], suite_name, result_dir, 446 modulename=module_name, message=error_message, 447 is_monkey=kwargs.get("is_monkey", False), device_up_info=kwargs.get("device_up_info", None)) 448 suite_reporter.create_empty_report() 449 return "%s.xml" % os.path.join(result_dir, suite_name) 450 451 452def get_sub_path(test_suite_path): 453 pattern = "%stests%s" % (os.sep, os.sep) 454 file_dir = os.path.dirname(test_suite_path) 455 pos = file_dir.find(pattern) 456 if -1 == pos: 457 return "" 458 459 sub_path = file_dir[pos + len(pattern):] 460 pos = sub_path.find(os.sep) 461 if -1 == pos: 462 return "" 463 return sub_path[pos + len(os.sep):] 464 465 466def is_config_str(content): 467 return True if "{" in content and "}" in content else False 468 469 470def is_python_satisfied(): 471 mini_version = (3, 7, 0) 472 if sys.version_info > mini_version: 473 return True 474 LOG.error("Please use python {} or higher version to start project".format(mini_version)) 475 return False 476 477 478def get_instance_name(instance): 479 return instance.__class__.__name__ 480 481 482def convert_ip(origin_ip): 483 addr = origin_ip.strip().split(".") 484 if len(addr) == 4: 485 return "{}.{}.{}.{}".format( 486 addr[0], '*' * len(addr[1]), '*' * len(addr[2]), addr[-1]) 487 else: 488 return origin_ip 489 490 491def convert_port(port): 492 _port = str(port) 493 if len(_port) >= 2: 494 return "{}{}{}".format(_port[0], "*" * (len(_port) - 2), _port[-1]) 495 else: 496 return "*{}".format(_port[-1]) 497 498 499def convert_serial(serial): 500 if serial.startswith("local_"): 501 return serial 502 elif serial.startswith("remote_"): 503 return "remote_{}_{}".format(convert_ip(serial.split("_")[1]), 504 convert_port(serial.split("_")[-1])) 505 else: 506 length = len(serial) // 3 507 return "{}{}{}".format( 508 serial[0:length], "*" * (len(serial) - length * 2), serial[-length:]) 509 510 511def convert_mac(message): 512 if isinstance(message, list): 513 return message 514 pattern = r'.+\'hcptest\':\'(.+)\'' 515 pattern2 = r'.+pass_through:.+\'hcptest\':\'(.+)\'' 516 result1 = re.match(pattern, message) 517 result2 = re.search(pattern2, message) 518 if result1 or result2: 519 result = result1 if result1 else result2 520 result = result.group(1) 521 length = len(result) // 8 522 convert_mes = "{}{}{}".format(result[0:length], "*" * (len(result) - length * 2), result[-length:]) 523 return message.replace(result, convert_mes) 524 else: 525 return message 526 527 528def get_shell_handler(request, parser_type): 529 suite_name = request.root.source.test_name 530 parsers = get_plugin(Plugin.PARSER, parser_type) 531 if parsers: 532 parsers = parsers[:1] 533 parser_instances = [] 534 for listener in request.listeners: 535 listener.device_sn = request.config.environment.devices[0].device_sn 536 for parser in parsers: 537 parser_instance = parser.__class__() 538 parser_instance.suite_name = suite_name 539 parser_instance.listeners = request.listeners 540 parser_instances.append(parser_instance) 541 handler = ShellHandler(parser_instances) 542 return handler 543 544 545def get_kit_instances(json_config, resource_path="", testcases_path=""): 546 from _core.testkit.json_parser import JsonParser 547 kit_instances = [] 548 549 # check input param 550 if not isinstance(json_config, JsonParser): 551 return kit_instances 552 553 # get kit instances 554 for kit in json_config.config.kits: 555 kit["paths"] = [resource_path, testcases_path] 556 kit_type = kit.get("type", "") 557 device_name = kit.get("device_name", None) 558 if get_plugin(plugin_type=Plugin.TEST_KIT, plugin_id=kit_type): 559 test_kit = \ 560 get_plugin(plugin_type=Plugin.TEST_KIT, plugin_id=kit_type)[0] 561 test_kit_instance = test_kit.__class__() 562 test_kit_instance.__check_config__(kit) 563 setattr(test_kit_instance, "device_name", device_name) 564 kit_instances.append(test_kit_instance) 565 else: 566 raise ParamError("kit %s not exists" % kit_type, error_no="00107") 567 return kit_instances 568 569 570def check_device_name(device, kit, step="setup"): 571 kit_device_name = getattr(kit, "device_name", None) 572 device_name = device.get("name") 573 if kit_device_name and device_name and \ 574 kit_device_name != device_name: 575 return False 576 if kit_device_name and device_name: 577 LOG.debug("Do kit:%s %s for device:%s", 578 kit.__class__.__name__, step, device_name) 579 else: 580 LOG.debug("Do kit:%s %s", kit.__class__.__name__, step) 581 return True 582 583 584def check_device_env_index(device, kit): 585 if not hasattr(device, "env_index"): 586 return True 587 kit_device_index_list = getattr(kit, "env_index_list", None) 588 env_index = device.get("env_index") 589 if kit_device_index_list and env_index and \ 590 len(kit_device_index_list) > 0 and env_index not in kit_device_index_list: 591 return False 592 return True 593 594 595def check_path_legal(path): 596 if path and " " in path: 597 return "\"%s\"" % path 598 return path 599 600 601def get_local_ip(): 602 try: 603 sys_type = platform.system() 604 if sys_type == "Windows": 605 _list = socket.gethostbyname_ex(socket.gethostname()) 606 _list = _list[2] 607 for ip_add in _list: 608 if ip_add.startswith("10."): 609 return ip_add 610 611 return socket.gethostbyname(socket.getfqdn(socket.gethostname())) 612 elif sys_type == "Darwin": 613 hostname = socket.getfqdn(socket.gethostname()) 614 return socket.gethostbyname(hostname) 615 elif sys_type == "Linux": 616 real_ip = "/%s/%s" % ("hostip", "realip") 617 if os.path.exists(real_ip): 618 srw = None 619 try: 620 import codecs 621 srw = codecs.open(real_ip, "r", "utf-8") 622 lines = srw.readlines() 623 local_ip = str(lines[0]).strip() 624 except (IOError, ValueError) as error_message: 625 LOG.error(error_message) 626 local_ip = "127.0.0.1" 627 finally: 628 if srw is not None: 629 srw.close() 630 else: 631 local_ip = "127.0.0.1" 632 return local_ip 633 else: 634 return "127.0.0.1" 635 except Exception as error: 636 LOG.debug("Get local ip error: %s, skip!" % error) 637 return "127.0.0.1" 638 639 640class SplicingAction(argparse.Action): 641 def __call__(self, parser, namespace, values, option_string=None): 642 setattr(namespace, self.dest, " ".join(values)) 643 644 645def get_test_component_version(config): 646 if check_mode(ModeType.decc): 647 return "" 648 649 try: 650 paths = [config.resource_path, config.testcases_path] 651 test_file = get_file_absolute_path("test_component.json", paths) 652 flags = os.O_RDONLY 653 modes = stat.S_IWUSR | stat.S_IRUSR 654 with os.fdopen(os.open(test_file, flags, modes), "r") as file_content: 655 json_content = json.load(file_content) 656 version = json_content.get("version", "") 657 return version 658 except (ParamError, ValueError) as error: 659 LOG.error("The exception {} happened when get version".format(error)) 660 return "" 661 662 663def check_mode(mode): 664 from xdevice import Scheduler 665 return Scheduler.mode == mode 666 667 668def do_module_kit_setup(request, kits): 669 for device in request.get_devices(): 670 setattr(device, ConfigConst.module_kits, []) 671 672 from xdevice import Scheduler 673 for kit in kits: 674 run_flag = False 675 for device in request.get_devices(): 676 if not Scheduler.is_execute: 677 raise ExecuteTerminate() 678 if not check_device_env_index(device, kit): 679 continue 680 if check_device_name(device, kit): 681 run_flag = True 682 kit_copy = copy.deepcopy(kit) 683 module_kits = getattr(device, ConfigConst.module_kits) 684 module_kits.append(kit_copy) 685 kit_copy.__setup__(device, request=request) 686 if not run_flag: 687 kit_device_name = getattr(kit, "device_name", None) 688 error_msg = "device name '%s' of '%s' not exist" % ( 689 kit_device_name, kit.__class__.__name__) 690 LOG.error(error_msg, error_no="00108") 691 raise ParamError(error_msg, error_no="00108") 692 693 694def do_module_kit_teardown(request): 695 for device in request.get_devices(): 696 for kit in getattr(device, ConfigConst.module_kits, []): 697 if check_device_name(device, kit, step="teardown"): 698 kit.__teardown__(device) 699 setattr(device, ConfigConst.module_kits, []) 700 701 702def get_current_time(): 703 current_time = time.time() 704 local_time = time.localtime(current_time) 705 data_head = time.strftime("%Y-%m-%d %H:%M:%S", local_time) 706 return "%s" % (data_head) 707 708 709def check_mode_in_sys(mode): 710 if not hasattr(sys, "mode"): 711 return False 712 return getattr(sys, "mode") == mode 713 714 715def get_cst_time(): 716 sh_tz = timezone( 717 timedelta(hours=8), 718 name='Asia/Shanghai', 719 ) 720 return datetime.now(tz=sh_tz) 721 722 723def get_delta_time_ms(start_time): 724 end_time = get_cst_time() 725 delta = (end_time - start_time).total_seconds() * 1000 726 return delta 727 728 729def get_netstat_proc_pid(device, port): 730 if not hasattr(device, "execute_shell_command") or \ 731 not hasattr(device, "log") or \ 732 not hasattr(device, "get_recover_state"): 733 return "" 734 if not device.get_recover_state(): 735 return "" 736 cmd = 'netstat -anp | grep {}'.format(port) 737 proc_running = device.execute_shell_command(cmd).strip() 738 proc_running = proc_running.split("\n") 739 for data in proc_running: 740 if str(port) in data and "grep" not in data: 741 data = data.split() 742 data = data[len(data) - 1] 743 device.log.debug('{} proc:{}'.format(port, data)) 744 data = data.split("/") 745 return data[0] 746 return "" 747 748 749def calculate_elapsed_time(begin, end): 750 """计算时间间隔 751 Args: 752 begin: int/datetime, begin time 753 end : int/datetime, end time 754 Returns: 755 elapsed time description 756 """ 757 elapsed = [] 758 # 传入datetime对象 759 if isinstance(begin, datetime) and isinstance(end, datetime): 760 total_seconds = (end - begin).total_seconds() 761 # 传入耗时秒数 762 else: 763 total_seconds = end - begin 764 total_seconds = float(round(total_seconds, 3)) 765 766 seconds = int(total_seconds) 767 if seconds < 0: 768 return f"calculate error, total seconds is {total_seconds}" 769 if seconds == 0: 770 milliseconds = int((total_seconds - seconds) * 1000) 771 if milliseconds > 0: 772 return "{} ms".format(milliseconds) 773 else: 774 return "0 second" 775 d, s = divmod(seconds, 24 * 60 * 60) 776 if d == 1: 777 elapsed.append("1 day") 778 if d > 1: 779 elapsed.append("{} days".format(d)) 780 h, s = divmod(s, 60 * 60) 781 if h == 1: 782 elapsed.append("1 hour") 783 if h > 1: 784 elapsed.append("{} hours".format(h)) 785 m, s = divmod(s, 60) 786 if m == 1: 787 elapsed.append("1 minute") 788 if m > 1: 789 elapsed.append("{} minutes".format(m)) 790 if s == 1: 791 elapsed.append("1 second") 792 if s > 1: 793 elapsed.append("{} seconds".format(s)) 794 return " ".join(elapsed) 795 796 797def copy_folder(src, dst): 798 if not os.path.exists(src): 799 LOG.error(f"copy folder error, source path '{src}' does not exist") 800 return 801 if not os.path.exists(dst): 802 os.makedirs(dst) 803 for filename in os.listdir(src): 804 fr_path = os.path.join(src, filename) 805 to_path = os.path.join(dst, filename) 806 if os.path.isfile(fr_path): 807 shutil.copy(fr_path, to_path) 808 if os.path.isdir(fr_path): 809 os.makedirs(to_path) 810 copy_folder(fr_path, to_path) 811