• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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