python 远程管理

       基于pywinrm 无法实时的反馈执行中的命令(特殊是服务器的启动等),因此就重新开发一个基于socket的tcp server -client 远程命令管理工具,该工具多线程client 连接,一些cmd, telnet 等阻塞需要输入命令采用SKIP操作(配置man_order, server.py _dosomething 函数中添加elif 处理代码,最后_setpoll0()告诉client命令结束标识),集成ftp的服务器的功能,在客户端的输入的ftp命令即可启动ftp-server,使用ftp-client进行登录等操作即可。

ftp 源码: 由 https://github.com/henryyuan/MYFTP   blog:https://segmentfault.com/a/1190000014744919

问题: subprocees.Pope readline 当内容过多的时候, 会出现阻塞。

process = subprocess.Popen(cmd, shell=True,
                           stdin=subprocess.PIPE,
                           stdout=subprocess.PIPE,
                           stderr=subprocess.STDOUT)
self._processs.append(process)
line = process.stderr.readline()

  问题解释:https://www.520mwx.com/view/46308

临时的解决方式:

data, err = process.communicate()   等待suboprocess exit, 但是一直在运行执行中的命令在无法立即返回。临时方案:将命令提出order.py添加 LOB_order = [] 然后分开进行处理.  源码见临时改进版: python 远程管理临时改进版

代码如下:

Order.py

#!/usr/ftpbin/env python
# -*- coding: utf-8 -*-
#    @version : 0.0.1
#    @File    : Order.py
#    @Time    : 2019/7/12 15:40
#    @Site    : 
#    @Software: PyCharm
#    @Author  : KANGXINWEN
#    @Author_email: singbogo@163.com
#    @description: 

man_order = ["man", "shutdown", "Quit",
             "quit", "QUIT", "Q", "EXIT",
             "BYE", "BYEBYE", "CMD", "cmd", "Cmd", "FTP", "ftp", "Ftp", "FTp"]

MAXBUFFER = 1024


def set_manorder(order):
    if order:
        man_order.append(order)

 

server.py

#!/usr/ftpbin/env python
# -*- coding: utf-8 -*-
#    @version : 0.0.1
#    @File    : server.py
#    @Time    : 2019/7/11 15:35
#    @Site    : 
#    @Software: PyCharm
#    @Author  : KANGXINWEN
#    @Author_email: singbogo@163.com
#    @description:  通过socket远程调用执行命令并返回


import socket
import os
import signal
import subprocess
from threading import Timer
import exception
import threading
import struct
import time

from Order import man_order, MAXBUFFER
from util.System.network import IP
from util.RemoteManagement.MYFTP.ftpbin.myftps import ftp_server_run


class AutoMainPlantSocket(object):

    def __init__(self, ip=None):
        self._socket_obj = None
        self._code = "utf8"
        self._address_port = ('127.0.0.1', 9999)
        self._backlog = 5  # max connecting count
        self._conn = None
        self._process = None
        self._timeout = 10  # time out
        self._is_timeout = False
        self._timer = None
        self._MAXBUFFER = 1024
        self._threads = []
        self._connaddress = []    # <class 'socket.socket'>
        self._processs = []

        self._init(ip)

    def set_timeout(self, timeout):
        self._timeout = timeout

    def _timeout_callback(self, conn, p):
        self._is_timeout = True
        print("exe time out call back")
        self._send(conn, str("poll:%s" % '0'))

        try:
            os.killpg(p.pid, signal.SIGKILL)
        except Exception as error:
            print("Exception _timeout_callback" % str(error))

    def set_addressport(self, address, port):
        self._address_port = (address, port)

    def set_localIPPort(self, ip, port=9999):
        if ip:
            self.set_addressport(ip, port)
        else:
            self._address_port = (IP.get_host_ip(), port)

    def set_backlog(self, backlog):
        self._backlog = backlog

    def _start_timer(self, conn):
        self._timer = Timer(self._timeout, self._timeout_callback, [conn, self._process])
        return self._timer.start()

    @staticmethod
    def _cmdsplit(msg, maxsplit=1):
        if type(msg) is 'str':
            msg = bytes(msg)
        if msg:
            # TypeError: must be str, not bytes
            if msg.find(str(" ")) != -1:
                return msg.split(maxsplit=maxsplit)
            else:
                return msg, "notFound"
        else:
            return None

    def _init(self, ip="127.0.0.1", port=9999):
        """
        socket init
        :return:
        """
        # init program
        self.set_localIPPort(ip, port)

        # init socket
        self._socket_obj = socket.socket()
        self._socket_obj.bind(self._address_port)
        print("sevser blind %s port %d successful......" % (self._address_port[0], self._address_port[1]))
        self._socket_obj.listen(self._backlog)
        print('server listening......')

    def _accept(self):
        conn, address = self._socket_obj.accept()
        print("Accept new connection from %s....." % str(address))
        self._send(conn, b"Welcome!\n")
        return conn, address

    def _recv(self, conn, buffersize=MAXBUFFER):
        # recv data
        # when client close connect recv  exception ConnectionResetError
        data = str()
        try:
            data = conn.recv(buffersize)
        except ConnectionResetError:
            print("%s is be disconnected" % str(conn))
            if conn in self._connaddress:
                self._connaddress.remove(conn)
            # client connecting  be closed by client, server need shutdown recv
            data = "quit"
        except ConnectionAbortedError:
            print("%s is be disconnected" % str(conn))
            if conn in self._connaddress:
                self._connaddress.remove(conn)
            # client connecting  be closed by client, server need shutdown recv
            data = "quit"
        except OSError as e:
            print("%s is be disconnected" % str(conn))
            if conn in self._connaddress:
                self._connaddress.remove(conn)
            data = "quit"
        finally:
            return data

    @staticmethod
    def __bytes2str(bytes_data):
        if bytes_data:
            str_data = str(bytes_data, 'utf-8')
            print(">>>%s" % str_data)
            return str_data

    @staticmethod
    def __str2bytes(str_data):
        if str_data:
            bytes_data = bytes(str_data, 'utf-8')
            print(">>>%s" % bytes_data)
            return bytes_data

    def _send(self, conn, data):
        if type(data) is str:
           data = bytes(str(data), 'utf-8')
        print("send data: %s ......" % data)
        try:
            return conn.sendall(data)
        except ConnectionResetError:
            print("%s is be disconnected" % str(conn))
            if conn in self._connaddress:
                self._connaddress.remove(conn)
            # client connecting  be closed by client, server need shutdown recv
            return "quit"
        except ConnectionAbortedError:
            print("%s is be disconnected" % str(conn))
            if conn in self._connaddress:
                self._connaddress.remove(conn)
            # client connecting  be closed by client, server need shutdown recv
            return "quit"
        except OSError as oserr:
            print("%s is be disconnected" % str(conn))
            if conn in self._connaddress:
                self._connaddress.remove(conn)
            # client connecting  be closed by client, server need shutdown recv
            return "quit"

    def _run_cmd(self, conn, cmd):
        # subprocess Popen
        process = None
        try:
            process = subprocess.Popen(cmd, shell=True,
                                       stdin=subprocess.PIPE,
                                       stdout=subprocess.PIPE,
                                       stderr=subprocess.STDOUT)
            self._processs.append(process)
            #
            return self._dispose(process, conn, process.poll(), cmd)
        except exception as e:
            print("Exception _run_cmd:%s....." % str(e))
            self._close_process(process)
        finally:
            pass

    def _dispose(self, process, conn, poll_flag, cmd):
        """
        ret msg dispose
        :param poll_flag:
        :param cmd:
        :return:
        """
        while poll_flag is None or poll_flag > 0:
            # failed cmd
            if process.returncode:
                print("Non zero exit code:%s executing: %s poll_flag: %s" % (process.returncode, cmd, poll_flag))
                if process.stderr is None:
                    line = process.stdout.readline()
                else:
                    line = process.stderr.readline()
            else:
                line = process.stdout.readline()  # read line
            poll_flag = process.poll()
            line = line.strip()
            if line:
                # 为了将数据全部发送 先发送长度
                # run_result_len = len(line)
                # self._conn.sendall(bytes(str(run_result_len), 'utf8')))
                self._send(conn, line)
            elif len(line) is 0:
                str1 = str("poll:%s" % poll_flag)
                if poll_flag is None:
                    pass
                elif poll_flag >= 0:
                    self._send(conn, str1)
                    poll_flag = 0
                # else:
                #     self._start_timer(conn)        # wait timeout
                #     poll_flag = 0     # set while-end flag

    def _close_connect(self, conn):
        if conn:
            conn.close()
            # remove list
            if conn in self._connaddress:
                self._connaddress.remove(conn)

    def _close_socket(self):
        if self._socket_obj:
            self._socket_obj.close()

    def _close_process(self, process):
        if process:
            process.kill()
            process.wait()
            # remove list
            if process in self._processs:
                self._processs.remove(process)

    def _close_thread(self, thread):
        if thread:
            thread.kill()
            # remove list
            if thread in self._threads:
                self._threads.remove(thread)

    def dosomething(self, conn, msg):
        # input_msg = input('>>>:')
        # if len(input_msg) == 0:
        #     return
        # data encode utf-8
        # input_byte = bytes(input_msg, 'utf8')
        # 1、send to cilent
        # exit
        order, paramer = AutoMainPlantSocket._cmdsplit(msg)
        if order.upper() in ("QUIT", "Q", "EXIT", "BYE", "BYEBYE"):
            self._send(conn, msg)
            # 2、so something self like shutdown connect
            self._close_connect(conn)
            return "quit"
        elif order in ("shutdown", ):
            pass
        elif order.upper() in ['cmd', 'CMD', 'Cmd']:
            self._send_poll0(conn)
        elif order.upper() in ["ftp", "FTP"]:
            self._send_poll0(conn)
            self._run_ftp_server(conn)
        else:
            pass
        return "continuewhile"

    def _send_poll0(self, conn):
        """
        send end recv flag
        :param conn:
        :return:
        """
        poll0 = str("poll:%s" % 0)
        self._send(conn, bytes(poll0, 'utf-8'))

    def tcplink(self, conn):

        try:
            while True:
                # 2、recv from client cmd
                cmd = self._recv(conn)
                if cmd == "quit":
                    break
                cmd = self.__bytes2str(cmd)
                # when recv order is None user can input order do something....
                if cmd:
                    # order in man_order
                    # ERROR: not enough values to unpack (expected 2, got 1)
                    order, paramer = AutoMainPlantSocket._cmdsplit(cmd)
                    if order in man_order:
                        try:
                            ret = self.dosomething(conn, cmd)
                            if ret == 'continuewhile':
                                continue
                            # conn is shutdown while need to breakup
                            elif ret == "quit":
                                break
                        except Exception as e:
                            print("dosomething Exception %s" % str(e))
                            continue
                    # 4、local exec order
                    self._run_cmd(conn, cmd)
        except exception as e:
            print("Exception tcplink:%s....." % str(e))
            self._close_connect(conn)
            self.run()
        finally:
            # last: need release source
            pass

    def run(self):
        """
        1、accpet
        2、while recv
        :return:
        """
        conn, address, thread = None, None, None
        try:
            while True:
                # 1、accept  thie locator it still wait accept
                conn, address = self._accept()
                # tuple1 = (conn, address)
                self._connaddress.append(conn)
                thread = threading.Thread(target=self.tcplink, args=(conn,))
                thread.start()
                self._threads.append(thread)
        except exception as e:
            print("Exception run: %s....." % str(e))
            self._close_thread(threading)
            self._close_connect(conn)
            self._close_socket()
            self.run()
        finally:
            # last: need release source
            pass

    @staticmethod
    def _getinput(format="-->>>"):
        # get keyboard
        inpt = input(format).strip()
        if len(inpt) == 0:
            return None
        # 数据转换
        input_byte = bytes(inpt, 'utf8')
        return input_byte

    def _run_ftp_server(self, conn):
        thread = None
        try:
            # format = "please enter where dir you want to store >>>: "
            # input_dir = AutoMainPlantSocket._getinput(format)
            # # when input_dir is Exst and is dir, go next
            # while not os.path.isdir(input_dir) or not os.path.exists(input_dir):
            #     print("%s is not dir or is not exits" % input_dir)
            #     input_dir = AutoMainPlantSocket._getinput(format)

            thread = threading.Thread(target=self.ftplink, args=(conn, ))
            thread.start()
            # before start thread function-run recv will done once
            self._threads.append(thread)
        except Exception as e:
            print("Exception _run_ftp_server:%s....." % str(e))
            self._close_thread(thread)
            self._run_ftp_server(conn)
        finally:
            # last: need release source
            pass

    def ftplink(self, conn):
        if conn:
            ftp_server_run()

    # def ftplink(self, conn, filepath):
    #     """
    #     1、正常的情况下 ftp server Thread destory itself  - dosomething is while continue
    #     2、unpack Exception
    #     3、open Exception recv data
    #     4、(2、3)need get last message
    #     :param conn:
    #     :param filepath:
    #     :return:
    #     """
    #     successful_flag = True
    #     try:
    #         self._send(conn, "ftp server thread is start.......")
    #         self._send_poll0(conn)
    #         while True:
    #             file_info = struct.calcsize('128sl')
    #             # file_head_length = int(self._recv(conn, self._MAXBUFFER), 10)
    #             # if file_head_length == "quit":
    #             #     return
    #             file_head = self._recv(conn, file_info)
    #             # if file_head == b"quit":  # connect is colsed  or put finish
    #             #     return
    #             print("file head: %s" % file_head)
    #             if file_head:
    #                 # TypeError: string argument without an encoding unpack(fmt, string)
    #                 file_name, file_size = None, None
    #                 try:
    #                     file_name, file_size = struct.unpack('128sl', file_head)
    #                 except Exception as e:
    #                     print("Exception struct.unpack: %s" % (str(e)))
    #                     successful_flag = False
    #                     while True:
    #                         if "EOF" == self._recv(conn):
    #                             break
    #                     return successful_flag
    #                 # 使用strip()删除打包时附加的多余空字符
    #                 file_name = file_name.decode().strip('\00')
    #                 print(file_name)
    #                 # where you want to save
    #                 # TypeError: Can't mix strings and bytes in path components
    #                 file_new_name = os.path.join(str(filepath, "utf-8"), file_name)
    #                 print('start receiving file name %s size %s...' % (file_name, file_size))
    #                 filew = None
    #                 print("write %s start....." % file_new_name)
    #                 try:
    #                     filew = open(file_new_name, 'wb')
    #                     received_size = 0  # 接收文件的大小
    #                     while not received_size == file_size:
    #                         if file_size - received_size > self._MAXBUFFER:
    #                             r_data = self._recv(conn, self._MAXBUFFER)
    #                             received_size += len(r_data)
    #                         else:
    #                             r_data = conn.recv(file_size - received_size)
    #                             received_size = file_size
    #                         filew.write(r_data)
    #                 except Exception as e:
    #                     except_err = "Exception recv %s data Failed: %s" % (file_name, str(e))
    #                     self._send(conn, except_err)
    #                     successful_flag = False
    #                     self._run_ftp_server(conn)
    #                 finally:
    #                     if filew:
    #                         filew.close()
    #                     msg = None
    #                     if successful_flag:
    #                         msg = ("%s put successful" % file_new_name)
    #                     else:
    #                         msg = ("%s put failed" % file_new_name)
    #                     self._send(conn, msg)
    #                     self._send_poll0(conn)
    #                     while True:
    #                         if "EOF" == self._recv(conn):
    #                             break
    #                     return successful_flag
    #     except Exception as e:
    #         print("Exception ftplink: %s" % str(e))
    #         successful_flag = False
    #     finally:
    #         while True:
    #             if "EOF" == self._recv(conn):
    #                 break
    #         return successful_flag

if __name__ == '__main__':
    AutoMainPlantSocket("127.0.0.1").run()

 

client.py

#!/usr/ftpbin/env python
# -*- coding: utf-8 -*-
#    @version : 0.0.1
#    @File    : client.py
#    @Time    : 2019/7/11 15:36
#    @Site    : 
#    @Software: PyCharm
#    @Author  : KANGXINWEN
#    @Author_email: singbogo@163.com
#    @description:  远程执行命令客户端

import socket
import os
import struct
import time

from Order import man_order, MAXBUFFER
from util.System.network import IP
from util.RemoteManagement.MYFTP.ftpbin.myftpc import ftp_client_run


class AutoMainPlantClient(object):

    def __init__(self):
        self._server_address_port = ('127.0.0.1', 9999)  # server ip port
        self.socket_obj = None
        self._connaddress = None

        self._init()

    def set_server_address_port(self, address, port):
        self._server_address_port = (address, port)

    def set_local_ip_port(self, ip, port=9999):
        if ip:
            self.set_server_address_port(ip, port)
        else:
            self._server_address_port = (IP.get_host_ip(), port)

    def _init(self):
        self.socket_obj = socket.socket()

    def _connect(self):
        connect_flag = True
        try:
            return self.socket_obj.connect(self._server_address_port)
        except Exception as e:
            print("%s is connected failed Exception: %s" % (str(self.socket_obj), str(e)))
            connect_flag = False
        finally:
            return connect_flag

    @staticmethod
    def _str_length_integer(data):
        run_len = len(str(data, 'utf8'))
        print("run_len: %s" % str(run_len))
        return run_len

    @staticmethod
    def _getinput():
        # 发送数据
        inpt = input('>>>').strip()
        if len(inpt) == 0:
            return None, None
        # 数据转换
        input_byte = bytes(inpt, 'utf8')
        return input_byte, inpt

    @staticmethod
    def _cmdsplit(msg, maxsplit=1):
        # TypeError: a bytes-like object is required, not 'str'
        if type(msg) is 'str':
            msg = bytes(msg)
        if msg:
            if msg.find(str(" ")) != -1:
                return msg.split(maxsplit=maxsplit)
            else:
                return msg, "notFound"
        else:
            return None

    @staticmethod
    def _files_list(filename):
        if filename is None:
            return False
        if not os.path.exists(filename):
            print("%s not Exist....." % filename)
            return False
        files_list = list()
        # file
        if os.path.isfile(filename) and os.path.exists(filename):
            files_list.append(filename)
        # dir
        elif os.path.isdir(filename) and os.path.exists(filename):
            filename_list = os.listdir('{}'.format(filename))
            for file in filename_list:
                files_list.append(os.path.join(filename, file))
        return files_list

    def _recv(self, buffersize=MAXBUFFER):
        # recv data
        # when client close connect recv  exception ConnectionResetError
        try:
            data = self.socket_obj.recv(buffersize)
        except ConnectionResetError as connreterr:
            print("%s is be disconnected ConnectionResetError: %s" % (str(self.socket_obj), str(connreterr)))
            # client connecting  be closed by client, server need shutdown recv
            return "quit"
        except ConnectionAbortedError as connaborterr:
            print("%s is be disconnected  ConnectionAbortedError: %s" % (str(self.socket_obj), str(connaborterr)))
            return "quit"
        except Exception as e:
            print("%s is be disconnected  OSError: %s" % (str(self.socket_obj), str(e)))
            return "quit"
        # 转换字符串
        cmd_data = str(bytes(data), 'gbk')
        if not cmd_data.startswith("poll"):
            print("   %s" % cmd_data)
        return cmd_data

    def _send(self, data):
        if len(data) is 0:
            return False
        if type(data) is str:
           data = bytes(str(data), 'utf-8')
        print("send data: %s ......" % data)
        try:
            return self.socket_obj.sendall(data)
        except ConnectionResetError as connreterr:
            print("%s is be disconnected ConnectionResetError: %s" % (str(self.socket_obj), str(connreterr)))
            # client connecting  be closed by client, server need shutdown recv
            return "quit"
        except ConnectionAbortedError as connaborterr:
            print("%s is be disconnected  ConnectionAbortedError: %s" % (str(self.socket_obj), str(connaborterr)))
            return "quit"
        except Exception as e:
            print("%s is be disconnected  OSError: %s" % (str(self.socket_obj), str(e)))
            return "quit"

    def _dispose(self):
        # 存放接收数据的容器变量
        poll, data = None, bytes()
        # poll is 0
        while poll is None:  # 一直等待 poll 0 退出
            data = self._recv()
            if data:
                # 接收数据
                if data == "quit":
                    return "quit"
                # data = str(bytes(data), 'gbk')
                if data.startswith("poll"):
                    poll = data[5:]
                    if poll is None:
                        pass
                    elif int(poll) >= 0:
                        print("   %s" % data)

    def run(self):
        # connect
        if self._connect() is False:
            return False
        # welcome
        self._recv()

        while True:
            # try:
            cmd, inpt = AutoMainPlantClient._getinput()
            if cmd is None or len(cmd) is 0:
                continue
            self._send(cmd)
            if "quit" == self._dispose():
                break
            # order in man_order
            order, paramer = AutoMainPlantClient._cmdsplit(inpt)
            if order in man_order:
                ret = self._dosomething(inpt)
                if ret == "continuewhile":
                    continue
            # except Exception as e:
            #     print("Exception run:%s....." % str(e))
            #     self._close()

    def _close(self):
        if self.socket_obj:
            self.socket_obj.close()

    # def _put(self, cmd, files_list):
    #     """
    #     put filename / dir
    #     :param cmd:
    #     :param files_list:
    #     :return:
    #     """
    #     print("put %s is begining....." % files_list)
    #     successful_flag = True
    #     if files_list is None or cmd is None:
    #         return False
    #     try:
    #         for file in files_list:
    #             if os.path.isfile(file):
    #                 [dirname, filename] = os.path.split(file)
    #                 # file_info = struct.calcsize("128sl") # zip rule
    #                 f_head = struct.pack("128sl", filename.encode("utf-8"), os.stat(file).st_size)
    #                 print("file head %s" % f_head)
    #                 # send file head length
    #                 time.sleep(1)
    #                 # self._send(bytes(str(self._str_length_integer(f_head)), "utf-8"))
    #                 self._send(f_head)
    #                 fopen = None
    #                 try:
    #                     with open(file, 'rb') as fopen:
    #                         while True:
    #                             file_data = fopen.read(MAXBUFFER)
    #                             if not file_data:
    #                                 break
    #                             self._send(file_data)
    #                 except Exception as e:
    #                     print("Exception _put: %s %s" % (f_head, str(e)))
    #                     successful_flag = False
    #                 finally:
    #                     if successful_flag:
    #                         print("\r\n%s put successful\r\n" % file)
    #                     else:
    #                         print("\r\n%s put failed\r\n" % file)
    #                     fopen.close()
    #     except Exception as e:
    #         print("Exception _put: %s" % str(e))
    #         successful_flag = False
    #     finally:
    #         # wait server send msg: file put is successful / failed
    #         self._dispose()
    #         # all file put tell ftp server shutdown
    #         self._send("EOF")
    #         return successful_flag

    def _dosomething(self, cmd):
        """

        :param cmd:
        :return:
        """
        order, paramer = AutoMainPlantClient._cmdsplit(cmd)
        return "continuewhile"



if __name__ == '__main__':
    AutoMainPlantClient().run()

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值