• 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,
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