Skip to content
This repository has been archived by the owner on Aug 5, 2024. It is now read-only.

Commit

Permalink
fix:
Browse files Browse the repository at this point in the history
Description:

Log:
  • Loading branch information
mikigo committed Jun 20, 2024
1 parent 66c549d commit 3709af7
Show file tree
Hide file tree
Showing 6 changed files with 126 additions and 87 deletions.
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ dependencies = [
"funnylog",
"click",
"pexpect",
"youqu-html",
]
dynamic = [
"version",
Expand All @@ -31,6 +30,7 @@ desktop-ui = [
"youqu-mousekey",
"youqu-button-center",
"pdocr-rpc",
"youqu-html",
# "wdotool",
]
webui = [
Expand Down
3 changes: 0 additions & 3 deletions youqu3/_setting/_setting.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,6 @@ class _Setting(_DynamicSetting):
IMAGE_SERVER_HOST = "10.8.11.139"

# REMOTE
MODE = "parallel"
BUILD_ENV = True
SEND = True

#
PYPI_MIRROR = "https://pypi.tuna.tsinghua.edu.cn/simple"
Expand Down
1 change: 0 additions & 1 deletion youqu3/dogtail.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,3 @@

if HAS_DOGTAIL is False:
raise YouQuPluginInstalledError("youqu-dogtail")

16 changes: 4 additions & 12 deletions youqu3/driver/cli.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import click

from youqu3 import version


Expand All @@ -16,16 +17,19 @@ def cli(): ...
help="指定用例关键词执行,支持 'and/or/not' 逻辑表达式")
@click.option("-t", "--tags", default=None, type=click.STRING,
help="指定用例标签执行,支持 'and/or/not' 逻辑表达式")
@click.option("--setup-plan", is_flag=True, default=False, help="")
def run(
filepath,
keywords,
tags,
setup_plan,
):
"""本地执行"""
args = {
"filepath": filepath,
"keywords": keywords,
"tags": tags,
"setup_plan": setup_plan,
}
from youqu3.driver.run import Run
Run(**args).run()
Expand All @@ -35,12 +39,6 @@ def run(
@click.help_option("-h", "--help", help="查看帮助信息")
@click.option("-c", "--clients", default=None, type=click.STRING,
help="远程机器信息:user@ip:password,多个机器之间用 '/' 连接")
@click.option("-s", "--send", default=None, type=click.STRING,
help="发送代码到远程机器")
@click.option("-e", "--build-env", default=None, type=click.STRING,
help="远程机器安装环境")
@click.option("-m", "--mode", default="parallel", type=click.Choice(["parallel", "nginx"]),
help="远程控制驱动模式 (parallel or nginx)")
@click.option("-f", "--filepath", default=None, type=click.STRING,
help="指定用例文件路径执行")
@click.option("-k", "--keywords", default=None, type=click.STRING,
Expand All @@ -49,19 +47,13 @@ def run(
help="指定用例标签执行,支持 'and/or/not' 逻辑表达式")
def remote(
clients,
send,
build_env,
mode,
filepath,
keywords,
tags,
):
"""远程控制执行"""
args = {
"clients": clients,
"send": send,
"build_env": build_env,
"mode": mode,
"filepath": filepath,
"keywords": keywords,
"tags": tags,
Expand Down
172 changes: 107 additions & 65 deletions youqu3/driver/remote.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
# SPDX-License-Identifier: GPL-2.0-only
import pathlib
import re
import time
from concurrent.futures import ALL_COMPLETED
from concurrent.futures import ThreadPoolExecutor
from concurrent.futures import wait
Expand All @@ -21,9 +20,6 @@ class Remote:
def __init__(
self,
clients=None,
send=None,
build_env=None,
mode=None,
filepath=None,
keywords=None,
tags=None,
Expand All @@ -35,42 +31,49 @@ def __init__(
self.keywords = keywords
self.tags = tags
self.clients = clients
self.send = send or setting.SEND
self.build_env = build_env or setting.BUILD_ENV
self.mode = mode or setting.MODE

cli_clients = {}
if self.clients:
if not self.clients:
raise ValueError("REMOTE驱动模式, 未传入远程客户端信息:-c/--clients user@ip:pwd")
self.group_type = False
if "{" in self.clients and "}" in self.clients:
self.group_type = True

if self.group_type is False:
self.cli_clients = {}
_cli_clients = self.clients.split("/")
for index, _client in enumerate(_cli_clients):
_cli_client_info = re.findall(r"^(.+?)@(\d+\.\d+\.\d+\.\d+):{0,1}(.*?)$", _client)
if _cli_client_info:
_c = list(_cli_client_info[0])
if _c[2] == "":
_c[2] = setting.PASSWORD
cli_clients[f"client{index + 1}"] = _c

self.cli_clients[f"client{index + 1}"] = _c
else:
raise ValueError("REMOTE驱动模式, 未传入远程客户端信息:-c/--clients user@ip:pwd")

self.clients = cli_clients
self.cli_groups = {}
groups = re.findall(r'\{(.*?)\}', self.clients)
for group_index, group in enumerate(groups):
cli_clients = {}
for client_index, _client in enumerate(group.split("/")):
_cli_client_info = re.findall(r"^(.+?)@(\d+\.\d+\.\d+\.\d+):{0,1}(.*?)$", _client)
if _cli_client_info:
_c = list(_cli_client_info[0])
if _c[2] == "":
_c[2] = setting.PASSWORD
cli_clients[f"client{client_index + 1}"] = _c
self.cli_groups[f"group{group_index + 1}"] = cli_clients

self.server_rootdir = pathlib.Path(".").absolute()
self.rootdir_name = self.server_rootdir.name

self.client_rootdir = lambda x: f"/home/{x}/{self.rootdir_name}_{setting.TIME_STRING}"
self.client_report_path = lambda x: f"{self.client_rootdir(x)}/report"
self.client_html_report_path = lambda x: f"{self.client_report_path(x)}/html"
self.client_json_report_path = lambda x: f"{self.client_report_path(x)}/json"

self.strf_time = time.strftime("%m%d%p%I%M%S")
self.rsync = "rsync -av -e ssh -o StrictHostKeyChecking=no"
self.empty = "> /dev/null 2>&1"

self.collection_json = False
self.server_json_dir_id = None
self.pms_user = None
self.pms_password = None

from funnylog.conf import setting as log_setting

Expand Down Expand Up @@ -99,7 +102,7 @@ def send_code(self, user, _ip, password):
exclude += f"--exclude='{i}' "
_, return_code = Cmd.expect_run(
f"{self.rsync} {exclude} {self.server_rootdir}/* {user}@{_ip}:{self.client_rootdir(user)}/",
events={'(?i)password':f'{password}\\n'}
events={'(?i)password': f'{password}\\n'}
)
_, return_code = Cmd.expect_run(
f"{self.rsync} {exclude} {self.server_rootdir}/.env {user}@{_ip}:{self.client_rootdir(user)}/",
Expand All @@ -117,7 +120,8 @@ def _remote_run(cmd):
if return_code != 0:
_remote_run("curl -sSL https://bootstrap.pypa.io/get-pip.py -o get-pip.py && python3 get-pip.py")
_remote_run(f"pip3 install -U youqu3 -i {setting.PYPI_MIRROR}")
_, return_code = _remote_run(f"export PATH=$PATH:$HOME/.local/bin;cd {self.client_rootdir(user)} && youqu3 envx")
_, return_code = _remote_run(
f"export PATH=$PATH:$HOME/.local/bin;cd {self.client_rootdir(user)} && youqu3 envx")
logger.info(f"环境安装{'成功' if return_code == 0 else '失败'} - < {user}@{_ip} >")

def send_code_and_env(self, user, _ip, password):
Expand All @@ -128,30 +132,34 @@ def send_code_and_env(self, user, _ip, password):
def makedirs(dirs):
pathlib.Path(dirs).mkdir(parents=True, exist_ok=True)

def get_back_all_report(self, client_list):
def get_back_all_report(self, client_list, clients):
def get_back(user, _ip, password):
server_html_path = f"{self.server_rootdir}/report/remote/{self.strf_time}_{_ip}_{self.rootdir_name}"
server_html_path = f"{self.server_rootdir}/report/remote/{setting.TIME_STRING}_{_ip}_{self.rootdir_name}"
self.makedirs(server_html_path)
Cmd.run(
f"{self.rsync % password} {user}@{_ip}:{self.client_report_path(user)}/* {server_html_path}/ {self.empty}")

if len(self.clients) >= 2:
if len(clients) >= 2:
_ps = []
executor = ThreadPoolExecutor()
for client in client_list[:-1]:
user, _ip, password = self.clients.get(client)
user, _ip, password = clients.get(client)
_p4 = executor.submit(get_back, user, _ip, password)
_ps.append(_p4)
sleep(2)
user, _ip, password = self.clients.get(client_list[-1])
user, _ip, password = clients.get(client_list[-1])
get_back(user, _ip, password)
wait(_ps, return_when=ALL_COMPLETED)
else:
user, _ip, password = self.clients.get(client_list[0])
user, _ip, password = clients.get(client_list[0])
get_back(user, _ip, password)

def generate_remote_cmd(self, user):
cmd = ["cd", f"{self.client_rootdir(user)}/", "&&", "youqu3-cargo", "run"]
def changdir_remote_cmd(self, user):
return ["cd", f"{self.client_rootdir(user)}/", "&&"]

@property
def generate_cmd(self):
cmd = ["youqu3-cargo", "run"]

if self.filepath:
cmd.append(self.filepath)
Expand All @@ -160,69 +168,103 @@ def generate_remote_cmd(self, user):
if self.tags:
cmd.extend(["-m", f"'{self.tags}'"])

cmd.extend([
f"--maxfail={setting.MAX_FAIL}",
f"--reruns={setting.RERUNS}",
f"--timeout={setting.TIMEOUT}",
"--json-report",
"--json-report-indent=2",
f"--json-report-file={self.client_json_report_path(user)}/report_{setting.TIME_STRING}.json",
f"--alluredir={self.client_html_report_path(user)}",
"--clean-alluredir",
])

return cmd

def run_test(self, user, _ip, password):
RemoteCmd(user, _ip, password).remote_run(" ".join(self.generate_remote_cmd(user)))
RemoteCmd(user, _ip, password).remote_run(
" ".join(
self.changdir_remote_cmd(user) + self.generate_cmd
)
)

def parallel_run(self):
@property
def collection_only_cmd(self):
return self.generate_cmd + ["--setup-plan"]

@property
def get_collection_only_cases(self):
stdout = Cmd.run(f"cd {self.server_rootdir} && {' '.join(self.collection_only_cmd)}", timeout=600)
lines = stdout.split("\n")
_collection_cases = []
for line in lines:
line = line.strip()
if line and " " not in line:
_collection_cases.append(line.split("::")[0])
collection_cases = set(_collection_cases)
return collection_cases

def parallel_run(self, clients):
_ps = []
executor = ThreadPoolExecutor()
for client in list(self.clients.keys())[:-1]:
user, _ip, password = self.clients.get(client)
for client in list(clients.keys())[:-1]:
user, _ip, password = clients.get(client)
_p3 = executor.submit(self.run_test, user, _ip, password)
_ps.append(_p3)
sleep(1)
user, _ip, password = list(self.clients.values())[-1]
user, _ip, password = list(clients.values())[-1]
self.run_test(user, _ip, password)
wait(_ps, return_when=ALL_COMPLETED)
sleep(5)

def mul_do(self, func_obj, client_list):
def mul_do(self, func_obj, client_list, clients):
if len(client_list) >= 2:
executor = ThreadPoolExecutor()
_ps = []
for client in client_list[:-1]:
user, _ip, password = self.clients.get(client)
user, _ip, password = clients.get(client)
_p1 = executor.submit(func_obj, user, _ip, password)
_ps.append(_p1)
user, _ip, password = self.clients.get(client_list[-1])
user, _ip, password = clients.get(client_list[-1])
func_obj(user, _ip, password)
wait(_ps, return_when=ALL_COMPLETED)
else:
user, _ip, password = self.clients.get(client_list[0])
user, _ip, password = clients.get(client_list[0])
func_obj(user, _ip, password)

def run(self):
client_list = list(self.clients.keys())
Cmd.run(f"rm -rf ~/.ssh/known_hosts {self.empty}")

print("-" * 51)
print(f"|{'CLIENTS'.center(11)}|{'USER'.center(10)}|{'IP'.center(15)}|{'PASSWORD'.center(10)}|")
print("-" * 51)
for c, (user, _ip, password) in self.clients.items():
print(f"|{c.center(11)}|{user.center(10)}|{_ip.center(15)}|{password.center(10)}|")
print("-" * 51)
if self.group_type:
print("远程测试机列表".center(54))
print("-" * 58)
print(
f"|{'GROUPS'.center(8)}|{'CLIENTS'.center(9)}|{'USER'.center(10)}|{'IP'.center(15)}|{'PASSWORD'.center(10)}|")
print("-" * 58)
for group, clients in self.cli_groups.items():
for c, (user, _ip, password) in clients.items():
print(f"|{group.center(8)}|{c.center(9)}|{user.center(10)}|{_ip.center(15)}|{password.center(10)}|")
print("-" * 58)

for group, clients in self.cli_groups.items():
client_list = list(clients.keys())
if len(client_list) > 1:
# TODO
cases = self.get_collection_only_cases
def split_list(lst, n):
k, m = divmod(len(lst), n)
return [lst[i * k + min(i, m):(i + 1) * k + min(i + 1, m)] for i in range(n)]

client_case_list = split_list(list(cases), len(client_list))
client_case_map = dict(zip(client_list, client_case_list))
print(1)


else:
self.mul_do(self.send_code_and_env, client_list, clients)
self.parallel_run(clients)
self.get_back_all_report(client_list, clients)

Cmd.run(f"rm -rf ~/.ssh/known_hosts {self.empty}")
if self.build_env:
self.mul_do(self.send_code_and_env, client_list)
else:
if self.send:
self.mul_do(self.send_code, client_list)

if self.mode == "parallel":
self.parallel_run()
else:
...
self.get_back_all_report(client_list)
print("远程测试机列表".center(47))
print("-" * 49)
print(f"|{'CLIENTS'.center(9)}|{'USER'.center(10)}|{'IP'.center(15)}|{'PASSWORD'.center(10)}|")
print("-" * 49)
for c, (user, _ip, password) in self.cli_clients.items():
print(f"|{c.center(9)}|{user.center(10)}|{_ip.center(15)}|{password.center(10)}|")
print("-" * 49)

client_list = list(self.cli_clients.keys())
self.mul_do(self.send_code_and_env, client_list, self.cli_clients)
self.parallel_run(self.cli_clients)
self.get_back_all_report(client_list, self.cli_clients)
Loading

0 comments on commit 3709af7

Please sign in to comment.