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