自动化监控系统(四) 客户端设计

一个程序的目录结构:

bin:可执行文件

conf:配置文件

core:逻辑关系

plugins:各种插件

var:日志

客户端:

1、设置一个程序入口,运行该文件A就能启动客户端。

2、给A传位置参数(start 或 stop),通过获取位置参数名称,使用反射来调用相应方法,做到启动或者停止client。

3、启动客户端后,通过发起HTTP请求,获取service下发的任务(监控哪些服务:CPU、memory、network。。。。。。)

发送请求中,应该有client相关配置信息:主机Id、主机IP地址、主机端口、url配置、请求超时时间和更新监控配置时间。

暂时这些,后面再添加

 client端通过特定url,发送http请求,然后server端返回相应的信息,告诉client需要监控什么服务

在服务端也需要添加相应的url和view,返回客户端的请求信息。

view

from django.shortcuts import render

# Create your views here.

from django.views.generic import View
from django.http import HttpResponse
import json

from .serializer import ClientHandler


class client_config_view(View):

    #返回客户端需要监控信息
    def get(self,request,client_id):
        #获取client信息
        # client_configs = ClientHandler(client_id).fetch_configs()
        client_obj = ClientHandler(client_id)
        client_config = client_obj.fetch_configs()
        print("客户端ID",client_id)
        if client_config:
            return HttpResponse(json.dumps(client_config),content_type="application/json")
View Code

serializer

#!/usr/bin/env python
# _*_ coding:utf-8 _*_
__author__ = "BIGNI"
__date__ = "2017/4/29 16:34"
import traceback

from django.views.generic import View

from .models import Host


class ClientHandler(View):
    #初始化
    def __init__(self,client_id):
        self.client_id = client_id
        #client配置
        self.client_configs = {
            "services":{}
        }


    def fetch_configs(self):

        try:

            host_obj_id = Host.objects.get(id=self.client_id)
            print(">>>>>>>>>",host_obj_id)
            #获取模板list
            template_list = list(host_obj_id.templates.select_related())
            print(">>>>",template_list)
            #获取主机组obj

            host_group_obj = host_obj_id.host_groups.select_related()
            #把主机组下的目标添加进来
            template_list.extend([template for template in host_group_obj])
            print("--->",template_list)
            #获取服务列表
            for template in template_list:
                for service in template.services.select_related():
                    print(service)
                    #获取插件名和间隔时间
                    self.client_configs['services'][service.name] = [service.plugin_name,service.interval]

        except:
            traceback.print_exc()
        return self.client_configs

# test = ClientHandler(1)
# print(test,test.fetch_configs())
View Code

 测试下:

调用特定url,server端可以返回需要监控的信息。

{"services": {"LinuxCPU": ["get_linux_cpu", 15], "LinuxNetwork": ["GetNetworkStatus", 60], "LinuxMemory": ["get_memory_info", 60]}}
返回这种格式{"services":{"监控的服务":["插件名",监控间隔]}}
client拿到这个信息后会根据插件名调用相关的脚本,拿到数据返回给server,这一过程会在线程里完成。

 接下来继续client端的开发:

1、通过运行monitor_client启动客户端,执行main_command方法。

2、main_command方法自带start和stop两个方法

#!/usr/bin/env python
# _*_ coding:utf-8 _*_
__author__ = "BIGNI"
__date__ = "2017/4/15 23:48"

from .client import ClientHandlers

class main_command(object):
    #初始化
    def __init__(self,sys_argv):
        self.sys_argv = sys_argv
        if len(sys_argv) < 2 :
            exit("请输入start或stop")
        else:
            self.entry_command()

    #根据命令调用方法
    def entry_command(self):
        print("##############################################")
        if hasattr(self.sys_argv[1]):
            func = getattr(self,self.sys_argv[1])
            return func()
        else:
            print("请输入正确的命令")


    def start(self):
        client = ClientHandlers()
        client.forever_run()

    def stop(self):
        pass
main_command

3、start方法会创建ClientHandlers对象,接着调用里面的forever_run方法,获取server端返回的信息,并根据插件名给各个服务进行监控

#!/usr/bin/env python3
# _*_ coding:utf-8 _*_
__author__ = "BIGNI"
__date__ = "2017/4/29 11:42"
import time,threading,json

import requests

from conf import settings
from plugins import plugin_api

class ClientHandlers(object):

    def __init__(self):
        #初始化监控服务
        self.monitor_services = {}

    def load_lastest_config(self):
        """
        加载最新的配置信息
        :return:
        """
        #request请求方式
        request_type = settings.configs["urls"]["get_configs"][1]
        #拼接url
        request_url = "%s/%s" % (settings.configs['urls']['get_configs'][0], settings.configs['HostID'])
        lastest_config = self.url_request(request_type,request_url)
        #把最小配置更新到监控服务字典中
        self.monitor_services.update(lastest_config)


    def forever_run(self):
        #启动客户端
        exit_flag = False
        #第一次启动时初始化最新配置时间
        config_lastest_update_time = 0
        while not exit_flag:

            if time.time() - config_lastest_update_time > settings.configs['ConfigUpdateInterval']:
                self.load_lastest_config()
                print("Lastest_config:",self.monitor_services)
                config_lastest_update_time = time.time()

            """
            Lastest_config: {'services': {'LinuxCPU': ['get_linux_cpu', 15], 'LinuxMemory': ['get_memory_info', 60],
            'LinuxNetwork': ['GetNetworkStatus', 60]}}

            """
            for service_name,val in self.monitor_services['services'].items():
                if len(val) ==2:
                    # 第一次启动插件时,初始化当前时间,给每个服务维护一个计时器
                    self.monitor_services['services'][service_name].append(0)
                #获取监控间隔和最新插件时间
                monitor_interval = val[1]
                last_invoke_time = val[2]
                if time.time() - last_invoke_time > monitor_interval:
                    print("--->",last_invoke_time,"--->",time.time())
                    #重置计时器时间
                    self.monitor_services['services'][service_name][2] = time.time()
                    t = threading.Thread(target=self.invoke_plugin,args=(service_name,val))
                    t.start()
                    print("启动监控的服务: [{ServiceName}]".format(ServiceName=service_name))

                else:
                    #需要等待监控间隔时间
                    print("监控的服务: {ServiceName} 距离下次启动时间:{interval} secs".format(ServiceName=service_name,
                                                                  interval=monitor_interval - (time.time() - last_invoke_time)))
                    time.sleep(5)

        time.sleep(1)


    #运行插件
    def invoke_plugin(self,service_name,val):
        #{"services": {"LinuxNetwork": ["n/a", 60,0], "LinuxMemory": ["n/a", 60,0], "LinuxCPU": ["n/a", 60,0]}}
        #获取插件名
        plugin_name = val[0]
        if hasattr(plugin_api,plugin_name):
            func = getattr(plugin_api,plugin_name)
            plugin_callback = func()
            print("####################################################")
            print(plugin_callback)
            print("####################################################")

            report_data = {
                'client_id':settings.configs['HostID'],
                'service_name':service_name,
                'data':plugin_callback
            }
            #请求方式get or post
            request_action = settings.configs['urls']['service_report'][1]
            #请求路径
            request_url = settings.configs['urls']['service_report'][0]

            #report_data = json.dumps(report_data)
            # print('---report data(发送的数据):',report_data)
            #调用url_request方法,以post方式发送request
            self.url_request(request_action, request_url, params=report_data)
        else:
            print("33[31;1mCannot find service [%s]'s plugin name [%s] in plugin_api33[0m"% (service_name,plugin_name ))
        print('--plugin:',val)


    def url_request(self,action,request_url,**extra_data):

        abs_url = "http://{ip_addr}:{port}/{url}".format(ip_addr=settings.configs["Server"],
                                                         port=settings.configs["ServerPort"],
                                                         url=request_url)
        print("33[31;1m{abs_url}33[0m".format(abs_url=abs_url),type(extra_data),extra_data)
        if action in ('get',"GET"):
            print(abs_url,extra_data)
            try:
                r = requests.get(abs_url,timeout=settings.configs['RequestTimeout'])
                r_data = r.json()
                return r_data

            except requests.RequestException as E :
                exit("33[31;1m%s33[0m" % E)

        elif action in ('post','POST'):
            try:
                #把数据转换成json再通过post发送
                # data = json.dumps(extra_data['params'])
                req = requests.post(url=abs_url,data=extra_data["params"])

                res_data = req.json()
                # res_data = req.text
                print("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~")
                print("33[31;1m[%s]:[%s]33[0m response:
%s,%s" % (action, abs_url, res_data,data))
                print("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~")
                return res_data

            except Exception as e:
                print('------exec', e)
                exit("33[31;1m%s33[0m" % e)
client

PS:客户端在接收到服务端返回的配置信息,并不能识别具体要监控的服务,比如server端告诉client端需要监控CPU各项参数等,所以两端要能约定好,比如server端返回CPU、Memory和Network这三个字符串,就表示要监控这三种服务,三个字符串在这里就是插件名。插件统一放在plugins目录下。

 不同服务监控的时间可以通过server端返回的监控间隔来判断

 

 每次监控服务就生成一个新的线程,然后就让线程返回数据给服务端,不需要再做处理。

client通过post发送的数据格式:

report_data = {
     #客户端ID
      client_id':settings.configs['HostID'],
      #服务名
      'service_name':service_name,
      #数据
      'data':plugin_callback
}

发送数据这里我踩了个坑,其实也是不熟悉requests模块造成的,requests的post发送有三种方式,其中有form和json两种,

区别是form发送可以直接把report_data直接发送到客户端,通过request.post.get方法可以获取到值。但我的report_data是嵌套字典,这种发送方式会把数据内容进行转换(细节我没搞懂,反正我传的数据变了。),所以嵌套字典的先转换成json格式,然后再用requests发送,服务端通过request.body可以拿到数据。

 对于server端和client端的数据交互,推荐使用requests模块,可以自动处理数据的编码、json、发送超时等问题。

 问题1:在models里的manytomany字段不会存储在数据表中,多对多的在xadmin里要按ctrl选中才行,不然通过select_relate获取到的是空的queryset,

问题2:我调用model的serializer没放在app的默认views文件里,进行debug调试时,serializer也需要打上断点。

问题3:我在client通过post方式发送数据给server端,debug调试时发现没取到值,查了资料,原来requests库有三种post发送数据方式,我使用的是json方式(发送前先用json.dumps处理下),改成用form方式(实际就是不需要json,requests会自动转换)

 
原文地址:https://www.cnblogs.com/laonicc/p/6701779.html