• 【python】使用python写windows服务


    背景

        运维windows服务器的同学都知道,windows服务器进行批量管理的时候非常麻烦,没有比较顺手的工具,虽然saltstack和ansible都能够支持windows操作,但是使用起来总感觉不太舒服,比如ansible使用的winrm进行远程操作,需要提前在windows上进行设置,并且要求操作系统版本和powershell版本,根据个人使用经验,经常存在链接不上,连接慢,或者推送失败的问题,较高的失败率对于自动化运维体系的建立不利,所以与其受制于人,不如自己动手写一个agent,完全自己控制。

    技术平台

        服务器:windows服务器,测试过win2008r2、win2012、win7和win10.其他版本不知道怎么样。

        开发语言:python

        python模块:pywin32(python中的windows库),pyinstaller(用于将py文件生成exe程序),flask(web服务器)。以上三个模块都可以通过pip安装。以及一些python内置模块

        优势:完全自己控制,根据自己需要开发功能。服务器端不需要安装任何其他依赖(不需要安装python环境,exe自带运行环境)

    流程结构

    代码实现

    agent的实现代码如下,代码有详细的注释,就不解释了。

      1 # -*- coding:utf-8 -*-
      2 import win32serviceutil
      3 import win32service
      4 import win32event
      5 from flask import Flask
      6 from flask import request
      7 import sys
      8 import os
      9 import zipfile
     10 import requests
     11 import shutil
     12 import re
     13 import time
     14 #设置编码
     15 reload(sys)
     16 sys.setdefaultencoding('utf-8')
     17 ################################################################
     18 ##########自定义函数区##########################################
     19 ################################################################
     20 #下载文件函数
     21 def get_url_file(url):
     22     #下载到临时目录,如果临时目录不存在就创建一个
     23     download_path = "D:\tmp\%s" % (time.time())
     24     if os.path.exists(download_path):
     25         pass
     26     else:
     27         os.makedirs(download_path)
     28     r = requests.get(url, stream=True)
     29     #解析文件名,如url为:http://www.ftp.com/test.zip那么文件名就是test.zip,并下载和写入文件
     30     filename = "%s\%s" % (download_path, url.split('/')[-1])
     31     with open(filename, "wb") as pdf:
     32         for chunk in r.iter_content(chunk_size=1024):
     33             if chunk:
     34                 pdf.write(chunk)
     35         return {'filename':filename,'download_path':download_path}
     36 
     37 
     38 def config_zabbix(hostname):
     39     #复制一份C:\zabbix\zabbix_agentd_win.conf.bak的zabbix模板文件,并修改其中的Hostname=new为新的hostname,然后生成新的配置文件。
     40     try:
     41         open('C:\zabbix\zabbix_agentd_win.conf', 'w').write(re.sub(r'Hostname=new', 'Hostname=%s' % hostname, open('C:\zabbix\zabbix_agentd_win.conf.bak').read()))
     42     except:
     43         return "error"
     44     # 重启zabbix agent服务
     45     service_list = os.popen('net start').read()
     46     if service_list.find('Zabbix Agent') == -1:
     47         try:
     48             os.system('net start "Zabbix Agent"')
     49         except:
     50             return "error"
     51     else:
     52         try:
     53             os.system('net stop "Zabbix Agent')
     54         except:
     55             return "error"
     56         try:
     57             os.system('net start "Zabbix Agent')
     58         except:
     59             return "error"
     60         return "ok"
     61 
     62 
     63 # 加压缩文件函数
     64 def un_zip(file_name,dest_path):
     65     zip_file = zipfile.ZipFile(file_name)
     66     file_pre = file_name.split('\')[-1].split('.')[0]
     67     for names in zip_file.namelist():
     68         zip_file.extract(names, dest_path)
     69     zip_file.close()
     70     return (dest_path + '\' + file_pre)
     71 ###################################################################
     72 #############自定义函数区结束######################################
     73 ###################################################################
     74 
     75 #windows服务中显示的名字
     76 class zlsService(win32serviceutil.ServiceFramework):
     77     _svc_name_ = 'zls_agent' ###可以根据自己喜好修改
     78     _svc_display_name_ = 'zls_agent'  ###可以根据自己喜好修改
     79     _svc_description_ = 'zls_agent'  ###可以根据自己喜好修改
     80 
     81 
     82     def __init__(self,args):
     83         win32serviceutil.ServiceFramework.__init__(self,args)
     84         self.stop_event = win32event.CreateEvent(None, 0, 0, None)
     85         self.run = True
     86 
     87     def SvcDoRun(self):
     88         app = Flask(__name__)
     89         ##############################################################
     90         #########flask路由设置区,自定义功能也放在这里################
     91         ##############################################################
     92         #推送文件服务,比如传入一个url和目标地址,装有本agent的客户端就会下载这个url并把文件放在指定位置
     93         @app.route('/transferfile', methods=['GET', 'POST'])
     94         def transferfile():
     95             if request.method == "GET":
     96                 url = request.args.get('url')
     97                 dest = request.args.get('dest')
     98                 try:
     99                     down_data = get_url_file(url)
    100                     filename = down_data['filename']
    101                     download_path = down_data['download_path']
    102                 except BaseException, e:
    103                     return "download url file error : %s" % e
    104                 # 加压缩文件
    105                 try:
    106                     filepath = un_zip(filename,download_path)
    107                 except BaseException, e:
    108                     return "un zip error : %s" % e
    109                 # 删除压缩文件
    110                 try:
    111                     os.remove(filename)
    112                 except:
    113                     pass
    114                 # 复制文件内容到指定目录
    115                 try:
    116                     shutil.copytree('%s' % filepath.replace('\', '\\'), '%s' % dest)
    117                 except BaseException, e:
    118                     return "copy file error: %s" % e
    119                 # d删除解压缩文件夹
    120                 try:
    121                     shutil.rmtree(filepath.replace('\', '\\'))
    122                 except:
    123                     pass
    124                 return "ok"
    125         #修改配置文件路由,用来远程修改zabbix配置文件,比如curl http://server_ip:50000/zabbix?hostname="zabbix_agent"就会把位于服务器上的zabbix_agentd配置文件中的Hostname修改为Hostname=zabbix_agent。
    126         @app.route('/zabbix', methods=['GET', 'POST'])
    127         def config_zabbix_func():
    128             if request.method == "GET":
    129                 hostname = request.args.get('hostname')
    130                 result = config_zabbix(hostname)
    131                 return result
    132         #测试路由,用来测试agent是否正在运行,返回ok表示正在运行
    133         @app.route('/', methods=['GET', 'POST'])
    134         def test():
    135             if request.method == "GET":
    136                 return "ok"
    137         #使用flask自带的web服务器,监听本地所有地址的50000端口
    138         ##############################################################
    139         #############自定义功能区结束#################################
    140         ##############################################################
    141         app.run(host='0.0.0.0', port=50000)
    142     def SvcStop(self):
    143         self.ReportServiceStatus(win32service.SERVICE_STOP_PENDING)
    144         win32event.SetEvent(self.stop_event)
    145         self.ReportServiceStatus(win32service.SERVICE_STOPPED)
    146         self.run = False
    147 
    148 if __name__ == '__main__':
    149     import sys
    150     import servicemanager
    151     if len(sys.argv) == 1:
    152         try:
    153             evtsrc_dll = os.path.abspath(servicemanager.__file__)
    154             servicemanager.PrepareToHostSingle(zlsService) #如果修改过名字,名字要统一
    155             servicemanager.Initialize('zlsService',evtsrc_dll) #如果修改过名字,名字要统一
    156             servicemanager.StartServiceCtrlDispatcher()
    157         except win32service.error as details:
    158             import winerror
    159             if details == winerror.ERROR_FAILED_SERVICE_CONTROLLER_CONNECT:
    160                 win32serviceutil.usage()
    161     else:
    162         win32serviceutil.HandleCommandLine(zlsService) #如果修改过名字,名字要统一
    View Code

    生成exe程序

    安装完成pyinstaller以后,打开命令行窗口

    输入:pyinstaller -F windows_service.py

    说明:-F表示生成一个单一的exe文件。其他选项使用pyinstaller --help查看。

    执行完成后,提示生成exe程序

    找到这个程序

    将程序复制到测试服务器上,安装

    以管理员身份打开cmd,中输入 windows_servcie.exe install

    提示安装成功

    启动服务,然后设置成自动启动

     测试,浏览器中访问IP:50000端口,返回ok表示agent运行正常

    后续:

    想要卸载服务,可以使用

     重要说明:

    以上的测试,均在测试环境中进行,不要用在正式环境,因为这个agent没有加密通信,存在被人攻击的危险!!!

    实际使用过程中,一定要对通信进行加密,可以使用https或者在命令中加入token,对token进行认证,只有正确的token才执行命令,或者使用rsa证书加密,这里就不再举例。

    此agent只是给了三个示例性应用,实际用的时候,可以根据自己需要在函数区和路由定义区加入自己的程序,比如把这个agent做成监控系统,获取本地信息穿给服务端,在此抛砖引玉,剩下的就看自己的发挥了。

  • 相关阅读:
    css clear
    设置 系统软件
    Canvas 生成 bitmap
    HashTable
    JSF
    android 自定义 锁屏
    java socket
    php mysql 配置
    mysql 启动方法
    jQuery 表格
  • 原文地址:https://www.cnblogs.com/zhenglisai/p/8073963.html
Copyright © 2020-2023  润新知