使用Python脚本推送路由器流量情况

使用Python脚本推送路由器流量情况

前言

近一两年PCDN逐渐发展起来,有能力有资源的厂商通过将普通用户的带宽资源收集并整合,从而替代一部分昂贵的CDN资源,同时,厂商给与普通用户一定的激励。

抱着白赚硬件和赚回每个月带宽费的想法,我入坑了某厂的硬件,并在后续又上了另一个类似的项目。至此,家里的上传带宽得以充分利用。于是每天早上起来都要看一下昨天又“赚”到了多少“收益”,过了过了。原本只是为了赚个网费而已,却搞得好像买了股票一般。

这类项目每天的奖励是跟上传流量挂钩的,正好加上那段时间开始尝试使用python语言来解决一些实际问题,于是趁着练手的热度决定写一个脚本将每天的家里网络流量推送给我。推送服务同样选择了 Server酱 ,使用简单方便。

准备工作

Wrtbmon作为流量数据的来源

我并不想写一个监控流量的程序记录流量情况,直接使用了 luci-wrtbwmon 插件,该插件安装完成后,能直接通过Openwrt的菜单访问到实时流量监控与统计页面。我通过该插件的说明和实际操作,大概了解到了该插件的一些工作机制(实际是到今天修复一个问题时,才真正又有了进一步的了解,故写下此文)。

  1. wrtbmon插件会在安装时会生成usage.db、usage.htm。

  2. usage.db文件中就有我想要的各个设备的流量数据。

  3. usage.db文件根据设置的不同会有以下路径:

    • /tmp/usage.db
    • /etc/config/usage.db
  4. usage.htm即是访问该插件页面时展示数据的页面。

  5. 当访问插件页面时,wrtbmon会将数据更新至usage.db文件中,并通过usage.db内的数据更新usage.htm页面。

Wrtbmon插件部分功能源代码

更新页面数据

function usage_data()
    local db = usage_database_path()
    local publish_cmd = "wrtbwmon publish " .. db .. " /tmp/usage.htm /etc/wrtbwmon.user"
    local cmd = "wrtbwmon update " .. db .. " && " .. publish_cmd .. " && cat /tmp/usage.htm"
    luci.http.prepare_content("text/html")
    luci.http.write(luci.sys.exec(cmd))
end

数据存放位置设置

function usage_database_path()
    local cursor = luci.model.uci.cursor()
    if cursor:get("wrtbwmon", "general", "persist") == "1" then
        return "/etc/config/usage.db"
    else
        return "/tmp/usage.db"
    end
end

重置统计

function usage_reset()
    local db = usage_database_path()
    local ret = luci.sys.call("wrtbwmon update " .. db .. " && rm " .. db)
    luci.http.status(204)
end

usage.db文件内容

root@OpenWrt:/usr/bin/myscripts# cat /etc/config/usage.db
#mac,ip,iface,in,out,total,first_date,last_date
48:89:e7:61:39:8d,192.168.0.136,br-lan,58240270,14814386,73054656,19-02-2021_11:20:48,19-02-2021_14:19:09
00:0c:29:0d:5c:d3,192.168.0.128,br-lan,0,0,0,19-02-2021_11:20:48,19-02-2021_11:20:48
00:0c:29:f8:f9:18,192.168.0.129,br-lan,0,0,0,19-02-2021_11:20:48,19-02-2021_11:20:48
1e:53:61:10:ba:ef,192.168.0.138,br-lan,1388266019,31991293,1420257312,19-02-2021_11:20:48,19-02-2021_14:19:09
86:d2:6a:e1:9a:06,192.168.0.154,br-lan,0,0,0,19-02-2021_11:20:48,19-02-2021_11:20:48

开工

有了数据来源就可以开始干活了。

  1. 每日备份当日数据

    先写一个shell脚本备份当天的usage.db文件后重置统计流量。

    #!/bin/ash
    #update at 2021/02/19
    # v0.2 更新了之前发现没有usage.db文件生成,从而导致备份当日数据失败的问题
    # 问题在于之前没有搞懂wrtbmon的usage.db文件生成机制。
    #睡眠59秒,等到到23:59:59再执行.
    #需要先通过wrtbmon更新至usage.db
    current_date=`date +"%Y%m%d"`
    sleep 59
    #更新数据到usage.db文件
    wrtbwmon update /etc/config/usage.db
    #备份usage.db
    cp /etc/config/usage.db /usr/bin/myscripts/backup_usage/usage_${current_date}.db
    #重置wrtbmon数据库.将数据导出到usage.db并删除
    rm /etc/config/usage.db
    sleep 1
    # # # wait 1s ,导出新数据到usage.db,生成新的usage.db文件 这时已经是第二天
    wrtbwmon update /etc/config/usage.db
    
  2. 写一个Python脚本整理数据并使用Server酱推送

    这里我的想法如下:

    • 读取usage.db并写入Sqlite数据库,方便我后面数据拿来做别的(比如画个周期的流量图啥的)。
    • 从数据库中读取昨日的流量数据并按照Markdown表格格式整理好。
    • 通过Server酱推送。

    开始写脚本:

    #!/usr/bin/env python
    import requests
    import sqlite3
    import os
    import datetime
    #固定参数
    path_usagefile = "/usr/bin/myscripts/backup_usage/" #流量记录文件路径
    
    #推送昨日流量信息到server酱微信
    def push2wechat(title, content):
        api = "https://sc.ftqq.com/Server酱KEY.send"
    #     title = u"XX月XX日流量情况汇总"
    #     content = """
    # |        MAC        | 上传流量 | 下载流量 | 总流量 |
    # | :---------------: | -------- | -------- | ------ |
    # | AA-AA-AA-AA-AA-AA | 11.0GB   | 6.0GB    | 17.0GB |
    #     """
    
        data = {
            "text":title,
            "desp":content
        }
        req = requests.get(url=api, params=data)
        os.system("echo `date +%y-%m-%d_%H:%M:%S` Pushed sucessfully to wechat!  >> push2wechat.log")
        return req
    
    #连接和写入数据到数据库
    def init_table_sqlite():
        #创建表
        conn = sqlite3.connect('/usr/bin/myscripts/usage2push.db')
        print("openned database sucessfully!")
        c = conn.cursor()
        c.execute('''CREATE TABLE DEV_STAT 
        (ID INTEGER PRIMARY KEY NOT NULL,
        MAC    TEXT    NOT NULL,
         IP TEXT   NOT NULL,
         INTERFACE  TEXT    NOT NULL,
         DOWN   INT   NOT NULL,
         UP   INT   NOT NULL,
         TOTAL  INT   NOT NULL,
         F_DATE   TEXT NOT NULL,
         L_DATE TEXT    NOT NULL);''')
    
        print("Table created successfully!")
        conn.commit()
        conn.close()
    
    #往表里写入数据
    def write2sqlite():
        #先连接数据库,后面边读边写。
        conn = sqlite3.connect('/usr/bin/myscripts/usage2push.db')
        print("openned database sucessfully!")
        c = conn.cursor()
        #检查数据日期是否重复
        ye_day = get_yesterday()
        #对可能存在1位数字的月份和日进行判断,补0位。
        str_ye_day = str(ye_day.day)
        str_ye_month = str(ye_day.month)
    
        if len(str_ye_day) == 1:
            str_ye_day = "0" + str_ye_day
        if len(str_ye_month) == 1:
            str_ye_month = "0" + str_ye_month
    
        #拼接字符串查找是否已经存在重复数据
        sql_str1 = "SELECT * FROM DEV_STAT WHERE F_DATE LIKE '" + str_ye_day + "-" + str_ye_month + "-" + str(ye_day.year) +"%'"
        c.execute(sql_str1)
        result_exsite = c.fetchone()
        if result_exsite !=None:
            print("该日期数据已存在,不再写入数据库!")
            os.system("echo `date +%y-%m-%d_%H:%M:%S` Date is exsite in database! >> push2wechat.log")#写入失败日志
        else:
            filename = path_usagefile + "usage_" + str(ye_day.year) + str_ye_month + str_ye_day
            print(filename + " 文件正在打开...")
            #打开昨日文件
            f = open(filename)
            for line in f.readlines():
    
                line = line.strip()
                #跳过首行
                if line[0] == '#':
                    continue
    
                tmp = line.split(',')
                sql_str_head = 'INSERT INTO DEV_STAT (MAC,IP,INTERFACE,DOWN,UP,TOTAL,F_DATE,L_DATE) VALUES ('
                sql_str_values = "'" + tmp[0] + "', '" +tmp[1] + "', '" + tmp[2] + "', " + tmp[3] + "," + tmp[4] + "," +tmp[5] + ", '" + tmp[6] + "', '" + tmp[7] + "'"
                sql_str_end = ');'
                sql_str2 = sql_str_head + sql_str_values + sql_str_end
                c.execute(sql_str2)
            #提交数据
            conn.commit()
            print("write to database successfully!")
        #关闭连接
        conn.close()
    
        #计算昨日日期
    def get_yesterday():
        yesterday = datetime.date.today() + datetime.timedelta(-1)
        return yesterday
    
    def repo():
        #从数据库中读取昨日数据
        yesterday = get_yesterday()
        str_ye_day = str(yesterday.day)
        str_ye_month = str(yesterday.month)
        if len(str_ye_day) == 1:
            str_ye_day = "0" + str_ye_day
        if len(str_ye_month) == 1:
            str_ye_month = "0" + str_ye_month
        str_ye = str_ye_day + '-' + str_ye_month + '-' + str(yesterday.year)
        sql_str = "SELECT * FROM DEV_STAT WHERE F_DATE LIKE " + "'" + str_ye + "%' ORDER BY TOTAL DESC"
        #连接数据库
        conn = sqlite3.connect('/usr/bin/myscripts/usage2push.db')
        print("openned database sucessfully!")
        c = conn.cursor()
        c.execute(sql_str)
        values = c.fetchall()
    
    
        #构造markdown格式数据
        title = str(yesterday.year) + "年" + str_ye_month + "月" + str_ye_day + "日流量情况汇总"
        content_head = """
    |        主机名        | 下载流量 | 上传流量 | 总流量 |  
    | :---------------: | --------: | --------: | ------: |  
    """
        content_values = ''
        for value in values:
            c.execute("select * from DEV_HOST where MAC = '" + value[1] + "'")
            result_hostname = c.fetchone()
            if result_hostname != None:
                hostname = result_hostname[1]
            else:
                hostname = "xx:xx:xx:" + value[1][9:]
            downdata = value[4]/1024/1024/1024
            updata = value[5]/1024/1024/1024
            total = value[6]/1024/1024/1024
            content_line = "| " + hostname + " |" + str(round(downdata, 1)) + "GB |" + str(round(updata, 1)) + "GB |" + str(round(total, 1)) + "GB |  \n"
            content_values += content_line
        
        c.close()
    
        content = content_head + content_values
        print(title)
        print(content)
        return (title, content)
    
    if __name__ == "__main__":
    
        #若无数据库,则初始化数据库
        if os.path.exists('/usr/bin/myscripts/usage2push.db') != True:
            init_table_sqlite()
        write2sqlite()
        msg = repo()
        push2wechat(msg[0], msg[1]) #推送至微信
    
    
  3. 在crontab中添加备份文件和推送的定时任务

    这里我设置的每天23点59分启动备份脚本,每天0点2分启动python脚本推送信息,同时将执行脚本的输出信息追加到对应的log文件中,方便发现问题。

    59 23 * * * /usr/bin/myscripts/backup_usage.sh >> /usr/bin/myscripts/backup_usage/backup.log 2>&1 &
    2 0 * * * /usr/bin/python3.8 /usr/bin/myscripts/push2wechat.py >> /usr/bin/myscripts/push.log 2>&1 &
    

效果展示

image-20210219152430932

小结

通过这次折腾,让我每天都能收到路由器推送的流量使用情况。

在Python脚本的编写过程成,了解了Sqlite3、Requests模块的简单使用。

posted @ 2021-02-19 15:38  芥末味洋葱  阅读(488)  评论(0编辑  收藏  举报