远程重启zimbra服务,python之sshtunnel+socket+thread编程

一、概要

由于偶尔需要重启zimbra服务,出现故障时运维人员可能不在电脑旁,故想要如何来使用手机便捷得去重启服务器上的服务。服务器出于安全考虑,采用跳板机的方式ssh登录,再进行重启服务操作

二、难点

zimbra服务器这里称为A,跳板机称为B,运维人员终端称为C

  • A只允许B进行ssh连接,也就是如果C要操作A,只能先连接上B,再从B使用ssh连接A,进行对A的操作
  • 如何远程操作A服务器
  • socket服务端启用端口监听后,由于硬件防火墙限制,监听端口外网无法访问
  • socket多用户连接导致阻塞
  • 多用户在操作的时候,如果同一条命令没有执行完,只允许一个用户正常执行

三、python sshtunnel 模块

为了解决跳板机的问题,以及监听端口外网无法访问的问题,这里我想到了使用ssh的隧道来进行连接(题外话,ssh隧道很强大,有兴趣的同学可以去谷歌学习了解下),那么python如何实现ssh隧道呢?这里就用到了sshtunnel,附上官方使用说明:https://github.com/pahaz/sshtunnel,然后下面先把代码贴出来:

#!/usr/bin/env python
# coding:utf-8
# Build by Zengfl

from sshtunnel import SSHTunnelForwarder

def connserver(host, port):
    server = SSHTunnelForwarder(
        ssh_address=('serverIP',serverport),
        ssh_username='sshname',
        ssh_password='sshpasswd',
        local_bind_address=('127.0.0.1',55005),
        remote_bind_address=('172.25.4.21',55005)
    )                                    #创建ssh隧道
    try:
        server.start()                   #ssh隧道连接
    except:
        print "[SSH CONNECT ERROR]"
        exit(1)
  • ssh_address: 我这里写的跳板机的IP地址和端口
  • ssh_username:连接跳板机的ssh用户名
  • ssh_password: 连接跳板机的ssh密码
  • local_bind_address:ssh连上跳板机后绑定本地的哪个ip和端口,我这里绑定的是我本机的55005端口(端口数字随便设,只要不和本机已用端口冲突即可)
  • remote_bind_address:将上条绑定的端口和远程服务器的端口进行绑定映射,这里我绑定的就是zimbra服务器的55005端口

以上代码便创建了ssh隧道,并将远端zimbra服务器上的55005端口映射到我本机的55005端口,之后我访问本机的55005端口即是访问zimbra服务器的55005端口,完美绕过跳板机和防火墙的限制(思考:如果是mysql服务器如何来进行隧道连接)

四、python socket网络编程

Ⅰ.方案选择

在解决了远程连接到跳板机后端的服务器之后,就要思考如何来对后端的服务器进行操作,当时我想到了两个方案:

  • Web页来进行操作
  • 通过socket来进行操作

由于考虑到使用web来操作的话,有点重,并且开发周期会稍长一点(可能是我比较菜,大佬应该还是挺快的),所以我这里选择了socket来进行操作,做个轻量级的远程控制

Ⅱ.socket编程思路

TCP服务端: (UDP这里不做介绍,由于本文主要概述一下 Python Socket 来解决服务器上服务的操作,因此不会对相关函数参数、返回值进行详细介绍,需要了解的可以查看相关手册)

  1. 创建套接字,绑定套接字到本地IP与端口
s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
s.bind()
  1. 开始监听连接
s.listen()
  1. 进入循环,不断接受客户端的连接请求,并将连接的请求赋值给变量
s.accept()
  1. 接收/发送数据
s.recv()
s.sendall()
  1. 传输完毕后,关闭套接字
s.close()

TCP客户端:

  1. 打开 socket
socket.socket(socket.AF_INET,socket.SOCK_STREAM)
s.connect()
  1. 连接后发送数据和接收数据
s.sendall()
s.recv()
  1. 传输完毕后,关闭套接字
s.close()

Ⅲ. 创建socket客户端和服务端连接

按照以上思路如下编写
服务端:

#!/usr/bin/env python
# coding:utf-8
# Build by Zengfl

import socket
import sys
import time
 
# 创建socket
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
print 'Socket created'
 
# 绑定socket的ip和端口
try:
    s.bind(("", 55005))
except socket.error , msg:
    print 'Bind failed. Error Code : ' + str(msg[0]) + ' Message ' + msg[1]
    sys.exit()

# 监听端口(用来控制连接的个数。如果设为 5,那么有 5 个连接正在等待处理,此时第 6 个请求过来时将会被拒绝)
s.listen(5)
print "[+] Server is running on port: 55005 at %s" % (time.strftime("%Y%m%d %H:%M:%S", time.localtime()))

# 接收连接
mainsocket, mainhost = s.accept()
print "[+] Connect success -> %s at %s" % (str(mainhost), time.strftime("%Y%m%d %H:%M:%S", time.localtime()))

# 接收消息
data = mainsocket.recv(1024)
if data:
    print "[+] Receive:%s" % data
# 发送给客户端消息接收成功的信息
    mainsocket.sendall("[Server]success")
    mainsocket.close()
s.close()

客户端:

#!/usr/bin/env python
# coding:utf-8
# Build by Zengfl

import time
import socket

# 创建socket
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((host, 55005))   #host配置服务端的ip
# 终端输入并发送到服务端
print "\n[*] Please input command:"
data = raw_input()
s.sendall(data)
# 接收服务端的返回结果
recvdata = s.recv(1024)
print "[+] Send %s:55005 -> %s" % (host, data)
if recvdata:
    print "[+] Receive :%s" % recvdata

运行结果:
服务端:

[root@iZwz9esy3n96ermeee5jzkZ ~]# python server.py 
Socket created
[+] Server is running on port 55005: at 20171228 13:33:50
[+] Connect success -> ('183.15.179.197', 52653) at 20171228 13:34:23
[+] Receive:hello world

客户端:

C:\Users\Zengfl\Desktop>python client.py

[*] Please input command:
hello world
[+] Send xxx.xxx.xx.xxx:55005 -> hello world
[+] Receive :[Server]success

总结:
如上则完成了一次简单的会话通讯,但是还是没有达到我所期望的目的。我们期望的是:

  • 服务端能够无限制接收客户端过来的消息,并不是完成一次会话后就断开连接
  • 服务端接收到客户端的固定指令(exit,quit,close)才断开连接
  • 客户端也要同样的无限制得可以向服务端发送消息
  • 客户端发送固定执行(exit,quit,close)自己中断连接

Ⅳ. 优化(1)

针对上方总结出的需求,对代码进行优化,需要将服务端一直接受请求,最简单的办法就是将服务端接收消息放到一个循环中无限执行,这样就能一直接收,客户端同理
至于如何中断连接,对数接收到的数据进行判断,如果为exit,quit,close,则关闭连接,并且推出循环。
优化后的代码如下:
服务端(这里使用函数来封装,便于使用)

#!/usr/bin/env python
# coding:utf-8
# Build by Zengfl

import socket
import sys
import time
import threading

def recvdata(port):
    close_list=["close session","exit","quit","close"]    # 断开连接指令列表
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    s.bind(('', port))
    s.listen(5)
    print "[+] Server is running on port:%s at %s" % (str(port), time.strftime("%Y%m%d %H:%M:%S", time.localtime()))
    mainsocket, mainhost = s.accept()
    print "[+] Connect success -> %s at %s" % (str(mainhost), time.strftime("%Y%m%d %H:%M:%S", time.localtime()))
    if mainhost:
        # 增加收发消息的循环
        while True:      
            data = mainsocket.recv(1024)
            if data:
                print "[+] Receive:%s" % data
                mainsocket.sendall("[Server]success")
            #判断是否是断开连接的指令
            if data in close_list:
                mainsocket.close()
                print "[+] Quit success"
                break

if __name__ == "__main__":
    connPort = 55005
    onethreads = threading.Thread(target=recvdata, args=(connPort,))
    onethreads.start()

客户端

#!/usr/bin/env python
# coding:utf-8
# Build by Zengfl

import time
import socket

def connserver(host, port):
    close_list=["close session","exit","quit","close"]                     #监听输入状态,如果为这些状态则断开连接
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s.connect((host, port))

    while True:
        print "\n[*] Please input command:"
        data = raw_input()
        if not data:
            continue
        s.sendall(data)
        recvdata = s.recv(1024)
        time.sleep(0)
        if recvdata:
            print "[+] Receive :%s" % recvdata         
        if data in close_list:
            print "[+] Close sucess"
            s.close()
            break

if __name__ == "__main__":
    server_list = ["xxx.xxx.xxx.xxx"]
    connPort = 55005

    if server_list != []:
        for host in server_list:
            connserver(host, connPort)

运行结果:
服务端:

[root@iZwz9esy3n96ermeee5jzkZ ~]# python server.py 
[+] Server is running on port:55005 at 20171228 14:55:10
[+] Connect success -> ('183.15.179.197', 53661) at 20171228 14:55:54
[+] Receive:hello world
[+] Receive:my name is kard
[+] Receive:bye bye
[+] Receive:exit
[+] Quit success
[root@iZwz9esy3n96ermeee5jzkZ ~]#

客户端:

C:\Users\Zengfl\Desktop>python client.py

[*] Please input command:
hello world
[+] Receive :[Server]success

[*] Please input command:
my name is kard
[+] Receive :[Server]success

[*] Please input command:
bye bye
[+] Receive :[Server]success

[*] Please input command:
exit
[+] Receive :[Server]success
[+] Close sucess

总结:
从上述代码来看,我们已经解决了之前遇到的问题,实现了客户端可以多次发送消息,但是又有新的问题:

  • 服务端在收到客户端退出请求以后,服务端也退出了监听,我想要的是服务端一直处于监听状态,只要有客户端连接进来,就能够正常收发消息

Ⅴ. 优化(2)

针对此问题,我们只需要修改服务端,将监听会话连接也加个循环即可
服务端

#!/usr/bin/env python
# coding:utf-8
# Build by Zengfl

import socket
import sys
import time
import threading

def recvdata(port):
    close_list=["close session","exit","quit","close"]    # 断开连接指令列表
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    s.bind(('', port))
    s.listen(5)
    print "[+] Server is running on port:%s at %s" % (str(port), time.strftime("%Y%m%d %H:%M:%S", time.localtime()))
    # 增加连接建立的循环
    while True:
        mainsocket, mainhost = s.accept()
        print "[+] Connect success -> %s at %s" % (str(mainhost), time.strftime("%Y%m%d %H:%M:%S", time.localtime()))
        if mainhost:
            # 增加收发消息的循环
            while True:      
                data = mainsocket.recv(1024)
                if data:
                    print "[+] Receive:%s" % data
                    mainsocket.sendall("[Server]success")
                #判断是否是断开连接的指令
                if data in close_list:
                    mainsocket.close()
                    print "[+] Quit success"
                    break

if __name__ == "__main__":
    connPort = 55005
    onethreads = threading.Thread(target=recvdata, args=(connPort,))
    onethreads.start()

运行结果:
服务端

[root@iZwz9esy3n96ermeee5jzkZ ~]# python server.py 
[+] Server is running on port:55005 at 20171228 16:00:28
[+] Connect success -> ('183.15.179.197', 54658) at 20171228 16:01:11
[+] Receive:hello wordl
[+] Receive:ni hao
[+] Receive:bye bye
[+] Receive:exit
[+] Quit success
[+] Connect success -> ('183.15.179.197', 54663) at 20171228 16:01:34
[+] Receive:nihao 
[+] Receive:bye
[+] Receive:close
[+] Quit success

客户端

C:\Users\Zengfl\Desktop>python client.py

[*] Please input command:
hello wordl
[+] Receive :[Server]success

[*] Please input command:
ni hao
[+] Receive :[Server]success

[*] Please input command:
bye bye
[+] Receive :[Server]success

[*] Please input command:
exit
[+] Receive :[Server]success
[+] Close sucess
----------------------------------------------------------------------------------------------------------------
C:\Users\Zengfl\Desktop>python client.py

[*] Please input command:
nihao
[+] Receive :[Server]success

[*] Please input command:
bye
[+] Receive :[Server]success

[*] Please input command:
close
[+] Receive :[Server]success
[+] Close sucess

总结:
以上方式修改了之后,服务端能够一直监听客户端,并响应消息,客户端退出重连都可以正常使用。but!新的问题和需求来了:

  • 当一个客户端连接上了之后,socket处于消息阻塞,故新的客户端连接上去,服务端无法正常响应,并且新客户端会处于卡死状态(具体原因是什么我还没有去研究,忘大佬解答)
  • 当客户端输入Ctrl+c 退出后,服务端没有接收到退出信息,故没有中断客户端连接,导致服务端连接被占用
  • 并且我们需要判断客户端的消息,然后进行服务器上的操作
  • 已经在操作系统命令,不允许重复执行

Ⅵ. 优化(3)

对于消息阻塞,我这里想到了用多线程来解决此问题
对于客户端输入Ctrl+c 我采用监控KeyboardInterrupt异常来进行判断,如果出现此异常则向服务端发送中断消息
然后客户端消息判断,则在服务端加一个判断的函数,监听cmd.run开头的信息,如果客户端以cmd.run来发送消息,服务端则执行cmd.run 后面的语句。
已经在执行的命令,通过ps在服务上查看进程,判断命令是否正在执行
代码如下:
服务端 (注释解释得挺详细了)

#!/usr/bin/env python
# coding:utf-8
# By Zengfl

import time
import socket
import threading
import traceback
import subprocess
from thread import *

# 处理收到消息的方法,判断消息是否为cmd.run 开头
def parsecmd(strings):
    midsplit = str(strings).split(" ")
    # 判断消息是否大于2个传参并且为cmd.run开头
    if len(midsplit) >= 2 and midsplit[0] == "cmd.run":
        # 统计传入的命令在服务器上是否有进程在运行,再进行判断
        res=subprocess.Popen("ps -ef|grep \""+strings[8:]+"\"|grep -v grep|wc -l",stdout=subprocess.PIPE,shell=True)
        pronum=int(res.stdout.read())
        if pronum < 1:
            try:
                # 正常执行消息传进来的参数
                command = subprocess.Popen(strings[8:], shell=True)
                command.communicate()
                print "传参:%s,执行成功...请稍等"%strings
            except Exception, e:
                # 传参执行失败
                print "传参:%s,执行失败!!!"%strings
                print e.message
                traceback.print_exc()
        else:
            # 如果消息中传的参数,服务器上有进行在运行,则不执行
            print "传参:%s,正在执行。"%strings
    else:
        # 如果消息不以cmd.run 开头并且少于2个传参,则不执行
        print "收到传参:%s,不予执行!"%strings

# 客户端消息收发的方法
def clientthread(mainsocket):
    close_list=["close session","exit","quit","close"]
    while True:
        data = mainsocket.recv(1024)
        if data:
            mainsocket.sendall("[Server] success")
            # 将收到的消息,传到消息处理的方法中,进行消息处理
            parsecmd(data)
        if data in close_list:
            print "[+] Client quit success"
            break
    mainsocket.close()

# 创建socket,并建立连接的方法
def recvdata(port):
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    # 这条是我在网上看到粘包的问题,加上的
    s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) 
    s.bind(('', port))
    s.listen(5)
    print "[-] Server is running on port:%s" % str(port)
    while True:
        mainsocket, mainhost = s.accept()
        print "[+] Connect success -> %s" % str(mainhost)
        # 判断连接是否成功,如果连接成功,将此连接的会话创建到一个线程中
        if mainhost:
            # 创建线程,线程中调用客户端收发消息的方法
            start_new_thread(clientthread ,(mainsocket,))

if __name__ == "__main__":
    connPort = 55005
    onethreads = threading.Thread(target=recvdata, args=(connPort,))
    onethreads.start()

客户端

#!/usr/bin/env python
# coding:utf-8
# Build by Zengfl
#

import time
import socket

def connserver(host, port):
    close_list=["close session","exit","quit","close"]                     # 监听输入状态,如果为这些状态则断开连接
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s.connect((host, port))

    while True:
        try:
            print "\n[*] Please input command:"
            data = raw_input()
            if not data:
                continue
            s.sendall(data)
            recvdata = s.recv(1024)
            time.sleep(0)
            if recvdata:
                print "[+] Receive :%s" % recvdata
            if data in close_list:
                print "[+] Close sucess"
                s.close()
                break
        except KeyboardInterrupt:                        #监听键盘输出Ctrl + c,并发送中断消息以及中断连接
            s.sendall("close session")
            recvdata = s.recv(1024)
            if recvdata:
                print "[+] Close sucess"
            s.close()
            break

if __name__ == "__main__":
    server_list = ["xxx.xxx.xxx.xxx"]
    connPort = 55005

    if server_list != []:
        for host in server_list:
            connserver(host, connPort)

运行结果:
客户端1

C:\Users\Zengfl\Desktop>python test.py

[*] Please input command:
hello
[+] Receive :[Server] success

[*] Please input command:
world
[+] Receive :[Server] success

[*] Please input command:
wo shi 1
[+] Receive :[Server] success

[*] Please input command:
wo zai xian
[+] Receive :[Server] success

[*] Please input command:
bye bye
[+] Receive :[Server] success

[*] Please input command:
exit
[+] Receive :[Server] success
[+] Close sucess

C:\Users\Zengfl\Desktop>

客户端2

C:\Users\Zengfl\Desktop>python test.py

[*] Please input command:
hello
[+] Receive :[Server] success

[*] Please input command:
wo shi 2
[+] Receive :[Server] success

[*] Please input command:
 1 zai xian ma
[+] Receive :[Server] success

[*] Please input command:
1 zou le
[+] Receive :[Server] success

[*] Please input command:
wo ye zou le
[+] Receive :[Server] success

[*] Please input command:
exit
[+] Receive :[Server] success
[+] Close sucess

C:\Users\Zengfl\Desktop>

客户端3

C:\Users\Zengfl\Desktop>python test.py

[*] Please input command:
cmd.run echo 111 > /tmp/hello
[+] Receive :[Server] success

[*] Please input command:
byebye
[+] Receive :[Server] success

[*] Please input command:
close
[+] Receive :[Server] success

[*] Please input command:
close session
[+] Receive :[Server] success
[+] Close sucess

C:\Users\Zengfl\Desktop>

服务端

[root@iZwz9esy3n96ermeee5jzkZ ~]# python server.py 
[-] Server is running on port:55005
[+] Connect success -> ('183.15.179.197', 55884)
收到传参:hello,不予执行!
收到传参:world,不予执行!
收到传参:wo shi 1,不予执行!
[+] Connect success -> ('183.15.179.197', 55890)
收到传参:hello,不予执行!
收到传参:wo shi 2,不予执行!
收到传参: 1 zai xian ma,不予执行!
收到传参:wo zai xian,不予执行!
收到传参:bye bye,不予执行!
收到传参:exit,不予执行!
[+] Client quit success
收到传参:1 zou le,不予执行!
收到传参:wo ye zou le ,不予执行!
收到传参:exit,不予执行!
[+] Client quit success
[+] Connect success -> ('183.15.179.197', 57579)
传参:cmd.run echo 111 > /tmp/hello,执行成功...请稍等
收到传参:byebye,不予执行!
收到传参:close ,不予执行!
收到传参:close session,不予执行!
[+] Client quit success

在服务端查看 /tmp/hello 文件:

[root@iZwz9esy3n96ermeee5jzkZ ~]# cat /tmp/hello 
111
[root@iZwz9esy3n96ermeee5jzkZ ~]#

总结:
综上,所有的需求都完成了。解决了下面几个功能:

  1. 服务端能够无限制接收客户端过来的消息,并且支持多个终端同时连接,同时进行会话
  2. 服务端接收到客户端的固定指令(exit,quit,close)断开连接
  3. 服务端可执行客户端发送过来的执行命令
  4. 服务端不允许执行正在运行的命令
  5. 客户端也要同样的无限制得可以向服务端发送消息
  6. 客户端发送固定执行(exit,quit,close)自己中断连接,客户端执行Ctrl+c也能中断连接
  7. s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) 这个解决粘包问题:参考文档

五、结合ssh隧道,实现跨跳板机进行服务端会话

跳板机的问题解决了
服务端远程执行远程指令也解决了
现在就使用这两个功能实现我们最初的目的,并将之前print的输出记录到日志,直接贴代码吧:
服务端

#!/usr/bin/env python
# coding:utf-8
# By Zengfl

import time,os,logging
import socket
import threading
import traceback
import subprocess
from thread import *

#日期模块
def unix_te(day=0):
    return time.strftime(
        '%Y-%m-%d',
        time.localtime(time.time()-86400*day)
    )

#定义日志配置
if not os.path.exists('/home/work/script/logs'): os.system('mkdir -p /home/work/script/logs')
logging.basicConfig(filename = '/home/work/script/logs/%s.log' % unix_te(), level = logging.DEBUG, filemode = 'a', format = '%(asctime)s - %(levelname)s: %(message)s')


def parsecmd(strings):
    midsplit = str(strings).split(" ")
    if len(midsplit) >= 2 and midsplit[0] == "cmd.run":
        res=subprocess.Popen("ps -ef|grep \""+strings[8:]+"\"|grep -v grep|wc -l",stdout=subprocess.PIPE,shell=True)
        pronum=int(res.stdout.read())
        if pronum < 1:
            try:
                command = subprocess.Popen(strings[8:], shell=True)
                command.communicate()
                logging.info("传参:%s,执行成功...请稍等"%strings)
            except Exception, e:
                logging.error("传参:%s,执行失败!!!"%strings)
                print e.message
                traceback.print_exc()
        else:
            logging.warning("传参:%s,正在执行。"%strings)
    else:
        logging.info("收到传参:%s,不予执行!"%strings)

def clientthread(mainsocket):
    close_list=["close session","exit","quit","close"]
    while True:
        data = mainsocket.recv(1024)
        if data:
            mainsocket.sendall("[Server] success")
            parsecmd(data)
        if data in close_list:
            #mainsocket.close()
            logging.info("[+] Client quit success")
            break 
    mainsocket.close()

def recvdata(port):
    #close_list=["close session","exit","quit","close"]
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    s.bind(('', port))
    s.listen(5)
    logging.info("[-] Server is running on port:%s" % str(port))
    while True:
        mainsocket, mainhost = s.accept()
        logging.info("[+] Connect success -> %s" % str(mainhost))
        if mainhost:
            start_new_thread(clientthread ,(mainsocket,)) 

if __name__ == "__main__":
    connPort = 55005 
    onethreads = threading.Thread(target=recvdata, args=(connPort,))
    onethreads.start()

客户端

#!/usr/bin/env python
# coding:utf-8
# Build by Zengfl

import time
import socket
from sshtunnel import SSHTunnelForwarder

def connserver(host, port):
    server = SSHTunnelForwarder(
        ssh_address=('xx.xx.xx.xx',xxx),    
        ssh_username='xxx',
        ssh_password='xxxx',
        local_bind_address=('127.0.0.1',55005),
        remote_bind_address=('192.168.1.21',55005)
    )                                    #创建ssh隧道
    try:
        server.start()                   #ssh隧道连接
    except:
        print "[SSH CONNECT ERROR]"
        exit(1)

    close_list=["close session","exit","quit","close"]                     #监听输入状态,如果为这些状态则断开连接
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s.connect((host, port))

    while True:
        try:
            print "\n[*] Please input command:"
            data = raw_input()
            if not data:
                continue
            s.sendall(data)
            recvdata = s.recv(1024)
            time.sleep(0)
            if recvdata:
                print "[+] Receive :%s" % recvdata
            if data in close_list:
                print "[+] Close sucess"
                s.close()
                break
        except KeyboardInterrupt:                        #监听键盘输出Ctrl + c,并中断连接
            s.sendall("close session")
            recvdata = s.recv(1024)
            if recvdata:
                print "[+] Close sucess"
            s.close()
            break
    server.stop()                                        #关闭ssh隧道


if __name__ == "__main__":
    server_list = ["127.0.0.1"]    # 这里很重要,因为我ssh做隧道的时候监听的就是本机的55005端口
    connPort = 55005

    if server_list != []:
        for host in server_list:
            connserver(host, connPort)

重启zimbra脚本

#!/usr/bin/env python
# coding:utf-8
# Build by Zengfl

import time
import socket
from sshtunnel import SSHTunnelForwarder

def connserver(host, port):
    server = SSHTunnelForwarder(
        ssh_address=('xxx.xxx.xxx.xxx',xxx),
        ssh_username='xxx',
        ssh_password='xxx',
        local_bind_address=('127.0.0.1',55005),
        remote_bind_address=('192.168.1.21',55005)
    )                                    #创建ssh隧道
    try:
        server.start()                   #ssh隧道连接
    except:
        print "[SSH CONNECT ERROR]"
        exit(1)

    close_list=["close session","exit","quit","close"]                     #监听输入状态,如果为这些状态则断开连接
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s.connect((host, port))

    data = "cmd.run /etc/init.d/zimbra restart"
    s.sendall(data)
    recvdata = s.recv(1024)
    if recvdata:
        print "[+] Receive :%s" % recvdata
        print u"操作成功,请等待5分钟后检测邮件收发是否正常"
    else:
        print "操作失败"
    s.sendall("close session")
    recvdata = s.recv(1024)
    s.close()
    server.stop()                                       #关闭ssh隧道

if __name__ == "__main__":
    server_list = ["127.0.0.1"]
    connPort = 55005

    if server_list != []:
        for host in server_list:
            connserver(host, connPort)

测试这里就不演示了,我这里已经在正常使用了,同学们可以参考此代码自行进行尝试

六、总结:

目前为止,已经解决了我所期望的需求,最后再将程序打个exe包就行了。具体如何打包,使用pyinstaller这个工具,怎么使用不是本文的重点

代码有写得不好的地方忘大佬们多多包涵并指出缺点,定改正

本文主要讲述的是一个解决问题的思路,一步步将我解决问题的流程和思路捋了出来,说实话写这篇文章比我完成这份代码花的时间长得多。
当碰到需求不要急于去动手做,先想好这个需求需要面临些什么问题,然后再化整为零,把需求细分成小的功能点,逐一解决,最后将各个功能点结合起来,然后大功告成,撒花

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 157,198评论 4 359
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 66,663评论 1 290
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 106,985评论 0 237
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,673评论 0 202
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 51,994评论 3 285
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,399评论 1 211
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,717评论 2 310
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,407评论 0 194
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,112评论 1 239
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,371评论 2 241
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,891评论 1 256
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,255评论 2 250
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,881评论 3 233
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,010评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,764评论 0 192
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,412评论 2 269
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,299评论 2 260

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,087评论 18 139
  • 最近在学习Python看了一篇文章写得不错,是在脚本之家里的,原文如下,很有帮助: 一、网络知识的一些介绍 soc...
    qtruip阅读 2,605评论 0 6
  • 7点30起床 8点洗漱完毕 8点30之前把桌子收拾完毕 9点之前把知米单词背了 敷面膜休息一会儿 9点30开始 整...
    你很完美阅读 169评论 0 0
  • 做妈妈后,我没有遭遇太多的问题,不会抑郁、不会脱发、不会畏寒,除了因为母乳无法减肥之外,唯一困扰我的就是失眠了。 ...
    不太笨的懒女人阅读 185评论 0 0
  • 活到大学了,我读过了很多的书,也确实忘记了很多。 就像孩子时吃过的食物,虽然绝大多数已经忘记了味道。但不可否认的是...
    一碗汤的距离阅读 764评论 0 0