美文网首页程序员程序员首页投稿首页投稿(暂停使用,暂停投稿)
基于Python3+PyQt5的自动替换部署工具介绍(二)

基于Python3+PyQt5的自动替换部署工具介绍(二)

作者: 迈阿密小白 | 来源:发表于2017-10-23 20:21 被阅读181次

    上一篇文章主要这款工具的环境准备、Qt Designer的基本使用,以及工具界面的生成(备注:今天抽空又把界面优化了一下,增加了日志显示),今天主要介绍后台具体实现逻辑部分。

    主界面.png

    paramiko的基本使用

    之前的一篇文章已经介绍过了paramiko的相关基础操作:
    主要包括:

    1.连接服务器
    def trans_connect(host,username,password):
        try:
            trans = paramiko.Transport((host,22))
            trans.connect(username=username,password=password)
        except Exception,e:
            print e
        return trans
    
    def ssh_connect(host,username,password):
        try:
            ssh_client = paramiko.SSHClient()
            ssh_client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
            ssh_client.connect(host,22,username,password)
        except Exception ,e:
            print e
        return ssh_client
    
    2.一些基本命令的实现
        cmd = "cd /home ;ls ;pwd" 
        arg = 'get_pty=True'
        ssh_client = ssh_connect(host, username, password)
        stdin, stdout, stderr = ssh_exec_cmd(ssh_client, cmd, arg)
        for line in stdout:
            print line.strip('\n')
        time.sleep(2)
        ssh_close(ssh_client)
    
    3.上传下载文件至服务器
    def trans_web(trans,remotepath,localpath):
        sftp = paramiko.SFTPClient.from_transport(trans)
        sftp.put(localpath,remotepath)
        trans.close()
    

    下载文件,只需切换localpath和remotepath的位置即可

    内部逻辑实现

    上一篇说到了界面与逻辑分离的规则,所有的界面实现都集中在FirstMainWin.py 这个文件中实现,而所有后台逻辑实现都集中在Change.py中实现,保证页面发生改变时不需要大概后台逻辑,同样改动后台逻辑时也不会对前台页面造成影响。
    首先缕一下在xshell中手动部署替换文件的顺序:
    1.连接服务器
    2.cd 到文件路径,备份该文件
    3.上传文件
    4.重启服务器(非必要操作)
    同样的顺序,在用这个工具的时候,也是按照连接服务--备份--上传文件--重启的顺序;
    即:在点开始部署这个pushbutton时要激活上述所有步骤:
    首先定义这些基础的步骤

    连接服务

    先介绍一下QComboBox(下拉列表框)的一些常用方法和常用信号:

    (1)addItems

    在QComboBox的添加一个下拉选项。

    (2)count

    返回列表项总数。

    (3)currentIndex

    当前显示的列表项序号。

    (4)currentText

    返回当前显示的文本。

    常用信号:
    (1)Activated

    当用户选中一个下拉框时发射该信号

    (2)currentIndexChanged

    当下拉选项的索引发生变化时发射该信号

    (3)highlighted

    当选中一个已经选中的下拉选项时,发射该信号

    class Main(QMainWindow,Ui_Form):
        def __init__(self,parent=None):
            super(Main,self).__init__(parent)
            self.ui = Ui_Form()
            self.ui.setupUi(self)
            self.setWindowTitle('Waiqin365-DATT-V1.0.0')
            self.password = 'xxxxxxxx'
            self.username='xxxx'
    
            self.ui.environment.activated.connect(self.printtomcat)
            self.ui.filename.editingFinished.connect(self.filename)
            self.ui.filenumber.activated.connect(self.filenumber)
            self.ui.filetype.activated.connect(self.filetype)
            self.ui.restart.activated.connect(self.needrestart)
            self.ui.start.clicked.connect(self.start)
    
    def printtomcat(self):
            '''
            根据选择的环境返回Index值,供下面connect使用
            1   231
            2   233
            '''
            print(self.ui.environment.currentText())
            tomcat = self.ui.environment.currentIndex()
            self.ui.log.setPlainText('部署的环境为:'+self.ui.environment.currentText())
            return tomcat
    
    def connect(self,Index):
            '''
            根据传入的Index连接服务器
            :param Index: 1  231  2  233
            :return: ssh
            '''
            if Index == 1:
                try:
                    ssh = paramiko.SSHClient()
                    ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
                    ssh.connect('172.31.3.231',22,self.username, self.password)
                except Exception:
                    print (Exception)
                return ssh
            elif Index == 2:
                try:
                    ssh = paramiko.SSHClient()
                    ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
                    ssh.connect('172.31.3.233',22,self.username, self.password)
                except Exception:
                    print (Exception)
                return ssh
    

    替换环境这个下拉框的object name(在设计界面的时候可以自定义)为environment

    self.ui.environment.activated.connect(self.printtomcat)

    当选中一个下拉选项时,激活printtomcat这段代码,并返回选项的Index,供下面连接服务器connect部分使用
    同样的道理:
    文件类型的两个下拉选项,是否重启的两个下拉选项,都是选中一个选项时,激活相应的操作

    返回文件数,是单个文件还是压缩文件

    self.ui.filenumber.activated.connect(self.filenumber)

        def filenumber(self):
            '''
            根据选择的文件数量:单个文件还是web.zip,返回Index供下面onefile和zip使用
            1  单个文件
            2  web.zip
            '''
            print(self.ui.filenumber.currentIndex())
            filenumber = self.ui.filenumber.currentIndex()
            self.ui.log.setPlainText('文件类型为:'+self.ui.filenumber.currentText())
            return filenumber
    
    返回文件类型是平台文件还是应用文件

    self.ui.filetype.activated.connect(self.filetype)

        def filetype(self):
            '''
            根据选择的文件类型:平台文件还是应用文件,自动加上前缀 /home/...web/WEB-INFO/class  还是 /home/...web/
            1  应用文件
            2  平台文件
            '''
            print(self.ui.filetype.currentIndex())
            filetype = self.ui.filetype.currentIndex()
            self.ui.log.setPlainText('文件为:'+self.ui.filetype.currentText())
            return filetype
    
    是否需要重启

    self.ui.restart.activated.connect(self.needrestart)

        def needrestart(self):
            '''
            根据所选,返回Index,供下面使用是否需要重启
            1 需要
            2 不需要
            '''
            need = self.ui.restart.currentIndex()
            print(need)
            return need
    

    备份替换文件并上传文件

    如果文件是单个文件,根据传入的Index判断是平台文件还是应用文件,在返回的文件路径前自动补全地址
    /home/iorder_appsvr/iorder_appsvr/web/WEB-INF/classes 或者 /home/iorder_appsvr/iorder_appsvr/web

    返回文件名
        def filename(self):
            '''
            返回填写的filename
            '''
            print(self.ui.filename.text())
            filename = self.ui.filename.text()
            self.ui.log.setPlainText('文件名为:'+self.ui.filename.text())
            return filename
    
        def localfile(self):
            '''
            返回local,即文件所在路径
            :return:
            '''
            print(self.ui.filename.text())
            filename = self.ui.filename.text()
            localfile = 'D:/file/'+ filename
            print(localfile)
            return localfile
        def path(self):
            '''
            返回填写的filepath 文件路径
            '''
            print(self.ui.filepath.toPlainText())
            filepath = self.ui.filepath.toPlainText()
            return filepath
    

    filename和filepath分别是QLineEdit和QTextEdit

    QLineEdit常用方法:
    (1)clear

    清除文本内容

    (2)setText

    设置文本内容

    (3)Text

    返回文本框内容

    (4)selectAll

    全选

    QTextEdit常用方法:
    (1)setPlainText

    设置多行文本框内容

    (2)toPlainText

    返回多行文本框内容

    其中trans主要用来上传文件,根据Index来连接对应的服务器
    onefile这个方法,主要用来替换单个文件,根据传入的Index,type连接对应的服务器,并根据类型拼接文件地址,然后执行相应的命令,备份文件,上传文件等操作
    zip这个方法,主要用来替换web.zip,只需要一个参数Index,连接对应的服务器即可以,也是类似的操作:上传文件,解压,复制。

    def trans(self,Index,localpath,remotepath):
            '''
            根据传入的Index连接服务器并上传文件
            :param Index:
            :return:
            '''
            if Index == 1:
                try:
                    trans = paramiko.Transport(('172.31.3.231',22))
                    trans.connect(username=self.username,password=self.password)
                except Exception as e:
                    print (e)
    
                sftp = paramiko.SFTPClient.from_transport(trans)
                sftp.put(localpath,remotepath)
                time.sleep(2)
                trans.close()
    
            elif Index == 2 :
                try:
                    trans = paramiko.Transport(('172.31.3.233',22))
                    trans.connect(username=self.username,password=self.password)
                except Exception as e:
                    print (e)
    
                sftp = paramiko.SFTPClient.from_transport(trans)
                sftp.put(localpath,remotepath)
                time.sleep(2)
                trans.close()
    
    
        def onefile(self,Index,type):
            '''
            一个文件,根据传入的type判断平台文件还是应用文件,拼接完整的路径
            :param type: 1 应用文件  2  平台文件
            :param type: Index  1  连接231  2  连接233
            :return:
            '''
            if type == 1:
                remotepath = '/home/iorder_appsvr/iorder_appsvr/web/WEB-INF/classes' + str(self.path())
                self.ui.log.setPlainText('文件路径为:'+ remotepath)
                filename = self.filename()
                self.localfile()
                bak = filename + time.strftime("%y%m%d") + 'bak'
                cmd = 'cd  {0};mv {1}  {2}'.format(remotepath,self.filename(),bak)
                #备份并上传替换文件
                print(cmd)
                self.ui.log.setPlainText('备份替换文件')
                # 根据Index 连接对应的环境
                ssh = self.connect(Index)
                stdin, stdout, stderr = ssh.exec_command(cmd,get_pty=True)
                for line in stdout:
                    print (line.strip('\n'))
                ssh.close()
    
                remotefile = '/home/iorder_appsvr/iorder_appsvr/web/WEB-INF/classes' + str(self.path())+str(self.filename())
                self.trans(Index,self.localfile(),remotefile)
                self.ui.log.setPlainText('文件上传成功')
                return cmd,filename,remotepath
    
            elif type == 2:
                remotepath = '/home/iorder_appsvr/iorder_appsvr/web' + str(self.path())
                self.ui.log.setPlainText('文件路径为:'+ remotepath)
                filename = self.filename()
                bak = filename + time.strftime("%y%m%d") + 'bak'
                cmd = 'cd  {0} ;mv {1}  {2}'.format(remotepath,self.filename(),bak)
                print(cmd)
                self.ui.log.setPlainText('备份替换文件')
    
                ssh = self.connect(Index)
                stdin, stdout, stderr = ssh.exec_command(cmd,get_pty=True)
    
                for line in stdout:
                    print (line.strip('\n'))
                ssh.close()
    
                remotefile = '/home/iorder_appsvr/iorder_appsvr/web' + str(self.path())+str(self.filename())
                self.trans(Index,self.localfile(),remotefile)
                self.ui.log.setPlainText('文件上传成功')
                return cmd,filename,remotepath
    
    
        def zip(self,Index):
            '''
            web.zip包直接复制到opt后解压后复制
            Index  用来判断231 还是 233
            :return:
            '''
            cmd = 'cd /opt;rm -rf web;mkdir web;ls'
            # cd 到opt下创建新的web目录
            ssh = self.connect(Index)
            stdin, stdout, stderr = ssh.exec_command(cmd,get_pty=True)
            for line in stdout:
                    print (line.strip('\n'))
            ssh.close()
    
            #上传文件至opt/web 并解压
            remotepath = '/opt/web/{}'.format(self.filename())
            localpath = r'D:/file/{}'.format(self.filename())
            print(remotepath,localpath)
            self.trans(Index,localpath,remotepath)
            self.ui.log.setPlainText('压缩包文件上传成功')
    
    
            cmd1 = 'cd /opt/web;ls;unzip {};rm -rf  {}'.format(self.filename(),self.filename())
            ssh = self.connect(Index)
            stdin, stdout, stderr = ssh.exec_command(cmd1,get_pty=True)
            for line in stdout:
                    print (line.strip('\n'))
            time.sleep(2)
            #复制解压后的文件
            cmd2 = '\cp -Rf /opt/web/*    /home/iorder_appsvr/iorder_appsvr/'
            stdin, stdout, stderr = ssh.exec_command(cmd2,get_pty=True)
            for line in stdout:
                    print (line.strip('\n'))
            ssh.close()
            self.ui.log.setPlainText('复制zip替换文件成功')
    

    开始部署

    上面一切准备工作就绪后,点击开始部署就Ok了

    开始部署.png

    部署部分的逻辑如下:
    根据上面printtomcat这个方法返回的Index,判断是对哪台服务器进行部署
    根据filenumber这个方法,判断是单个文件还是web.zip,并对应的使用onefile这个方法还是zip这个方法
    根据needrestart这个方法,判断是否需要重启

     def start(self):
            '''
            开始部署  1  部署231   2  部署233
            :return:
            '''
            if self.printtomcat() == 1:
                print('部署环境为:'+ self.ui.environment.currentText())
    
                # 部署单个文件
                if self.filenumber() == 1:
                    self.onefile(1,self.filetype())
    
                #部署web.zip
                elif self.filenumber() == 2:
                    #连接231 部署web.zip
                    self.zip(1)
                    # print('222')
    
                #判断是否需要重启
                if self.needrestart() == 1:
                    ssh = self.connect(1)
                    stdin, stdout, stderr = ssh.exec_command('service  tomcat_iorder_appsvr  restart',get_pty=False)
                    for line in stdout:
                        print (line.strip('\n'))
                    time.sleep(5)
                    ssh.close()
                elif self.needrestart() == 2:
                    time.sleep(2)
    
            elif self.printtomcat() == 2:
                print('部署环境为:'+ self.ui.environment.currentText())
    
                # 部署单个文件
                if self.filenumber() == 1:
                    self.onefile(2,self.filetype())
    
                # 部署web.zip
                elif self.filenumber()== 2:
                    #连接231 部署web.zip
                    # print('222 222')
                    self.zip(2)
    
                # 判断是否需要重启
                if self.needrestart() == 1:
                    ssh = self.connect(2)
                    stdin, stdout, stderr = ssh.exec_command('service  tomcat_iorder_appsvr  restart',get_pty=False)
                    for line in stdout:
                        print (line.strip('\n'))
                    time.sleep(2)
                    ssh.close()
                    self.ui.log.setPlainText('部署完成,服务器重启中,请稍候')
                elif self.needrestart() == 2:
                    time.sleep(2)
                    self.ui.log.setPlainText('部署完成')
    

    上面就是后台逻辑实现部分。
    既然这是一个窗口工具,那么如何在其他电脑上运行呢,只需要一步;

    打包成exe文件

    打包生成EXE文件只需要两个步骤

    1.安装PyInstaller
    pip install PyInstaller
    

    安装成功后,可以在python/Scripts下找到

    PyInstaller.png
    2.打包
    pyinstaller [opts] xxxxx.py
    

    可选参数有

    • -F,-onefile 打包成一个EXE文件
    • -D,-onedir,创建一个目录,包含EXE文件,但会依赖很多文件
    • -c,-console,-nowindowed,使用控制台,五窗口
    • -w,-windowed,noconsole,使用窗口,无控制台
      打开命令行,进入到需要打包的.py文件目录,运行下面的命令:
    pyinstaller -F -w  xxxxx.py
    

    完成后会在同目录的dist文件夹中生成一个同名的.exe文件,双击这个exe文件,运行效果和直接使用编辑器运行脚本效果一样

    目录.png exe.png

    在其他电脑上使用时,直接将dist目录拷贝过去即可,我这边加了platforms是因为在其他电脑运行报错,paltforms文件夹来自于

    C:\Python34\Lib\site-packages\PyQt5\plugins\platforms

    相关文章

      网友评论

        本文标题:基于Python3+PyQt5的自动替换部署工具介绍(二)

        本文链接:https://www.haomeiwen.com/subject/gioauxtx.html