diff --git a/build.sh b/build.sh index bd114779fca5fd7648dcf4e9b8522808d2b0f119..edd02abf86b6f63fdce39ac21ae3b341069e4e74 100644 --- a/build.sh +++ b/build.sh @@ -3,21 +3,21 @@ # Copyright: Copyright (c) Huawei Technologies Co., Ltd. All rights reserved. set -e -current_dir=$(cd $(dirname $0); pwd) +current_dir=$(cd $(dirname "$0"); pwd) -cd ${current_dir} -cp ${current_dir}/tools/download_dependency/src/* ${current_dir}/tools/install_dependency/src/ +tag="v0.1" -cd ${current_dir}/tools/download_dependency -pyinstaller -F ./src/download.py -p ./ +sh "${current_dir}"/tools/distribute/build_devkit_distribute.sh -cd ${current_dir}/tools/install_dependency -pyinstaller -F ./src/devkitpipeline.py -p ./ --add-data "${current_dir}/component:component" +sh "${current_dir}"/tools/download_dependency/build_download.sh -cp ${current_dir}/tools/install_dependency/config/machine.yaml ${current_dir}/tools/install_dependency/dist/machine.yaml -cp ${current_dir}/tools/download_dependency/dist/download ${current_dir}/tools/install_dependency/dist/ +sh "${current_dir}"/tools/install_dependency/build_install.sh -mkdir -p ${current_dir}/tools/install_dependency/v1.0/tools/ -cp -rf ${current_dir}/tools/install_dependency/dist/ ${current_dir}/tools/install_dependency/v1.0/tools/linux -cd ${current_dir}/tools/install_dependency/v1.0/ -tar -zcvf v1.0.tar.gz tools +cd "${current_dir}"/build + +mkdir -p "${current_dir}"/build/dekvit-pipeline-${tag}/linux +cp -rf "${current_dir}"/build/install/dist/* "${current_dir}"/build/dekvit-pipeline-${tag}/linux +cp -rf "${current_dir}"/build/download/dist/* "${current_dir}"/build/dekvit-pipeline-${tag}/linux +cp -rf "${current_dir}"/build/distribute/devkit_distribute "${current_dir}"/build/dekvit-pipeline-${tag}/linux + +tar -zcvf dekvit-pipeline-${tag}.tar.gz dekvit-pipeline-${tag} diff --git "a/document/\344\276\235\350\265\226/requirement.md" "b/document/\344\276\235\350\265\226/requirement.md" index 7349684eb4fd81f797fce43c7b083a3fbb42f140..3ee266aa68e9ff3368011a4c3110bd05ca566e77 100644 --- "a/document/\344\276\235\350\265\226/requirement.md" +++ "b/document/\344\276\235\350\265\226/requirement.md" @@ -3,4 +3,6 @@ pyinstaller paramiko PyYAML wget -timeout_decorator \ No newline at end of file +timeout_decorator +urllib3 +requests \ No newline at end of file diff --git a/tools/common/devkit_utils/__init__.py b/tools/common/devkit_utils/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/tools/common/devkit_utils/devkit_client.py b/tools/common/devkit_utils/devkit_client.py new file mode 100644 index 0000000000000000000000000000000000000000..ebcef7cfb1f976992f0d209c1fd8387e88fe83c5 --- /dev/null +++ b/tools/common/devkit_utils/devkit_client.py @@ -0,0 +1,91 @@ +import logging +import os.path + +import requests +from urllib3 import encode_multipart_formdata + + +class DevKitClient: + def __init__(self, ip, port, username, password): + self.ip = ip + self.port = port + self.username = username + self.password = password + self.user_id = "" + self.token = "" + self.header = dict() + self.login() + + def __del__(self): + self.logout() + + def __str__(self): + return f"{self.ip}, {self.port}, {self.username}, {self.password}, {self.user_id}, {self.token}, f{self.header}" + + def login(self): + url = f"https://{self.ip}:{self.port}/framework/api/v1.0/users/session/" + body = dict({"username": self.username, "password": self.password}) + try: + ret = requests.post(url=url, json=body, verify=False, timeout=10) + user_dict = ret.json() + self.token = ret.headers["token"] + self.user_id = user_dict["data"]["id"] + self.header = dict({ + "Authorization": self.token, + "Content-Type": "application/json", + "Accept-Language": "zh-cn" + }) + except requests.exceptions.ReadTimeout or requests.exceptions.ConnectionError as ex: + logging.exception(ex) + raise ex + + def logout(self): + url = f"https://{self.ip}:{self.port}/framework/api/v1.0/users/session/{self.user_id}/" + try: + requests.delete(url, headers=self.header, verify=False) + except Exception as ex: + logging.exception(ex) + pass + + def upload_report(self, file_path): + try: + data = dict({"file": (os.path.basename(file_path), open(file_path, "rb").read())}) + except OSError as e: + logging.exception(e) + raise + encoded_data = encode_multipart_formdata(data) + _header = self.header.copy() + _header.update({"Content-Type": encoded_data[1]}) + url = f"https://{self.ip}:{self.port}/plugin/api/v1.0/java_perf/api/records/actions/upload/" + return requests.post(url=url, headers=_header, data=encoded_data[0], verify=False) + + def get_record_list(self): + url = f"https://{self.ip}:{self.port}/plugin/api/v1.0/java_perf/api/records/user/" + data = {"userId": self.user_id} + return requests.post(url=url, json=data, headers=self.header, verify=False) + + def delete_report(self, task_id): + url = f"https://{self.ip}:{self.port}/plugin/api/v1.0/java_perf/api/records/{task_id}/" + requests.delete(url=url, headers=self.header, verify=False) + + def upload_report_by_force(self, file_path): + ret = self.upload_report(file_path) + if ret.status_code == requests.codes.ok: + return + if ret.json().get("code", "") == "JavaPerf.Upload.Recording.RecordingReachLimit": + records = self.get_record_list() + task_id, create_time = "", "999999999999999999999999999999" + for o in records.json().get("members", []): + if float(o["createTime"]) < float(create_time): + task_id, create_time = o["id"], o["createTime"] + self.delete_report(task_id) + self.upload_report(file_path) + + +if __name__ == "__main__": + try: + d = DevKitClient("172.39.173.2", "8086", "devadmin", "Huawei12#$") + d.upload_report_by_force("/home/panlonglong/Downloads/Main(136462)") + d.logout() + except Exception as e: + print(str(e)) diff --git a/tools/common/devkit_utils/error_coce.py b/tools/common/devkit_utils/error_coce.py new file mode 100644 index 0000000000000000000000000000000000000000..032cc9cc878a60d05223edcf6b9019d6155b7d97 --- /dev/null +++ b/tools/common/devkit_utils/error_coce.py @@ -0,0 +1,17 @@ +import enum +import typing + + +class ErrorCodeEnum(enum.Enum): + SUCCESS = 0 + FAILURE = -1 + FINISHED = 1 + NOT_FOUND_JCMD = 10001 + NOT_FOUND_APPS = 10002 + + +class ErrorCodeMsg: + LANGUAGE_EN: typing.Dict[ErrorCodeEnum, str] = { + ErrorCodeEnum.NOT_FOUND_JCMD: "the jcmd command was not found on server {}", + ErrorCodeEnum.NOT_FOUND_APPS: "the application called {} was not found on server {}", + } diff --git a/tools/common/devkit_utils/file_utils.py b/tools/common/devkit_utils/file_utils.py new file mode 100644 index 0000000000000000000000000000000000000000..a70cde7d0a60e1c9553de601d8846be8220bf0bf --- /dev/null +++ b/tools/common/devkit_utils/file_utils.py @@ -0,0 +1,30 @@ +import os + + +def create_dir(target, mode=0o700): + if os.path.exists(target): + if os.path.isdir(target): + return True + else: + return False + old_mask = os.umask(0o000) + os.makedirs(target, mode=mode) + os.umask(old_mask) + return True + + +def clear_dir(target): + """ + 删除目录下,所有文件,保留目录 + """ + if not os.path.exists(target): + return + for file in os.listdir(target): + sub = os.path.join(target, file) + if os.path.isfile(sub): + os.remove(sub) + if os.path.islink(sub): + os.unlink(sub) + if os.path.isdir(sub): + clear_dir(sub) + os.rmdir(sub) diff --git a/tools/common/devkit_utils/log_config.py b/tools/common/devkit_utils/log_config.py new file mode 100644 index 0000000000000000000000000000000000000000..e2a23022441a372167920756f7e1342c02960c58 --- /dev/null +++ b/tools/common/devkit_utils/log_config.py @@ -0,0 +1,17 @@ +import logging.config +import os + +from devkit_utils import file_utils +from devkit_utils import shell_tools + + +def config_log_ini(root_path, log_name): + log_template = os.path.join(root_path, "config/log.ini.template") + log_config = os.path.join(root_path, "config/log.ini") + log_path = os.path.join(root_path, "log") + log_replace_path = log_path.replace("/", "\\/") + shell_tools.exec_shell("cp {} {}".format(log_template, log_config), is_shell=True) + shell_tools.exec_shell("sed -i \"s/LOG_PATH/{}/g\" {} ".format(log_replace_path, log_config), is_shell=True) + shell_tools.exec_shell("sed -i \'s/LOG_NAME/{}/g\' {} ".format(log_name, log_config), is_shell=True) + file_utils.create_dir(log_path) + logging.config.fileConfig(os.path.join(root_path, "config/log.ini")) diff --git a/tools/common/devkit_utils/pyinstaller_utils.py b/tools/common/devkit_utils/pyinstaller_utils.py new file mode 100644 index 0000000000000000000000000000000000000000..604ee668fbe447ed25d55bfebe1f953a6dceabaf --- /dev/null +++ b/tools/common/devkit_utils/pyinstaller_utils.py @@ -0,0 +1,22 @@ +import os.path +import sys + + +def check_is_running_in_pyinstaller_bundle(): + """ + 判断是否在pyinstaller + """ + if getattr(sys, "frozen", False) and hasattr(sys, "_MEIPASS"): + return True + else: + return False + + +def obtain_root_path(root_path): + """ + 获取rootpath,当在pyinstaller中时,为父母录,否认为参入的参数 + """ + if check_is_running_in_pyinstaller_bundle(): + return os.path.dirname(os.path.dirname(sys.executable)) + else: + return root_path diff --git a/tools/common/devkit_utils/shell_tools.py b/tools/common/devkit_utils/shell_tools.py new file mode 100644 index 0000000000000000000000000000000000000000..a7f7d0e5cfa60c6e585709e179cb15fb5638c803 --- /dev/null +++ b/tools/common/devkit_utils/shell_tools.py @@ -0,0 +1,40 @@ +import logging +import shlex +import subprocess +import typing + + +class ExecutionOutcome: + def __init__(self, return_code=0, out=None, err=None): + self.return_code: int = return_code + self.out: typing.Optional[str] = out + self.err: typing.Optional[str] = err + + def __str__(self): + return "code:%s\n out:%s\n err:%s\n" % (self.return_code, self.out, self.err) + + +def exec_shell(command: str, is_shell=False, timeout=30) -> ExecutionOutcome: + """ + 执行命令,返回执行结果 + """ + try: + if is_shell: + child = subprocess.Popen(command, close_fds=True, stdout=subprocess.PIPE, stdin=None, + stderr=subprocess.PIPE, encoding="utf-8", universal_newlines=True, shell=is_shell) + + else: + child = subprocess.Popen(shlex.split(command), close_fds=True, stdout=subprocess.PIPE, stdin=None, + stderr=subprocess.PIPE, encoding="utf-8", universal_newlines=True) + + except Exception as ex: + logging.error(ex, exc_info=True) + raise ex + else: + try: + out, err = child.communicate(timeout=timeout) + except Exception as exception: + logging.error(exception, exc_info=True) + raise exception + else: + return ExecutionOutcome(child.returncode, out, err) diff --git a/tools/common/devkit_utils/transport_utils.py b/tools/common/devkit_utils/transport_utils.py new file mode 100644 index 0000000000000000000000000000000000000000..c3918e76ed68ef61ce4cf9125cad1a8ffc7e948a --- /dev/null +++ b/tools/common/devkit_utils/transport_utils.py @@ -0,0 +1,130 @@ +import logging +import os +import pathlib +from io import StringIO + +import paramiko + + +class TransportException(Exception): + def __init__(self, *args, **kwargs): + super(TransportException, self).__init__(args, kwargs) + + +class SSHClientFactory: + + def __init__(self, ip, user, password=None, port=22, pkey_file=None, pkey_content=None, pkey_password=None): + self.pkey_file = pkey_file + self.pkey_content = pkey_content + self.pkey_password = pkey_password + self.password = password + self.ip = ip + self.port = port + self.user = user + + def create_ssh_client(self): + ssh = paramiko.SSHClient() + ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) + try: + if self.password: + # 使用密码进行链接 + ssh.connect(hostname=self.ip, port=22, username=self.user, password=self.password) + elif self.pkey_file or self.pkey_content: + # 使用指定的免密文件路径或者免密内容进行链接(内容支持加密) + pkey = self.__pkey() + ssh.connect(hostname=self.ip, port=22, username=self.user, pkey=pkey) + else: + # 使用.ssh 下的私钥尝试链接 + try_result = False + for pkey_path in self.__get_home_pkey_path(): + try: + pkey = self.__pkey(pkey_path) + ssh.connect(hostname=self.ip, port=22, username=self.user, pkey=pkey) + try_result = True + break + except Exception as err: + logging.exception(err) + if not try_result: + raise TransportException("The passwordless configuration is incorrect") + except Exception as ex: + logging.exception(ex) + raise TransportException() + return ssh + + @staticmethod + def __get_home_pkey_path(): + pkey_files = list() + base = str(pathlib.Path.home()) + "/.ssh" + for filename in os.listdir(base): + filepath = os.path.join(base, filename) + if os.path.isfile(filepath) and filepath.startswith("id_") and not filepath.endswith("pub"): + pkey_files.append(filepath) + return pkey_files + + def __choose_pkey(self, pkey_path=None): + if pkey_path: + return paramiko.RSAKey.from_private_key_file(pkey_path) + if self.pkey_file: + return paramiko.RSAKey.from_private_key_file(self.pkey_file, password=self.pkey_password) + if self.pkey_content: + return paramiko.RSAKey.from_private_key(StringIO(self.pkey_content), password=self.pkey_password) + raise TransportException() + + def __pkey(self, pkey_path=None): + try: + # 指定本地的RSA私钥文件。如果建立密钥对时设置的有密码,password为设定的密码,如无不用指定password参数 + pkey = self.__choose_pkey(pkey_path) + except (IOError,) as e: + logging.error(f"Pkey file not exists. {str(e)}") + raise TransportException() + except (paramiko.ssh_exception.PasswordRequiredException, paramiko.ssh_exception.AuthenticationException) as e: + logging.warning(f"Pkey password is required. {str(e)}") + raise TransportException(str(e)) + except Exception as e: + logging.error(f"Connect remote {self.ip} failed because of wrong pkey. {str(e)}") + raise TransportException() + else: + return pkey + + +if __name__ == "__main__": + factory = SSHClientFactory("127.0.0.1", "root", pkey_content="""-----BEGIN OPENSSH PRIVATE KEY----- +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 +Q0ktTVQBAgMEBQYH +-----END OPENSSH PRIVATE KEY-----""") + factory.create_ssh_client() diff --git a/tools/distribute/build_devkit_distribute.sh b/tools/distribute/build_devkit_distribute.sh new file mode 100644 index 0000000000000000000000000000000000000000..6f7e05ec646ff6aec345189de233398046ccddf4 --- /dev/null +++ b/tools/distribute/build_devkit_distribute.sh @@ -0,0 +1,40 @@ +#!/bin/bash +# SourceCode build script +# Copyright: Copyright (c) Huawei Technologies Co., Ltd. All rights reserved. + +set -e +current_dir=$(cd $(dirname "$0"); pwd) +project_dir=$(realpath "${current_dir}/../..") +umask 077 + +build_dir=${project_dir}/build/distribute +rm -rf "${build_dir}" +mkdir -p "${build_dir}" + +cd "${build_dir}" + +pyinstaller -F "${current_dir}"/devkit_pipeline_agent/bin/flight_records_sample.py --runtime-tmpdir . \ + -p "${project_dir}"/tools/common + +mkdir -p devkit_pipeline_agent/bin +mkdir -p devkit_pipeline_agent/data +mkdir -p devkit_pipeline_agent/log + +cp "${build_dir}"/dist/flight_records_sample devkit_pipeline_agent/bin +cp "${current_dir}"/devkit_pipeline_agent/script/devkit_agent_start.sh devkit_pipeline_agent/bin +cp -rf "${current_dir}"/devkit_pipeline_agent/config devkit_pipeline_agent + +tar -czf devkit_pipeline_agent.tar.gz devkit_pipeline_agent + + +pyinstaller -F "${current_dir}"/devkit_distribute/bin/entrance.py --runtime-tmpdir . \ + -p "${project_dir}"/tools/common + +mkdir -p devkit_distribute/bin +mkdir -p devkit_distribute/data +mkdir -p devkit_distribute/log + +cp "${build_dir}"/dist/entrance devkit_distribute/bin +cp "${current_dir}"/devkit_distribute/script/devkit_pipeline_start.sh devkit_distribute/bin +cp -rf "${current_dir}"/devkit_distribute/config devkit_distribute +cp devkit_pipeline_agent.tar.gz devkit_distribute/config diff --git a/tools/distribute/devkit_distribute/bin/entrance.py b/tools/distribute/devkit_distribute/bin/entrance.py new file mode 100644 index 0000000000000000000000000000000000000000..b29ab2d6274dc6854bcce20124da3682d7e7dc21 --- /dev/null +++ b/tools/distribute/devkit_distribute/bin/entrance.py @@ -0,0 +1,192 @@ +import argparse +import datetime +import logging +import os +import uuid + +from devkit_utils import file_utils +from devkit_utils.devkit_client import DevKitClient +from devkit_utils.error_coce import ErrorCodeEnum, ErrorCodeMsg +from devkit_utils.log_config import config_log_ini +from devkit_utils.pyinstaller_utils import obtain_root_path +from devkit_utils.transport_utils import SSHClientFactory + +ROOT_PATH = os.path.dirname(os.path.dirname(__file__)) + + +class Distributor: + def __init__(self, args): + self.ips_list = args.ips_list.split(",") + self.port = args.port + self.user = args.user + self.password = args.password + self.pkey_file = args.pkey_file + self.pkey_content = args.pkey_content + self.pkey_password = args.pkey_password + self.duration = args.duration + self.root_path = args.root_path + self.apps = args.applications + self.data_path = os.path.join(self.root_path, "data") + self.log_path = os.path.join(self.root_path, "log") + self.devkit_ip = args.devkit_ip + self.devkit_port = args.devkit_port + self.devkit_user = args.devkit_user + self.devkit_password = args.devkit_password + file_utils.create_dir(self.data_path) + + def distribute(self): + task_id = str(uuid.uuid4()) + # 分发采集任务 + self.distribute_to_sample_task(task_id) + # 获取jfr文件,删除任务文件 + local_jfrs = list() + self.obtain_jfrs(local_jfrs, task_id) + # 发送至 Devkit + client = DevKitClient(self.devkit_ip, self.devkit_port, self.devkit_user, self.devkit_password) + for jfr in local_jfrs: + client.upload_report(jfr) + client.logout() + # 清空本地jfr文件 + file_utils.clear_dir(self.data_path) + + def obtain_jfrs(self, local_jfrs, task_id): + # 顺序获取 + for ip in self.ips_list: + factory = SSHClientFactory(ip=ip, user=self.user, port=self.port, password=self.password, + pkey_file=self.pkey_file, pkey_content=self.pkey_content, + pkey_password=self.pkey_password) + ssh_client = factory.create_ssh_client() + try: + logging.info("Wait for the server[%s] to finish uploading the jfr file", ip) + self.__blocking_util_upload_success(ssh_client, + f"{task_id}/devkit_pipeline_agent/config/complete_the_upload.ini", + ip) + + logging.info("obtain the jfr file name from ip:%s", ip) + stdin, stdout, stderr = ssh_client.exec_command( + f"cat {task_id}/devkit_pipeline_agent/config/upload_sample.ini") + jfr_paths_all = stdout.read().decode("utf-8") + jfr_paths = jfr_paths_all.split(os.linesep) if jfr_paths_all else [] + logging.info("jfr path:%s", jfr_paths) + self.__close_pipeline(stdin, stdout, stderr) + sftp_client = ssh_client.open_sftp() + log_ip_name = ip.replace(".", "_") + sftp_client.get(f"{task_id}/devkit_pipeline_agent/log/devkit_pipeline_agent.log", + f"{self.log_path}/devkit_pipeline_agent_{log_ip_name}.log") + logging.info("download the jfr file from ip:%s", ip) + if not jfr_paths: + raise Exception(f"The jfr file {self.apps} cannot be generated") + for jfr_path in jfr_paths: + local_path = os.path.join(self.data_path, os.path.basename(jfr_path)) + sftp_client.get(jfr_path, local_path) + local_jfrs.append(local_path) + sftp_client.close() + logging.info("the server[%s] has finish uploading the jfr file or has timeout 6000", ip) + except Exception as ex: + logging.exception(ex) + finally: + self.__delete_agent(ssh_client, task_id) + ssh_client.close() + + def distribute_to_sample_task(self, task_id): + # 分发采集任务 + for ip in self.ips_list: + factory = SSHClientFactory(ip=ip, user=self.user, port=self.port, password=self.password, + pkey_file=self.pkey_file, pkey_content=self.pkey_content, + pkey_password=self.pkey_password) + ssh_client = factory.create_ssh_client() + try: + logging.info("ip:%s create %s directory ", ip, task_id) + stdin, stdout, stderr = ssh_client.exec_command(f"mkdir {task_id}") + self.__close_pipeline(stdin, stdout, stderr) + agent_package = os.path.join(self.root_path, "config/devkit_pipeline_agent.tar.gz") + logging.info("ip:%s upload devkit_pipeline_agent.tar.gz", ip) + sftp_client = ssh_client.open_sftp() + sftp_client.put(agent_package, f"{task_id}/devkit_pipeline_agent.tar.gz") + sftp_client.close() + logging.info("ip:%s unpack devkit_pipeline_agent.tar.gz", ip) + stdin, stdout, stderr = ssh_client.exec_command( + f"cd {task_id} && tar -xvzf devkit_pipeline_agent.tar.gz --no-same-owner") + logging.info("upack tar.gz %s", stderr.readlines()) + self.__close_pipeline(stdin, stdout, stderr) + logging.info("ip:%s start devkit pipeline agent", ip) + stdin, stdout, stderr = ssh_client.exec_command( + f"sh {task_id}/devkit_pipeline_agent/bin/devkit_agent_start.sh " + f"-a {self.apps} -d {self.duration} -t {task_id}") + logging.info("start the sampling process on server %s:%s", ip, stderr.readlines()) + self.__close_pipeline(stdin, stdout, stderr) + logging.info("ip:%s The devkit pipeline agent was successfully launched", ip) + except Exception as ex: + self.__delete_agent(ssh_client, task_id) + raise ex + finally: + ssh_client.close() + + @staticmethod + def __delete_agent(ssh_client, task_id): + ssh_client.exec_command(f"rm -rf {task_id}") + ssh_client.exec_command(f"rm -rf /tmp/{task_id}") + + def __blocking_util_upload_success(self, ssh_client, transport_file, ip): + before = datetime.datetime.now() + while (datetime.datetime.now() - before).seconds < 6000 + self.duration: + stdin, stdout, stderr = ssh_client.exec_command(f"cat {transport_file}") + file_content = stdout.read().decode("utf-8").strip() + if file_content == str(ErrorCodeEnum.FINISHED): + return True + elif file_content == str(ErrorCodeEnum.NOT_FOUND_JCMD): + raise Exception(ErrorCodeMsg.LANGUAGE_EN.get(ErrorCodeEnum.NOT_FOUND_JCMD).format(ip)) + elif file_content == str(ErrorCodeEnum.NOT_FOUND_APPS): + raise Exception(ErrorCodeMsg.LANGUAGE_EN.get(ErrorCodeEnum.NOT_FOUND_APPS).format(self.apps, ip)) + return False + + @staticmethod + def __close_pipeline(stdin, stdout, stderr): + stdin.close() + stdout.close() + stderr.close() + + +def main(): + parser = argparse.ArgumentParser(description="Capture the flight records of the target program") + parser.add_argument("-i", "--ips", required=True, dest="ips_list", + help="the machine ips on which the java application is running ") + parser.add_argument("-u", "--user", required=True, dest="user", default="root", + help="the user password of the ips") + parser.add_argument("-P", "--port", dest="port", type=int, default=22, + help="the ssh port of the ips") + parser.add_argument("-p", "--password", dest="password", + help="the user password of the ips") + parser.add_argument("-f", "--pkey-file", dest="pkey_file", + help="the file path of the private key") + parser.add_argument("-c", "--pkey-content", dest="pkey_content", + help="the content of the private key") + parser.add_argument("-w", "--pkey-password", dest="pkey_password", + help="the private key password") + parser.add_argument("--devkit-ip", dest="devkit_ip", required=True, + help="the process names that can be multiple, each separated by a comma") + parser.add_argument("--devkit-port", dest="devkit_port", default="8086", + help="the process names that can be multiple, each separated by a comma") + parser.add_argument("--devkit-user", dest="devkit_user", default="devadmin", + help="the process names that can be multiple, each separated by a comma") + parser.add_argument("--devkit-password", dest="devkit_password", default="admin100", + help="the process names that can be multiple, each separated by a comma") + parser.add_argument("-a", "--app", required=True, dest="applications", + help="the process names that can be multiple, each separated by a comma") + parser.add_argument("-d", "--duration", required=True, dest="duration", type=int, + help="the time of the sample") + parser.set_defaults(root_path=obtain_root_path(ROOT_PATH)) + args = parser.parse_args() + config_log_ini(args.root_path, "devkit_distribute") + logging.info("devkit_distribute start") + logging.info(args) + distributor = Distributor(args) + distributor.distribute() + + +if __name__ == "__main__": + try: + main() + except Exception as err: + logging.exception(err) + raise err diff --git a/tools/distribute/devkit_distribute/bin/template.py b/tools/distribute/devkit_distribute/bin/template.py new file mode 100644 index 0000000000000000000000000000000000000000..23f1915f233edb889cdb34167b3e1e6a202bf6cc --- /dev/null +++ b/tools/distribute/devkit_distribute/bin/template.py @@ -0,0 +1,158 @@ +performance_report = """ + + +
+ + +