diff --git a/auto_autopkgtest.py b/auto_autopkgtest.py index cbe5b87..d62b376 100644 --- a/auto_autopkgtest.py +++ b/auto_autopkgtest.py @@ -1,9 +1,202 @@ +import os , shutil +import re import sys +import argparse +import paramiko +import subprocess +import time +from lib import sftp,ssh_cmd + +class QemuVM(object): + def __init__(self, vcpu=2,memory=2,workingDir='/',bkfile=None ,kernel=None,bios=None,id=1,port=12055,user='root',password='openEuler12#$', path='/root' , restore = True): + self.id = id + self.port , self.ip , self.user , self.password = port , '127.0.0.1' , user , password + self.vcpu , self.memory= vcpu , memory + self.workingDir , self.bkFile = workingDir , bkfile + self.kernel , self.bios = kernel , bios + self.drive = 'img'+str(self.id)+'.qcow2' + self.path = path + self.restore = restore + self.tapls = [] + if self.workingDir[-1] != '/': + self.workingDir += '/' + + def start(self): + self.port = findAvalPort(1)[0] + if self.drive in os.listdir(self.workingDir): + os.system('rm -f '+self.workingDir+self.drive) + if self.restore: + cmd = 'qemu-img create -f qcow2 -F qcow2 -b '+self.workingDir+self.bkFile+' '+self.workingDir+self.drive + res = os.system(cmd) + if res != 0: + print('Failed to create cow img: '+self.drive) + return -1 + ## Configuration + memory_append=self.memory * 1024 + if self.restore: + drive=self.workingDir+self.drive + else: + drive=self.workingDir+self.bkFile + if self.kernel is not None: + kernelArg=" -kernel "+self.kernel + else: + kernelArg=" " + if self.bios is not None: + if self.bios == 'none': + biosArg=" -bios none" + else: + biosArg=" -bios "+self.workingDir+self.bios + else: + biosArg=" " + ssh_port=self.port + cmd="qemu-system-riscv64 \ + -nographic -machine virt \ + -smp "+str(self.vcpu)+" -m "+str(self.memory)+"G \ + "+kernelArg+" \ + "+biosArg+" \ + -drive file="+drive+",format=qcow2,id=hd0 \ + -object rng-random,filename=/dev/urandom,id=rng0 \ + -device virtio-rng-device,rng=rng0 \ + -device virtio-blk-device,drive=hd0 \ + -device qemu-xhci -usb -device usb-kbd -device usb-tablet \ + -netdev user,id=usernet,hostfwd=tcp::"+str(ssh_port)+"-:22 -device virtio-net-device,netdev=usernet " + print(cmd) + self.process = subprocess.Popen(args=cmd,stderr=subprocess.PIPE,stdout=subprocess.PIPE,stdin=subprocess.PIPE,encoding='utf-8',shell=True) + + def waitReady(self): + conn = 519 + while conn == 519: + conn = paramiko.SSHClient() + conn.set_missing_host_key_policy(paramiko.AutoAddPolicy) + try: + time.sleep(5) + conn.connect(self.ip, self.port, self.user, self.password, timeout=5) + except Exception as e: + conn = 519 + if conn != 519: + conn.close() + + def destroy(self): + ssh_exec(self,'poweroff') + if self.restore: + os.system('rm -f '+self.workingDir+self.drive) + os.system('rm -f '+self.workingDir+'disk'+str(self.id)+'-*') + +def sftp_get(qemuVM,remotedir,remotefile,localdir,timeout=5): + conn = paramiko.SSHClient() + conn.set_missing_host_key_policy(paramiko.AutoAddPolicy) + conn.connect(qemuVM.ip,qemuVM.port,qemuVM.user,qemuVM.password,timeout=timeout,allow_agent=False,look_for_keys=False) + sftp.psftp_get(conn,remotedir,remotefile,localdir) + +def findAvalPort(num=1): + port_list = [] + port = 12055 + while(len(port_list) != num): + if os.system('netstat -anp 2>&1 | grep '+str(port)+' > /dev/null') != 0: + port_list.append(port) + port += 1 + return port_list + +def ssh_exec(qemuVM,cmd,timeout=5): + conn = paramiko.SSHClient() + conn.set_missing_host_key_policy(paramiko.AutoAddPolicy) + conn.connect(qemuVM.ip,qemuVM.port,qemuVM.user,qemuVM.password,timeout=timeout,allow_agent=False,look_for_keys=False) + exitcode,output = ssh_cmd.pssh_cmd(conn,cmd) + ssh_cmd.pssh_close(conn) + return exitcode,output + +def get_apt_list(workDir , image , kernel): + tempVM = QemuVM(workingDir=workDir , bkfile=image , id=1 , kernel=kernel , user='root' , password='openkylin') + tempVM.start() + tempVM.waitReady() + print(ssh_exec(tempVM , 'apt update')[1]) + ssh_exec(tempVM , "apt list | awk -F/ '{print$1}' > apt_list") + sftp_get(qemuVM=tempVM , remotedir='/root' , remotefile='apt_list' , localdir='.') + tempVM.destroy() + return 'apt_list' + if __name__ == "__main__": argv = sys.argv[1:] full_argv = ' '.join(argv) - autopkgtest_argv = ' -- '.join(full_argv.split(' -- ')[:-1]) - attach_argv = full_argv.split(' -- ')[-1] + autopkgtest_argv = full_argv.split(' -- ')[1:] + attach_argv = full_argv.split(' -- ')[0].split() - \ No newline at end of file + parser = argparse.ArgumentParser() + parser.add_argument('-l','--list' , type=str , dest="list" , default=None , help="Specify the test targets list") + parser.add_argument('--image' , type=str , default=None , help="Specify backing image file name") + parser.add_argument('-w','--workDir' , type=str , dest="workDir" , default='.' , help="Specify working directory , default is '.' , the test result will be store at workDir/testRes/testname") + parser.add_argument('--src' , action='store_true' , default=True , help="Get the test source code , if ture , will be store at workDir/testSrc") + parser.add_argument('-a' , action='store_true' , default=False , help="Get all the packade listed in apt list to test , if test targets list is specified , would use the target list") + parser.add_argument('--kernel' , type=str , default=None , help="Specify the boot kernel , will append to the autopkgtest qemu option and boot the qemuVM to get apt list") + parser.add_argument('--destdir' , type=str , default='' , help="Specify the autopkgtest install destdir") + args = parser.parse_args(attach_argv) + kernel , workDir , image , apt_list = args.kernel , args.workDir , None , None + destdir = args.destdir.rstrip('/') + if args.image is None: + print("please specify backing image file name") + exit(1) + else: + image = args.image + if args.list is not None: + apt_list = args.list + elif args.a and kernel is not None: + apt_list = get_apt_list(workDir , image , kernel) + + try: + os.mkdir(os.path.join(workDir , 'testRes')) + except FileExistsError: + pass + if args.src: + try: + os.mkdir(os.path.join(workDir , 'testSrc')) + except FileExistsError: + pass + if apt_list is not None: + list_file = open(apt_list , 'r') + raw = list_file.read() + test_list = raw.split(sep="\n") + list_file.close() + + test_list = [x.strip() for x in test_list if x.strip()!='' and x != 'Listing...'] #Remove empty elements + else: + test_list = [] + + + pkg_no_source = set() + for test in test_list: + if os.path.exists(os.path.join(workDir , 'testRes' , test)): + continue + else: + os.mkdir(os.path.join(workDir , 'testRes' , test)) + Args = ' -o '+os.path.join(workDir , 'testRes' , test) + srcDir = '' + cmd = destdir+'/usr/bin/autopkgtest '+autopkgtest_argv[0]+' '+test+Args+' -- qemu '+autopkgtest_argv[1]+" --qemu-option='-machine virt -kernel "+kernel+"' "+workDir+image + print("execute: "+cmd) + proc = subprocess.Popen(cmd , shell=True , stdin=subprocess.PIPE , stdout=subprocess.PIPE , stderr=subprocess.PIPE) + stderr = iter(proc.stderr.readline , b'') + for line in stderr: + line = line.decode('utf-8') + print(line ,end='') + if line == 'W: Unable to locate package '+test+'\n': + pkg_no_source.add(test) + elif args.src and not os.path.exists(os.path.join(workDir , 'testSrc' , test)): + if srcDir == '' and re.search(r'autopkgtest-virt-qemu: DBG: executing copyup /tmp/(.*)/src/ (.*)/tests-tree/\n' , line) != None: + srcDir = os.path.join(workDir , 'testSrc' , test) + elif srcDir == os.path.join(workDir , 'testSrc' , test): + os.mkdir(srcDir) + cpcmd = 'cp -r '+os.path.join(workDir , 'testRes' , test , 'tests-tree')+'/* '+srcDir + os.system(cpcmd) + if proc.poll() is not None: + if line == "": + break + if test in pkg_no_source: + shutil.rmtree(os.path.join(workDir , 'testRes' , test)) + elif 'SKIP no tests in this package' in open(os.path.join(workDir , 'testRes' , test , 'summary') , 'r').read(): + if not os.path.exists(os.path.join(workDir , 'emptyTest')): + os.mkdir(os.path.join(workDir , 'emptyTest')) + os.system('mv '+os.path.join(workDir , 'testRes' , test)+" "+os.path.join(workDir , 'emptyTest')) + with open('pkg_no_source' , 'w') as f: + for pkg in pkg_no_source: + f.write(pkg+'\n') + diff --git a/lib/sftp.py b/lib/sftp.py new file mode 100644 index 0000000..fe092fa --- /dev/null +++ b/lib/sftp.py @@ -0,0 +1,233 @@ +# -*- coding: utf-8 -*- +""" + Copyright (c) [2021] Huawei Technologies Co.,Ltd.ALL rights reserved. + This program is licensed under Mulan PSL v2. + You can use it according to the terms and conditions of the Mulan PSL v2. + http://license.coscl.org.cn/MulanPSL2 + THIS PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + See the Mulan PSL v2 for more details. + + @Author : lemon-higgins + @email : lemon.higgins@aliyun.com + @Date : 2021-04-21 16:14:27 + @License : Mulan PSL v2 + @Version : 1.0 + @Desc : 文件传输 +""" + +import os +import sys +import stat +import re +import paramiko +import argparse +import subprocess + +SCRIPT_PATH = os.path.dirname(os.path.abspath(__file__)) +sys.path.append(SCRIPT_PATH) +import ssh_cmd + + +def get_remote_file(sftp, remote_dir, remote_file=None): + """获取对端文件 + + Args: + sftp (class): 和对端建立连接 + remote_dir ([str]): 远端需要传输的文件所在的目录 + remote_file ([str], optional): 远端需要传输的文件. Defaults to None. + + Returns: + [list]: 文件列表 + """ + all_file = list() + + remote_dir = remote_dir.rstrip("/") + + dir_files = sftp.listdir_attr(remote_dir) + for d_f in dir_files: + if remote_file is not None and re.match(remote_file, d_f.filename) is None: + continue + + _name = remote_dir + "/" + d_f.filename + if stat.S_ISDIR(d_f.st_mode): + all_file.extend(get_remote_file(sftp, _name)) + else: + all_file.append(_name) + + return all_file + + +def psftp_get(conn, remote_dir, remote_file="", local_dir=os.getcwd()): + """获取远端文件 + + Args: + conn ([class]): 和远端建立连接 + remote_dir ([str]): 远端需要传输的文件所在的目录 + remote_file ([str], optional): 远端需要传输的文件. Defaults to None. + local_dir ([str], optional): 本地存放文件的目录. Defaults to os.getcwd(). + """ + if conn == 519: + sys.exit(519) + + sftp = paramiko.SFTPClient.from_transport(conn.get_transport()) + + if ssh_cmd.pssh_cmd(conn, "test -d " + remote_dir)[0]: + print("error: remote dir:%s does not exist" % remote_dir) + conn.close() + sys.exit(1) + + all_file = list() + if remote_file == "": + all_file = get_remote_file(sftp, remote_dir) + else: + if ssh_cmd.pssh_cmd(conn, "test -f " + os.path.join(remote_dir, remote_file))[0]: + print("error: remote file:%s does not exist" % remote_file ) + conn.close() + sys.exit(1) + + all_file = get_remote_file(sftp, remote_dir, remote_file) + + local_dir = os.path.normpath(local_dir) + remote_dir = os.path.normpath(remote_dir) + + for f in all_file: + if remote_file == "": + storage_dir = remote_dir.split("/")[-1] + storage_path = os.path.join( + local_dir, storage_dir + os.path.dirname(f[len(remote_dir):]) + ) + if not os.path.exists(storage_path): + os.makedirs(storage_path) + sftp.get(f, os.path.join(storage_path, f.split("/")[-1])) + else: + if not os.path.exists(local_dir): + os.makedirs(local_dir) + sftp.get(f, os.path.join(local_dir, f.split("/")[-1])) + print("start to get file:%s......" % f) + + conn.close() + + +def get_local_file(local_dir, local_file=None): + """获取本地文件列表 + + Args: + local_dir ([str]): 本地文件所在的目录 + local_file ([str], optional): 本地需要传输的文件. Defaults to None. + + Returns: + [list]: 文件列表 + """ + all_file = list() + + local_dir = local_dir.rstrip("/") + + dir_files = os.listdir(local_dir) + for d_f in dir_files: + if local_file is not None and re.match(local_file, d_f) is None: + continue + + _name = local_dir + "/" + d_f + if os.path.isdir(_name): + all_file.extend(get_local_file(_name)) + else: + all_file.append(_name) + + return all_file + + +def psftp_put(conn, local_dir=os.getcwd(), local_file="", remote_dir=""): + """将本地文件传输到远端 + + Args: + conn ([class]): 和远端建立连接 + local_dir ([str]): 本地文件所在的目录 + local_file ([str], optional): 本地需要传输的文件. Defaults to None. + remote_dir (str, optional): 远端存放文件的目录. Defaults to 根目录. + """ + if conn == 519: + sys.exit(519) + + sftp = paramiko.SFTPClient.from_transport(conn.get_transport()) + + if subprocess.getstatusoutput("test -d " + local_dir)[0]: + conn.close() + sys.exit(1) + + all_file = list() + if local_file == "": + all_file = get_local_file(local_dir) + else: + if subprocess.getstatusoutput("test -f " + os.path.join(local_dir, local_file))[0]: + conn.close() + sys.exit(1) + all_file = get_local_file(local_dir, local_file) + + if remote_dir == "": + remote_dir = ssh_cmd.pssh_cmd(conn, "pwd")[1] + + local_dir = os.path.normpath(local_dir) + remote_dir = os.path.normpath(remote_dir) + + for f in all_file: + if local_file == "": + storage_dir = local_dir.split("/")[-1] + storage_path = os.path.join( + remote_dir, storage_dir + os.path.dirname(f[len(local_dir):]) + ) + if ssh_cmd.pssh_cmd(conn, "test -d " + storage_path)[0]: + ssh_cmd.pssh_cmd(conn, "mkdir -p " + storage_path) + sftp.put(f, os.path.join(storage_path, f.split("/")[-1])) + else: + if ssh_cmd.pssh_cmd(conn, "test -d " + remote_dir)[0]: + ssh_cmd.pssh_cmd(conn, "mkdir -p " + remote_dir) + sftp.put(f, os.path.join(remote_dir, f.split("/")[-1])) + + conn.close() + + +if __name__ == "__main__": + parser = argparse.ArgumentParser(description="manual to this script") + parser.add_argument("operation", type=str, choices=["get", "put"], default=None) + parser.add_argument( + "--remotedir", type=str, default=None, help="Must be an absolute path" + ) + parser.add_argument("--node", type=int, default=2) + parser.add_argument("--remotefile", type=str, default="") + parser.add_argument("--localdir", type=str, default=os.getcwd()) + parser.add_argument("--localfile", type=str, default="") + parser.add_argument("--ip", type=str, default=None) + parser.add_argument("--password", type=str, default=None) + parser.add_argument("--port", type=int, default=22) + parser.add_argument("--user", type=str, default="root") + parser.add_argument("--timeout", type=int, default=None) + args = parser.parse_args() + + if args.node is not None and args.node > 0: + args.ip = os.environ.get("NODE" + str(args.node) + "_IPV4") + args.password = os.environ.get("NODE" + str(args.node) + "_PASSWORD") + args.port = os.environ.get("NODE" + str(args.node) + "_SSH_PORT") + args.user = os.environ.get("NODE" + str(args.node) + "_USER") + + if ( + args.ip is None + or args.password is None + or args.port is None + or args.user is None + ): + sys.exit(1) + + if args.node < 0: + if args.ip is None or args.password is None: + sys.exit(1) + + conn = ssh_cmd.pssh_conn(args.ip, args.password, args.port, args.user, args.timeout) + + if sys.argv[1] == "get": + psftp_get(conn, args.remotedir, args.remotefile, args.localdir) + elif sys.argv[1] == "put": + psftp_put(conn, args.localdir, args.localfile, args.remotedir) + else: + sys.exit(1) diff --git a/lib/ssh_cmd.py b/lib/ssh_cmd.py new file mode 100644 index 0000000..501f0db --- /dev/null +++ b/lib/ssh_cmd.py @@ -0,0 +1,131 @@ +# -*- coding: utf-8 -*- +""" + Copyright (c) [2021] Huawei Technologies Co.,Ltd.ALL rights reserved. + This program is licensed under Mulan PSL v2. + You can use it according to the terms and conditions of the Mulan PSL v2. + http://license.coscl.org.cn/MulanPSL2 + THIS PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + See the Mulan PSL v2 for more details. + + @Author : lemon-higgins + @email : lemon.higgins@aliyun.com + @Date : 2021-04-21 11:54:57 + @License : Mulan PSL v2 + @Version : 1.0 + @Desc : 远端命令执行 +""" + +import os +import sys +import argparse +import paramiko + +SCRIPT_PATH = os.path.dirname(os.path.abspath(__file__)) +sys.path.append(SCRIPT_PATH) + + +def pssh_conn( + ip, + password, + port=22, + user="root", + timeout=None, + log_level="error", +): + """和远端建立连接 + + Args: + ip ([str]): 远端ip + password ([str]): 远端用户密码 + port (int, optional): 远端ssh的端口号. Defaults to 22. + user (str, optional): 远端用户名. Defaults to "root". + timeout ([int], optional): ssh的超时时长. Defaults to None. + + Returns: + [class]: 建立起来的连接 + """ + conn = paramiko.SSHClient() + conn.set_missing_host_key_policy(paramiko.AutoAddPolicy) + try: + conn.connect(ip, port, user, password, timeout=timeout) + except ( + paramiko.ssh_exception.NoValidConnectionsError, + paramiko.ssh_exception.AuthenticationException, + paramiko.ssh_exception.SSHException, + TypeError, + AttributeError, + ) as e: + return 519 + + return conn + + +def pssh_cmd(conn, cmd): + """远端命令执行 + + Args: + conn ([class]): 和远端建立连接 + cmd ([str]): 需要执行的命令 + + Returns: + [list]: 错误码,命令执行结果 + """ + if conn == 519: + return 519, "" + stdin, stdout, stderr = conn.exec_command(cmd, timeout=None) + + exitcode = stdout.channel.recv_exit_status() + + if exitcode == 0: + output = stdout.read().decode("utf-8").strip("\n") + else: + output = stderr.read().decode("utf-8").strip("\n") + + return exitcode, output + + +def pssh_close(conn): + """关闭和远端的连接 + + Args: + conn ([class]): 和远端的连接 + """ + if conn != 519: + conn.close() + + +if __name__ == "__main__": + + parser = argparse.ArgumentParser(description="manual to this script") + parser.add_argument("--cmd", type=str, default=None, required=True) + parser.add_argument("--node", type=int, default=2) + parser.add_argument("--ip", type=str, default=None) + parser.add_argument("--password", type=str, default=None) + parser.add_argument("--port", type=int, default=22) + parser.add_argument("--user", type=str, default="root") + parser.add_argument("--timeout", type=int, default=None) + args = parser.parse_args() + + if args.node is not None: + args.ip = os.environ.get("NODE" + str(args.node) + "_IPV4") + args.password = os.environ.get("NODE" + str(args.node) + "_PASSWORD") + args.port = os.environ.get("NODE" + str(args.node) + "_SSH_PORT") + args.user = os.environ.get("NODE" + str(args.node) + "_USER") + + if ( + args.ip is None + or args.password is None + or args.port is None + or args.user is None + ): + sys.exit(1) + + conn = pssh_conn(args.ip, args.password, args.port, args.user, args.timeout) + exitcode, output = pssh_cmd(conn, args.cmd) + pssh_close(conn) + + print(output) + + sys.exit(exitcode) diff --git a/readme.md b/readme.md index 26a4f9a..713ed5b 100644 --- a/readme.md +++ b/readme.md @@ -76,13 +76,30 @@ sudo autopkgtest systemd_245.4-4ubuntu3.19.dsc -o test -d \ - --qemu-options决定qemu启动时附加的命令行参数 - qcow2文件为镜像文件 -## 自动化对应思路 -- 将准备的镜像拷贝两份,一份为source,正常启动并使用ssh获取openkylin对应软件源的源码包 -- 另一份作为autopkgtest的镜像文件 -- 其中[auto.sh](./auto.sh)即实现运行测试例,筛选功能,手动调整部分为启动source镜像,选择一个没有被占用的端口并输入auto.sh,同时需要保证source镜像内/root文件夹为空。apt_list为待测的软件包的列表,install_dist为你安装autopkgtest时的DESTDIR,workdir为autopkgtest使用镜像所在位置,img为对应镜像名, 总的使用格式如下 -``` -bash auto.sh port apt_list workdir img install_dist +## 自动化脚本说明 +此处给出可自动进行多个pkg包测试并分类归入文件夹的脚本:`auto_autopkgtest.py`,目前可实现如下功能: +- 自动获取镜像中apt可装所有软件,并生成列表 +- 导入给定列表将其中的软件包进行测试,并对结果进行分类,详细结果分类包括: + - 测试完成——测试结果将位于workDir/testRes文件夹中 + - 软件包源码中并未含有测试用例——对应文件夹将于workDir/emptyTest文件夹中 + - 无法通过此软件包名从`apt-get source`中获得源码包——将无对应文件夹,其软件名将输出至./pkg_no_source列表中 +- 自动获取其源码文件树——重点为源码中的测试源码 + - 由于源码中的测试例形式多样,无法以一种统一的方式获得全面的测试源码,故会将源码文件树完全保存,请自行甄别 + +详细使用方法如下: ``` -注意u-boot镜像也要在workdir中, -运行结束后会产生pkg_no_souce , pkg_no_test 与 pkg_finish列表,只做计数用。 -测试结果会在testing文件夹中 +python3 auto_autopkgtest.py [-h] [-l LIST] [--image IMAGE] [-w WORKDIR] [--src] [-a] [--kernel KERNEL] [--destdir DESTDIR] -- [autopkgtest_args] -- qemu [qemu_args] + +具体参数: + -l LIST, --list LIST 要批量测试的软件包列表 + --image IMAGE qemu镜像名 + -w WORKDIR, --workDir WORKDIR + 工作目录,qemu镜像因应在此目录下,测试结果同样会被输出自此,若不填,则默认为脚本所在目录 + --src 是否获取源码 + -a 是否获取apt中所有软件的源码 + --kernel KERNEL 启动qemu时-kernel一项文件所在的完整路径 + --destdir DESTDIR 安装autopkgtest是对应的destdir + autopkgtest_args autopkgtest运行时的运行参数,具体如上autopkgtest的使用 + qemu_args autopkgtest-virt-qemu运行时的参数,具体可参考上面autopkgtest的使用 +``` +简单的对第一个`--`后参数的解释,将正常启动autopkgtest的整串命令,去除前面的autopkgtest,中间的测试包名,以及output-dir,后面` -- qemu`后的镜像名,以及--qemu-option所得到的字符串即为需要放在第一个`--`后的全部 \ No newline at end of file diff --git a/sshcmd.sh b/sshcmd.sh deleted file mode 100644 index 583e7e0..0000000 --- a/sshcmd.sh +++ /dev/null @@ -1,135 +0,0 @@ -#!/usr/bin/bash -# Copyright (c) [2020] Huawei Technologies Co.,Ltd.ALL rights reserved. -# This program is licensed under Mulan PSL v2. -# You can use it according to the terms and conditions of the Mulan PSL v2. -# http://license.coscl.org.cn/MulanPSL2 -# THIS PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, -# EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, -# MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. -# See the Mulan PSL v2 for more details. -#################################### -#@Author : lemon.higgins -#@Contact : lemon.higgins@aliyun.com -#@Date : 2020-04-08 16:13:40 -#@License : Mulan PSL v2 -#@Version : 1.0 -#@Desc : Encapsulate ssh, user t directly, and execute remote commands -#################################### - -function sshcmd() { - cmd=$1 - remoteip=$2 - remotepasswd=${3} - remoteuser=${4} - timeout=${5} - connport=${6} - - test "$cmd"x = ""x && echo "Lack of execute command." && exit 1 - cmd=${cmd//\$/\\\$} - - test "$remoteip"x = ""x && echo "Missing ip." - test "$(echo ${remoteip} | awk -F"." '{if ($1!=0 && $NF!=0) split ($0,IPNUM,".")} END { for (k in IPNUM) if (IPNUM[k]==0) print IPNUM[k]; else if (IPNUM[k]!=0 && IPNUM[k]!~/[a-z|A-Z]/ && length(IPNUM[k])<=3 && IPNUM[k]<=255 && IPNUM[k]!~/^0/) print IPNUM[k]}' | wc -l)" -ne 4 && echo "the remote ip is Incorrect." && exit 1 - if ping -c 1 ${remoteip} | grep "100% packet loss"; then - echo "connection to $remoteip failed." - exit 101 - fi - - test "$remoteuser"x = "root"x && echo "the remote user uses the default configuration." - - test "$remotepasswd"x = "openEuler12#$"x && echo "the remote password uses the default configuration." - - test "$timeout"x = "15"x && echo "the timeout uses the default configuration." - - test "$connport"x = "22"x && echo "the connect port using the default configuration" - - cmd_last_world=$(echo ${cmd} | awk '{print $NF}') - - e_time=${timeout} - - test "$cmd_last_world"x == "&"x && { - timeout=0 - e_time=-1 - } - - expect <<-EOF - - set timeout ${e_time} - - spawn ssh -o "ConnectTimeout=${timeout}" -p ${connport} ${remoteuser}@${remoteip} "$cmd" - - expect { - "Are you sure you want to continue connecting*" - { - send "yes\r" - expect "*\[P|p]assword:" - send "${remotepasswd}\r" - } - "*\[P|p]assword:" - { - send "${remotepasswd}\r" - } - timeout - { - end_user "connection to $remoteip timed out: \$expect_out(buffer)\n" - exit 101 - } - eof - { - catch wait result - exit [lindex \$result 3] - } - } - expect { - eof { - catch wait result - exit [lindex \$result 3] - } - "\[P|p]assword:" - { - send_user "invalid password or account again.\$expect_out(buffer)\n" - send "${remotepasswd}\r" - } - timeout - { - send_user "connection to $remoteip timed out: \$expect_out(buffer)\n" - exit 101 - } - } - } -EOF - exit $? -} - -usage() { - printf "Usage: sshcmd.sh -c \"command\" -i \"remote machinet ip\" [-u login_user] [-p login_password] [-o port] [-t timeout]\n" -} - -while getopts "c:i:p:u:t:o:h" OPTIONS; do - case $OPTIONS in - c) cmd="$OPTARG" ;; - i) remoteip="$OPTARG" ;; - u) remoteuser="$OPTARG" ;; - p) remotepasswd="$OPTARG" ;; - t) timeout="$OPTARG" ;; - o) connport="$OPTARG" ;; - \?) - printf "ERROR - Invalid parameter" >&2 - usage - exit 1 - ;; - *) - printf "ERROR - Invalid parameter" >&2 - usage - exit 1 - ;; - esac -done - -if [ "$cmd"x = ""x ] || [ "$remoteip"x = ""x ]; then - usage - exit 1 -fi - -sshcmd "$cmd" "$remoteip" "${remotepasswd-openEuler12#$}" "${remoteuser-root}" "${timeout-300}" "${connport-22}" - -exit $? diff --git a/sshscp.sh b/sshscp.sh deleted file mode 100644 index 1874267..0000000 --- a/sshscp.sh +++ /dev/null @@ -1,107 +0,0 @@ -#!/usr/bin/bash -# Copyright (c) [2020] Huawei Technologies Co.,Ltd.ALL rights reserved. -# This program is licensed under Mulan PSL v2. -# You can use it according to the terms and conditions of the Mulan PSL v2. -# http://license.coscl.org.cn/MulanPSL2 -# THIS PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, -# EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, -# MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. -# See the Mulan PSL v2 for more details. -#################################### -#@Author : lemon.higgins -#@Contact : lemon.higgins@aliyun.com -#@Date : 2020-04-09 17:58:35 -#@License : Mulan PSL v2 -#@Version : 1.0 -#@Desc : 封装scp命令,供文件传输使用 -#################################### - -function sshscp() { - src=$1 - dest=$2 - remotepasswd=${3-openEuler12#$} - connport=${4-22} - - test "$src"x = ""x && echo "No transfer file provided." && exit 1 - - test "$dest"x = ""x && echo "No file storage path provided." && exit 1 - - test "$remotepasswd"x = "openEuler12#$"x && echo "the remote password uses the default configuration." - - test "$connport"x = "22"x && echo "the connect port using the default configuration" - - expect <<-EOF - set timeout -1 - spawn scp -P $connport $src $dest - expect { - "Are you sure you want to continue connecting*" - { - send "yes\r" - expect "\[P|p]assword:" - send "${remotepasswd}\r" - } - -re "\[P|p]assword:" - { - send "${remotepasswd}\r" - } - timeout - { - send_user "connection to remote timed out: \$expect_out(buffer)\n" - exit 101 - } - eof - { - catch wait result - exit [lindex \$result 3] - } - } - expect { - eof - { - catch wait result - exit [lindex \$result 3] - } - -re "\[P|p]assword:" - { - send_user "invalid password or account. \$expect_out(buffer)\n" - exit 13 - } - timeout - { - send_user "connection to remote timed out : \$expect_out(buffer)\n" - exit 101 - } - } -EOF - exit $? -} - -usage() { - printf "Usage: sshscp.sh -s src(user@ip:path) -d destination((user@ip:path)) [-p login_password] [-o port] -r -t timeout\n" -} - -while getopts "p:s:d:o:h" OPTIONS; do - case $OPTIONS in - p) remotepasswd="$OPTARG" ;; - s) src="$OPTARG" ;; - d) dest="$OPTARG" ;; - o) connport="$OPTARG" ;; - h) - usage - exit 1 - ;; - \?) - printf "ERROR - Invalid parameter" >&2 - usage - exit 1 - ;; - *) - printf "ERROR - Invalid parameter" >&2 - usage - exit 1 - ;; - esac -done - -sshscp "$src" "$dest" "$remotepasswd" "$connport" -exit $?