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, modulename=module_name, 446 is_monkey=kwargs.get("is_monkey", False), device_up_info=kwargs.get("device_up_info", None)) 447 suite_reporter.create_empty_report() 448 return "%s.xml" % os.path.join(result_dir, suite_name) 449 450 451def get_sub_path(test_suite_path): 452 pattern = "%stests%s" % (os.sep, os.sep) 453 file_dir = os.path.dirname(test_suite_path) 454 pos = file_dir.find(pattern) 455 if -1 == pos: 456 return "" 457 458 sub_path = file_dir[pos + len(pattern):] 459 pos = sub_path.find(os.sep) 460 if -1 == pos: 461 return "" 462 return sub_path[pos + len(os.sep):] 463 464 465def is_config_str(content): 466 return True if "{" in content and "}" in content else False 467 468 469def is_python_satisfied(): 470 mini_version = (3, 7, 0) 471 if sys.version_info > mini_version: 472 return True 473 LOG.error("Please use python {} or higher version to start project".format(mini_version)) 474 return False 475 476 477def get_instance_name(instance): 478 return instance.__class__.__name__ 479 480 481def convert_ip(origin_ip): 482 addr = origin_ip.strip().split(".") 483 if len(addr) == 4: 484 return "{}.{}.{}.{}".format( 485 addr[0], '*' * len(addr[1]), '*' * len(addr[2]), addr[-1]) 486 else: 487 return origin_ip 488 489 490def convert_port(port): 491 _port = str(port) 492 if len(_port) >= 2: 493 return "{}{}{}".format(_port[0], "*" * (len(_port) - 2), _port[-1]) 494 else: 495 return "*{}".format(_port[-1]) 496 497 498def convert_serial(serial): 499 if serial.startswith("local_"): 500 return serial 501 elif serial.startswith("remote_"): 502 return "remote_{}_{}".format(convert_ip(serial.split("_")[1]), 503 convert_port(serial.split("_")[-1])) 504 else: 505 length = len(serial) // 3 506 return "{}{}{}".format( 507 serial[0:length], "*" * (len(serial) - length * 2), serial[-length:]) 508 509 510def convert_mac(message): 511 if isinstance(message, list): 512 return message 513 pattern = r'.+\'hcptest\':\'(.+)\'' 514 pattern2 = r'.+pass_through:.+\'hcptest\':\'(.+)\'' 515 result1 = re.match(pattern, message) 516 result2 = re.search(pattern2, message) 517 if result1 or result2: 518 result = result1 if result1 else result2 519 result = result.group(1) 520 length = len(result) // 8 521 convert_mes = "{}{}{}".format(result[0:length], "*" * (len(result) - length * 2), result[-length:]) 522 return message.replace(result, convert_mes) 523 else: 524 return message 525 526 527def get_shell_handler(request, parser_type): 528 suite_name = request.root.source.test_name 529 parsers = get_plugin(Plugin.PARSER, parser_type) 530 if parsers: 531 parsers = parsers[:1] 532 parser_instances = [] 533 for listener in request.listeners: 534 listener.device_sn = request.config.environment.devices[0].device_sn 535 for parser in parsers: 536 parser_instance = parser.__class__() 537 parser_instance.suite_name = suite_name 538 parser_instance.listeners = request.listeners 539 parser_instances.append(parser_instance) 540 handler = ShellHandler(parser_instances) 541 return handler 542 543 544def get_kit_instances(json_config, resource_path="", testcases_path=""): 545 from _core.testkit.json_parser import JsonParser 546 kit_instances = [] 547 548 # check input param 549 if not isinstance(json_config, JsonParser): 550 return kit_instances 551 552 # get kit instances 553 for kit in json_config.config.kits: 554 kit["paths"] = [resource_path, testcases_path] 555 kit_type = kit.get("type", "") 556 device_name = kit.get("device_name", None) 557 if get_plugin(plugin_type=Plugin.TEST_KIT, plugin_id=kit_type): 558 test_kit = \ 559 get_plugin(plugin_type=Plugin.TEST_KIT, plugin_id=kit_type)[0] 560 test_kit_instance = test_kit.__class__() 561 test_kit_instance.__check_config__(kit) 562 setattr(test_kit_instance, "device_name", device_name) 563 kit_instances.append(test_kit_instance) 564 else: 565 raise ParamError("kit %s not exists" % kit_type, error_no="00107") 566 return kit_instances 567 568 569def check_device_name(device, kit, step="setup"): 570 kit_device_name = getattr(kit, "device_name", None) 571 device_name = device.get("name") 572 if kit_device_name and device_name and \ 573 kit_device_name != device_name: 574 return False 575 if kit_device_name and device_name: 576 LOG.debug("Do kit:%s %s for device:%s", 577 kit.__class__.__name__, step, device_name) 578 else: 579 LOG.debug("Do kit:%s %s", kit.__class__.__name__, step) 580 return True 581 582 583def check_device_env_index(device, kit): 584 if not hasattr(device, "env_index"): 585 return True 586 kit_device_index_list = getattr(kit, "env_index_list", None) 587 env_index = device.get("env_index") 588 if kit_device_index_list and env_index and \ 589 len(kit_device_index_list) > 0 and env_index not in kit_device_index_list: 590 return False 591 return True 592 593 594def check_path_legal(path): 595 if path and " " in path: 596 return "\"%s\"" % path 597 return path 598 599 600def get_local_ip(): 601 try: 602 sys_type = platform.system() 603 if sys_type == "Windows": 604 _list = socket.gethostbyname_ex(socket.gethostname()) 605 _list = _list[2] 606 for ip_add in _list: 607 if ip_add.startswith("10."): 608 return ip_add 609 610 return socket.gethostbyname(socket.getfqdn(socket.gethostname())) 611 elif sys_type == "Darwin": 612 hostname = socket.getfqdn(socket.gethostname()) 613 return socket.gethostbyname(hostname) 614 elif sys_type == "Linux": 615 real_ip = "/%s/%s" % ("hostip", "realip") 616 if os.path.exists(real_ip): 617 srw = None 618 try: 619 import codecs 620 srw = codecs.open(real_ip, "r", "utf-8") 621 lines = srw.readlines() 622 local_ip = str(lines[0]).strip() 623 except (IOError, ValueError) as error_message: 624 LOG.error(error_message) 625 local_ip = "127.0.0.1" 626 finally: 627 if srw is not None: 628 srw.close() 629 else: 630 local_ip = "127.0.0.1" 631 return local_ip 632 else: 633 return "127.0.0.1" 634 except Exception as error: 635 LOG.debug("Get local ip error: %s, skip!" % error) 636 return "127.0.0.1" 637 638 639class SplicingAction(argparse.Action): 640 def __call__(self, parser, namespace, values, option_string=None): 641 setattr(namespace, self.dest, " ".join(values)) 642 643 644def get_test_component_version(config): 645 if check_mode(ModeType.decc): 646 return "" 647 648 try: 649 paths = [config.resource_path, config.testcases_path] 650 test_file = get_file_absolute_path("test_component.json", paths) 651 flags = os.O_RDONLY 652 modes = stat.S_IWUSR | stat.S_IRUSR 653 with os.fdopen(os.open(test_file, flags, modes), "r") as file_content: 654 json_content = json.load(file_content) 655 version = json_content.get("version", "") 656 return version 657 except (ParamError, ValueError) as error: 658 LOG.error("The exception {} happened when get version".format(error)) 659 return "" 660 661 662def check_mode(mode): 663 from xdevice import Scheduler 664 return Scheduler.mode == mode 665 666 667def do_module_kit_setup(request, kits): 668 for device in request.get_devices(): 669 setattr(device, ConfigConst.module_kits, []) 670 671 from xdevice import Scheduler 672 for kit in kits: 673 run_flag = False 674 for device in request.get_devices(): 675 if not Scheduler.is_execute: 676 raise ExecuteTerminate() 677 if not check_device_env_index(device, kit): 678 continue 679 if check_device_name(device, kit): 680 run_flag = True 681 kit_copy = copy.deepcopy(kit) 682 module_kits = getattr(device, ConfigConst.module_kits) 683 module_kits.append(kit_copy) 684 kit_copy.__setup__(device, request=request) 685 if not run_flag: 686 kit_device_name = getattr(kit, "device_name", None) 687 error_msg = "device name '%s' of '%s' not exist" % ( 688 kit_device_name, kit.__class__.__name__) 689 LOG.error(error_msg, error_no="00108") 690 raise ParamError(error_msg, error_no="00108") 691 692 693def do_module_kit_teardown(request): 694 for device in request.get_devices(): 695 for kit in getattr(device, ConfigConst.module_kits, []): 696 if check_device_name(device, kit, step="teardown"): 697 kit.__teardown__(device) 698 setattr(device, ConfigConst.module_kits, []) 699 700 701def get_current_time(): 702 current_time = time.time() 703 local_time = time.localtime(current_time) 704 data_head = time.strftime("%Y-%m-%d %H:%M:%S", local_time) 705 return "%s" % (data_head) 706 707 708def check_mode_in_sys(mode): 709 if not hasattr(sys, "mode"): 710 return False 711 return getattr(sys, "mode") == mode 712 713 714def get_cst_time(): 715 sh_tz = timezone( 716 timedelta(hours=8), 717 name='Asia/Shanghai', 718 ) 719 return datetime.now(tz=sh_tz) 720 721 722def get_delta_time_ms(start_time): 723 end_time = get_cst_time() 724 delta = (end_time - start_time).total_seconds() * 1000 725 return delta 726 727 728def get_netstat_proc_pid(device, port): 729 if not hasattr(device, "execute_shell_command") or \ 730 not hasattr(device, "log") or \ 731 not hasattr(device, "get_recover_state"): 732 return "" 733 if not device.get_recover_state(): 734 return "" 735 cmd = 'netstat -anp | grep {}'.format(port) 736 proc_running = device.execute_shell_command(cmd).strip() 737 proc_running = proc_running.split("\n") 738 for data in proc_running: 739 if str(port) in data and "grep" not in data: 740 data = data.split() 741 data = data[len(data) - 1] 742 device.log.debug('{} proc:{}'.format(port, data)) 743 data = data.split("/") 744 return data[0] 745 return "" 746 747 748def calculate_elapsed_time(begin, end): 749 """计算时间间隔 750 Args: 751 begin: int/datetime, begin time 752 end : int/datetime, end time 753 Returns: 754 elapsed time description 755 """ 756 elapsed = [] 757 # 传入datetime对象 758 if isinstance(begin, datetime) and isinstance(end, datetime): 759 total_seconds = (end - begin).total_seconds() 760 # 传入耗时秒数 761 else: 762 total_seconds = end - begin 763 total_seconds = float(round(total_seconds, 3)) 764 765 seconds = int(total_seconds) 766 if seconds < 0: 767 return f"calculate error, total seconds is {total_seconds}" 768 if seconds == 0: 769 milliseconds = int((total_seconds - seconds) * 1000) 770 if milliseconds > 0: 771 return "{} ms".format(milliseconds) 772 else: 773 return "0 second" 774 d, s = divmod(seconds, 24 * 60 * 60) 775 if d == 1: 776 elapsed.append("1 day") 777 if d > 1: 778 elapsed.append("{} days".format(d)) 779 h, s = divmod(s, 60 * 60) 780 if h == 1: 781 elapsed.append("1 hour") 782 if h > 1: 783 elapsed.append("{} hours".format(h)) 784 m, s = divmod(s, 60) 785 if m == 1: 786 elapsed.append("1 minute") 787 if m > 1: 788 elapsed.append("{} minutes".format(m)) 789 if s == 1: 790 elapsed.append("1 second") 791 if s > 1: 792 elapsed.append("{} seconds".format(s)) 793 return " ".join(elapsed) 794 795 796def copy_folder(src, dst): 797 if not os.path.exists(src): 798 LOG.error(f"copy folder error, source path '{src}' does not exist") 799 return 800 if not os.path.exists(dst): 801 os.makedirs(dst) 802 for filename in os.listdir(src): 803 fr_path = os.path.join(src, filename) 804 to_path = os.path.join(dst, filename) 805 if os.path.isfile(fr_path): 806 shutil.copy(fr_path, to_path) 807 if os.path.isdir(fr_path): 808 os.makedirs(to_path) 809 copy_folder(fr_path, to_path) 810