1. 程式人生 > 其它 >一個簡陋的批量操作zabbix監控項頁面

一個簡陋的批量操作zabbix監控項頁面

繼上篇zabbix-api,決定簡單寫一個頁面來完成zabbix-api的批量操作

使用django完成

目錄如下

 這個簡陋的頁面長這樣,登入

 長這樣,主頁,展示groups

 長這樣,展示groups中的所有agent類host

 長這樣,展示host中篩選過的items

最後新增items頁面長這樣,可以多選key,批量新增 

程式碼部分

自己日常工作用,頁面不重要,重要的是功能,因為寫頁面太tm耗時了

下面貼下程式碼

urls.py

from django.contrib import admin
from django.urls import path
from DarkZabbix import views urlpatterns = [ path('admin/', admin.site.urls), path('', views.zabbix), path('login.html/', views.login), path('zabbix.html/', views.zabbix), path('zabbix_host.html/', views.zabbix_host), path('zabbix_items.html/', views.zabbix_items), path('additems.html/
', views.add_items), path('keylist.html/', views.key_list), path('delitem.html/', views.del_items), ]

templates裡只用兩個html頁面,一個用於登入,一個用於展示和操作

login.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge"
> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>登入</title> <style> .input{width: 300px; height: 20px;margin-top: 10px;} </style> </head> <body style="background-color: royalblue;"> <div style="position: absolute;top: 50%;left: 50%;transform: translate(-50%,-50%);"> <form action="/login.html/" method="POST"> <h1 style="text-align: center; color: seashell;">Dark-zabbix</h1> <input class="input" name="username" type="text" placeholder=username> <br> <input class="input" name="passwd" type="password" placeholder=passwd> <br> <button style="width: 300px;height: 30px; margin-top: 10px;" >登入</button> <span>{{error}}</span> </form> </div> </body> </html>

zabbix.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>index</title>
    <link href="/static/index.css" rel='stylesheet' type="text/css">
    <script src="/static/jquery-3.6.0.js"></script>
    <script src="/static/jquery.cookie.js"></script>
    <script src="/static/index.js"></script>
</head>
<body>
    {% csrf_token %}
    <div class="hidden block"></div>
    <div class="top">
        <div class="top-left">
           DarkZabbix
        </div>
        <div class="top-right">
            <div>使用者:{{request.user}}</div>
        </div>
    </div>
    <div class="left">
        {%for hostgroup in groupli%}
        <a class="leftmenu astyle" id={{hostgroup.groupid}} href="#">{{hostgroup.name}}</a>
        {%endfor%}
    </div>
    <div class="right">
    </div>
</body>
</html>

statics裡放登入用的證書和jquery檔案,還有自定義的js css檔案

index.js 裡存放頁面操作函式

token = $.cookie('csrftoken')
$(function(){
    hostshow()
    itemshow()
    additems()
    command()
    delitem()
})
function hostshow(){
$('.leftmenu').click(function(){
    var hostid = $(this).attr('id')
    $.ajax({
        url:'/zabbix_host.html/',
        data:{'id':hostid},
        type:'POST',
        headers:{'X-CSRFToken':token},
        success:function(host_li){
            $('.right').empty()
            $('.right').append(host_li)
        }
    })
})
}
function itemshow(){
$('.right').on('click','.host',function(){
    var hostid = $(this).attr('hostid')
    var hostname = $(this).text()
    var interfaceid = $(this).attr('interfaceid')
    $.ajax({
        url:'/zabbix_items.html/',
        data:{'hostid':hostid,'interfaceid':interfaceid,'hostname':hostname},
        type:'POST',
        headers:{'X-CSRFToken':token},
        success:function(items){
            $('.right').empty()
            $('.right').append(items)
        }
    })
})
}
function additems(){
    $('.right').on('click','.additem',function(){
        var hostid = $(this).attr('hostid')
        var interfaceid = $(this).attr('interfaceid')
        addlable='<textarea placeholder="itemname 格式示例:鄭州電信-10.0.0.1或鄭州電信-10.0.0.1,80" class="itemname"  rows="20" ></textarea><br>'+
        '<button class="command"'+'hostid='+ '\"'+hostid+'\"'+'interfaceid='+'\"'+interfaceid+'\"'+'>提交</button>'
        $.ajax({
            url:'/keylist.html/',
            data:{'hostid':hostid},
            type:'POST',
            headers:{'X-CSRFToken':token},
            success:function(ret){
                $('.right').empty()
                var lab = ret + addlable
                $('.right').append(lab)
            }
        })
    })
    }
function command(){
    $('.right').on('click','.command',function(){
        var data = $('.itemname').val()
        var key_ = $('select').val()
        var hostid = $(this).attr('hostid')
        var interfaceid = $(this).attr('interfaceid')
        console.log(data,key_)
        $.ajax({
            url:'/additems.html/',
            data:{'hostid':hostid,'interfaceid':interfaceid,'data':data,'key_':JSON.stringify(key_)},
            type:'POST',
            headers:{'X-CSRFToken':token},
            success:function(ret){
                $('.right').empty()
                $('.right').append(ret)
                 }
                })
            }
            )
        }

function delitem(){
    $('.right').on('click','.del',function(){
        itemid = $(this).attr('itemid')
        $.ajax({
            url:'/delitem.html/',
            data:{'itemid':itemid},
            type:'POST',
            headers:{'X-CSRFToken':token},
            success:function(ret){
                $("."+itemid).remove()
                var htm = $('.right').html()
                $('.right').empty()
                $('.right').append(htm)
            }
            })
        }
        )

    }

jquery.cook,js 官網下載檔案,用於django的CSRF,防止跨站

/*!
 * jQuery Cookie Plugin v1.4.1
 * https://github.com/carhartl/jquery-cookie
 *
 * Copyright 2006, 2014 Klaus Hartl
 * Released under the MIT license
 */
(function (factory) {
    if (typeof define === 'function' && define.amd) {
        // AMD (Register as an anonymous module)
        define(['jquery'], factory);
    } else if (typeof exports === 'object') {
        // Node/CommonJS
        module.exports = factory(require('jquery'));
    } else {
        // Browser globals
        factory(jQuery);
    }
}(function ($) {

    var pluses = /\+/g;

    function encode(s) {
        return config.raw ? s : encodeURIComponent(s);
    }

    function decode(s) {
        return config.raw ? s : decodeURIComponent(s);
    }

    function stringifyCookieValue(value) {
        return encode(config.json ? JSON.stringify(value) : String(value));
    }

    function parseCookieValue(s) {
        if (s.indexOf('"') === 0) {
            // This is a quoted cookie as according to RFC2068, unescape...
            s = s.slice(1, -1).replace(/\\"/g, '"').replace(/\\\\/g, '\\');
        }

        try {
            // Replace server-side written pluses with spaces.
            // If we can't decode the cookie, ignore it, it's unusable.
            // If we can't parse the cookie, ignore it, it's unusable.
            s = decodeURIComponent(s.replace(pluses, ' '));
            return config.json ? JSON.parse(s) : s;
        } catch(e) {}
    }

    function read(s, converter) {
        var value = config.raw ? s : parseCookieValue(s);
        return $.isFunction(converter) ? converter(value) : value;
    }

    var config = $.cookie = function (key, value, options) {

        // Write

        if (arguments.length > 1 && !$.isFunction(value)) {
            options = $.extend({}, config.defaults, options);

            if (typeof options.expires === 'number') {
                var days = options.expires, t = options.expires = new Date();
                t.setMilliseconds(t.getMilliseconds() + days * 864e+5);
            }

            return (document.cookie = [
                encode(key), '=', stringifyCookieValue(value),
                options.expires ? '; expires=' + options.expires.toUTCString() : '', // use expires attribute, max-age is not supported by IE
                options.path    ? '; path=' + options.path : '',
                options.domain  ? '; domain=' + options.domain : '',
                options.secure  ? '; secure' : ''
            ].join(''));
        }

        // Read

        var result = key ? undefined : {},
            // To prevent the for loop in the first place assign an empty array
            // in case there are no cookies at all. Also prevents odd result when
            // calling $.cookie().
            cookies = document.cookie ? document.cookie.split('; ') : [],
            i = 0,
            l = cookies.length;

        for (; i < l; i++) {
            var parts = cookies[i].split('='),
                name = decode(parts.shift()),
                cookie = parts.join('=');

            if (key === name) {
                // If second argument (value) is a function it's a converter...
                result = read(cookie, value);
                break;
            }

            // Prevent storing a cookie that we couldn't decode.
            if (!key && (cookie = read(cookie)) !== undefined) {
                result[name] = cookie;
            }
        }

        return result;
    };

    config.defaults = {};

    $.removeCookie = function (key, options) {
        // Must not alter options, thus extending a fresh object...
        $.cookie(key, '', $.extend({}, options, { expires: -1 }));
        return !$.cookie(key);
    };

}));
View Code

index.css 頁面簡單渲染

*{margin: 0;padding: 0; }
.top{
    background-color: rgb(83, 83, 236); 
    height: 52px;
    }

.top-left{
    width: 300px;height: 53px;
    float: left;
    text-align: center;line-height: 48px;
    color: seashell;
    
   
    }

.top-right{height: 53px;
    float: right;
    text-align: center;line-height: 48px;
    color: seashell;
    margin-right: 60px;
   
    }

.left{background-color: whitesmoke;
    position: absolute;left: 0;top: 48px;bottom: 0; z-index: 99;
    width: 300px;
    border-right:1px solid black;
    z-index: 99;
    overflow: auto;
    }

.right{
    background-color: whitesmoke;
    position: absolute;left: 301px;top: 48px;bottom: 0;right: 0; z-index: 99;
    overflow: scroll;
    z-index: 99;
    overflow: auto;
    }

a:hover{
    background-color: cornflowerblue;
    }

.astyle{
    display: block;padding: 10px;
    }

.block{
    position: absolute;top: 0;bottom: 0;right: 0;left: 0;
    background-color: black ;opacity: 0.2;z-index: 100;
}
.currsor:hover{
    cursor: pointer;
}
.hidden{
    display: none;
}
.pitch{
    background-color:black;color: white;
}
.unpitch{
    background-color: white;color: black;
}
.page{
    margin-left: 10px;
}

textarea{
    width: 453px;
    margin-top: 20px;
    margin-left: 10px;
}
.select{
    margin-top: 20px;
    margin-left: 10px;
    display: block;
}
.command{
    margin-top: 20px;
    margin-left: 10px;
    display: block;
}
table.gridtable {
    font-family: verdana,arial,sans-serif;
    font-size:11px;
    width: 100%;
    color:#333333;
    border-width: 1px;
    border-color: #666666;
    border-collapse: collapse;
    }
    table.gridtable th {
    border-width: 1px;
    padding: 8px;
    border-style: solid;
    border-color: #666666;
    background-color: #dedede;
    }
    .change{
        width: 70px;
    }
    table.gridtable td {
    border-width: 1px;
    padding: 8px;
    border-style: solid;
    border-color: #666666;
    background-color: #ffffff;
    }
    

models.py 定義資料庫,在這裡除了登入用不上

from django.db import models

# Create your models here.
class login(models.Model):
    username = models.CharField(max_length=64)
    passwd = models.CharField(max_length=64)
    def __str__(self):
        return self.username

zbauth.py 定義zabbix-api的登入和一系列操作

import requests
class ZabbixOperates:
    def __init__(self):
        self.zabbix_url = 'https://ex.zabbix.com/api_jsonrpc.php'
        self.user="user"
        self.pwd="passwd"
        self.verify_file,self.ssl_pem,self.ssl_key='/home/user.pem','/home/uer.pem','/home/user.key'
        self.head={"Content-Type":"application/json"}
        self.token = self.get_token()
    def get_token(self):
        data = {
        "jsonrpc":"2.0",
        "method":"user.login",
        "params":{
            "user":self.user,
            "password":self.pwd
        },
        "id":1 
        }
        res = requests.post(self.zabbix_url,headers=self.head,json=data,verify=self.verify_file,cert=(self.ssl_pem,self.ssl_key))
        return res.json()['result']
    def reuqest(self,data):
        res = requests.post(self.zabbix_url,json=data,verify=self.verify_file,cert=(self.ssl_pem,self.ssl_key))
        return(res.json())
    def show(self,value,filter=None):
        if 'error' in value:
            return(value['error'])
    def operate(self,data):
        return self.reuqest(data)
    def get_host(self,groupid=None):
        data={
            "jsonrpc": "2.0",
            "method":"host.get",
            "params": {
                "groupids": groupid,
                "output": [
                    "hostid",
                    "host",
                    "interfaceid",
           
                ],
                "selectInterfaces": [
                    "interfaceid"
                ],
                "filter":{"type":"1"}
            },
            "id":1,
            "auth":self.token
            }
        return self.reuqest(data)
    def get_group(self):
        data={
                "jsonrpc": "2.0",
                "method": "hostgroup.get",
                
                "params": {
                    "output": ["name"],
                }, 
                "auth": self.token,
                "id": 1
            }
        return self.reuqest(data)
    def get_item(self,hostid,key):
        data = {
                "jsonrpc": "2.0",
                "method": "item.get",
                "params": {
                    "output": [
                                'itemid',
                                'name',
                                'key_'
                            ],
                    "hostids": hostid,
                    "search": {
                        "key_": key
                    },
                    "sortfield": "name"
                },
                "auth": self.token,
                "id": 1
                }
        return self.reuqest(data)
    def create_item(self,*value):
        hostid,interfaceid,name,key,units=value
        data={
            "jsonrpc": "2.0",
            "method": "item.create",
            "params": {
                "name": name,
                "key_": key,
                "hostid": hostid,
                "type": 0,
                "value_type": 0,
                "interfaceid": interfaceid,
                "delay": '20s',
                'history': '90d',
                'trends': '365d',
                'units': units,
                'lifetime': '30d',
            },
            "auth": self.token,
            "id": 3
            }
        return self.reuqest(data)
    def del_items(self,itemid):
        data = {
                "jsonrpc": "2.0",
                "method": "item.delete",
                "params": [
                    itemid
                ],
                "auth": self.token,
                "id": 1
            }
        return self.reuqest(data)
    

views.py 存放後端資料處理函式

from django.shortcuts import render,redirect,HttpResponse
from django.views.decorators.csrf import csrf_exempt
from DarkZabbix import models
from functools import wraps
from DarkZabbix.zbauth import ZabbixOperates
import json

# Create your views here.
op = ZabbixOperates()
def auth(func):
    @wraps(func)
    def check_login(res,*args,**kwargs):
        try:
            res.session['name']
            return func(res,*args,**kwargs)
        except:
            return render(res,'login.html')
    return check_login

def deny_get(func):
    @wraps(func)
    def deny(res,*args,**kwatgs):
        if res.method == 'GET':
            return HttpResponse('已記錄此次訪問Ip地址')
        else:
            return func(res,*args,**kwatgs)
    return deny

def login(res):
    if res.method == 'GET':
        return render(res,'login.html')
    elif res.method == 'POST':
        username = res.POST.get('username')
        passwd = res.POST.get('passwd')
        if models.login.objects.filter(username=username,passwd=passwd):
            res.session.set_expiry(3600)
            res.session['name']=username
            return redirect('/zabbix.html')
        else:
            error = '使用者名稱或密碼錯誤'
            return render(res,'login.html',{'error':error})

@auth
def zabbix(res):
    groupli  = op.get_group()['result']
    return render(res,'zabbix.html',{'groupli':groupli})

@auth
@deny_get
def zabbix_host(res):
    group_id = res.POST.get('id')
    hostli = op.get_host(groupid=group_id)['result']
    lab=''
    for host in hostli:
        lab+='<a href="#" class="host astyle" hostid=%s interfaceid=%s>%s</a>'%(host['hostid'],host['interfaces'][0]['interfaceid'],host['host'])
    return HttpResponse(lab)

@auth
@deny_get
def zabbix_items(res):
    host_id,interface_id,hostname = res.POST.get('hostid'),res.POST.get('interfaceid'),res.POST.get('hostname')
    lab='<p>%s  <button class="additem" hostid=%s interfaceid=%s>新增item</button></p><table class="gridtable"><tr><td>name</td><td>key</td><td>operate</td></tr>'%(hostname,host_id,interface_id)
    items = op.get_item(host_id,'ping')['result']
    for item in items:
        lab+='<tr class=%s><td>%s</td><td>%s</td><td><button itemid=%s class=del>刪除</button></td></tr>'%(item['itemid'],item['name'],item['key_'],item['itemid'])
    lab+='</table>'
    return HttpResponse(lab)

@auth
@deny_get
def key_list(res):
    import re
    key_list=['ping_pkloss[*]','ping_restime[*]']
    host_id = res.POST.get('hostid')
    items = op.get_item(host_id,'ping')['result']
    for item in items:
        key_ = item['key_']
        try:
            host  = re.findall('\[.*\]',key_)[0]
            key_= key_.replace(host,'[*]')
        except:
            pass
        if key_ not in key_list:
            key_list.append(key_)
    labli=['<option>%s</option>'%x for x in key_list]
    lab = '<p class="select">key: <select multiple="multiple">'+''.join(labli)+'</select></p>'
    return HttpResponse(lab)

@auth
@deny_get
def add_items(res):
    hostid,interfaceid,itemname,key_list = res.POST.get('hostid'),res.POST.get('interfaceid'),res.POST.get('data'),res.POST.get('key_')
    key_list = json.loads(key_list)
    if not key_list:
        return HttpResponse('缺少必選項或格式錯誤')
    items = itemname.split('\n')
    for item in items:
        name = item
        item = item.strip()
        _,host = item.split('-')
        for key_ in key_list:
            key_ = key_.replace('*',host)
            type_ = 'restime' if 'restime' in key_ else 'pkloss'
            unit = 'ms' if type_ =='restime' else '%'
            ret = op.create_item(hostid,interfaceid,name+type_,key_,unit)
            if op.show(ret):
                return HttpResponse(json.dumps(op.show(ret)))
    lab='操作成功'
    return HttpResponse(lab)

@auth
@deny_get
def del_items(res):
    itemid = res.POST.get('itemid')
    ret = op.del_items(itemid)
    if op.show(ret):
        return HttpResponse(json.show(op.show(ret)))
    lab='操作成功'
    return HttpResponse(lab)

ending......