pfSense python 解释器未读取 json

pfSense python interpreter doen't read json

我摸不着头脑。我的目标是获得 json 的输入并将其输出到 influxdb 中。因为我是从 pfSense 收集数据,所以我的工具是有限的。因此我使用 Python 3.8.

首先在 Debian 机器上开发我写了这段代码:

#!/usr/local/bin/python3.8
import os
import json
from influxdb import InfluxDBClient
from datetime import datetime

INTERFACES = ["igb0", "pppoe0"]
WORKING_FOLDER = "/tmp/json"
NOW = datetime.now()
CLIENT = InfluxDBClient("host", "8086", "user", "pw", "firewall")
for interface in INTERFACES:
    stats = {}
    os.popen(
        "vnstat --json d 2 -i {} > {}/{}_d.json".format(interface, WORKING_FOLDER, interface)
    ).read()
    os.popen(
        "vnstat --json m 2 -i {} > {}/{}_m.json".format(interface, WORKING_FOLDER, interface)
    ).read()

    with open(f"{WORKING_FOLDER}/{interface}_d.json", "r") as j:
        day = json.loads(j.read())
    with open(f"{WORKING_FOLDER}/{interface}_m.json", "r") as k:
        month = json.loads(k.read())

    if not len(day["interfaces"][0]["traffic"]["day"]) == 2:
        stats["yesterday"] = {"rx": 0, "tx": 0}
        stats["today"] = {
            "rx": day["interfaces"][0]["traffic"]["day"][0]["rx"],
            "tx": day["interfaces"][0]["traffic"]["day"][0]["tx"],
        }

    else:
        stats["yesterday"] = {
            "rx": day["interfaces"][0]["traffic"]["day"][0]["rx"],
            "tx": day["interfaces"][0]["traffic"]["day"][0]["tx"],
        }
        stats["today"] = {
            "rx": day["interfaces"][0]["traffic"]["day"][1]["rx"],
            "tx": day["interfaces"][0]["traffic"]["day"][1]["tx"],
        }

    if not len(month["interfaces"][0]["traffic"]["month"]) == 2:
        stats["last_month"] = {"rx": 0, "tx": 0}
        stats["this_month"] = {
            "rx": month["interfaces"][0]["traffic"]["month"][0]["rx"],
            "tx": month["interfaces"][0]["traffic"]["month"][0]["tx"],
        }

    else:
        stats["last_month"] = {
            "rx": month["interfaces"][0]["traffic"]["month"][0]["rx"],
            "tx": month["interfaces"][0]["traffic"]["month"][0]["tx"],
        }
        stats["this_month"] = {
            "rx": month["interfaces"][0]["traffic"]["month"][1]["rx"],
            "tx": month["interfaces"][0]["traffic"]["month"][1]["tx"],
        }

    json_body = [
        {
            "measurement": f"stats_{interface}",
            "time": NOW,
            "fields": {
                "yesterday_rx": stats["yesterday"]["rx"],
                "yesterday_tx": stats["yesterday"]["tx"],
                "yesterday_total": int(stats["yesterday"]["rx"]) + int(stats["yesterday"]["tx"]),
                "today_rx": stats["today"]["rx"],
                "today_tx": stats["today"]["tx"],
                "today_total": int(stats["today"]["rx"]) + int(stats["today"]["tx"]),
                "last_month_rx": stats["last_month"]["rx"],
                "last_month_tx": stats["last_month"]["tx"],
                "last_month_total": int(stats["last_month"]["rx"]) + int(stats["last_month"]["tx"]),
                "this_month_rx": stats["this_month"]["rx"],
                "this_month_tx": stats["this_month"]["tx"],
                "this_month_total": int(stats["this_month"]["rx"]) + int(stats["this_month"]["tx"]),
            },
        }
    ]

    CLIENT.write_points(json_body)

在我的 Debian 机器上测试它(注释掉第 12 和 13 行以不覆盖我的 json 文件)一切都很顺利。所以我将脚本移到我的 pfSense 中,但出现以下错误:

[2.5.2-RELEASE][admin@pfSense]/usr/local/pythonscripts: ./trafficstats.py
Traceback (most recent call last):
  File "./trafficstats.py", line 18, in <module>
    month = json.loads(k.read())
  File "/usr/local/lib/python3.8/json/__init__.py", line 357, in loads
    return _default_decoder.decode(s)
  File "/usr/local/lib/python3.8/json/decoder.py", line 337, in decode
    obj, end = self.raw_decode(s, idx=_w(s, 0).end())
  File "/usr/local/lib/python3.8/json/decoder.py", line 355, in raw_decode
    raise JSONDecodeError("Expecting value", s, err.value) from None
json.decoder.JSONDecodeError: Expecting value: line 1 column 1 (char 0)

我问自己为什么。因为看moth文件是有效的json如果我没看错的话:

{"vnstatversion":"2.7","jsonversion":"2","interfaces":[{"name":"igb0","alias":"WAN","created":{"date":{"year":2022,"month":2,"day":4}},"updated":{"date":{"year":2022,"month":2,"day":12},"time":{"hour":9,"minute":30}},"traffic":{"total":{"rx":82416467756,"tx":43825701833},"month":[{"id":88,"date":{"year":2022,"month":2},"rx":82416467756,"tx":43825701833}]}}]}

有人知道发生了什么事吗?

干杯, 加米

“期望值为 char 0”通常意味着您正在尝试 json.loads() 一个空字符串:

>>> import json
>>> json.loads("")
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "C:\Python39\lib\json\__init__.py", line 346, in loads
    return _default_decoder.decode(s)
  File "C:\Python39\lib\json\decoder.py", line 337, in decode
    obj, end = self.raw_decode(s, idx=_w(s, 0).end())
  File "C:\Python39\lib\json\decoder.py", line 355, in raw_decode
    raise JSONDecodeError("Expecting value", s, err.value) from None
json.decoder.JSONDecodeError: Expecting value: line 1 column 1 (char 0)

你没有检查那些 vnstat 调用是否成功(无论如何你应该使用 subprocess.check_call() 而不是 os.popen 因为你甚至没有真正使用管道),所以它是可能文件已创建但为空。

总而言之,我可能会重构为这样的东西:

  • 使用subprocess.check_call()等待进程完成(并检查结果)
  • 不要使用基本上被丢弃的 stats dict
  • 重构重复位
#!/usr/local/bin/python3.8
import json
import subprocess
from datetime import datetime

from influxdb import InfluxDBClient

INTERFACES = ["igb0", "pppoe0"]
WORKING_FOLDER = "/tmp/json"
NOW = datetime.now()
CLIENT = InfluxDBClient("host", "8086", "user", "pw", "firewall")


# Process an interfaces.X.traffic.PERIOD dict into a pair of (previous_rx, previous_tx), (current_rx, current_tx)
def process_traffic(traffic):
    if len(traffic) != 2:
        return ((0, 0), (traffic[0]["rx"], traffic[0]["tx"]))
    return ((traffic[0]["rx"], traffic[0]["tx"]), (traffic[1]["rx"], traffic[1]["tx"]))


def process_iface(interface):
    day_json = f"{WORKING_FOLDER}/{interface}_d.json"
    month_json = f"{WORKING_FOLDER}/{interface}_m.json"
    # TODO: consider shlex.quote for safety
    subprocess.check_call(
        f"vnstat --json d 2 -i {interface} > {day_json}",
        shell=True,
    )
    subprocess.check_call(
        f"vnstat --json m 2 -i {interface} > {month_json}",
        shell=True,
    )

    with open(day_json, "r") as j:
        day_data = json.loads(j.read())
    with open(month_json, "r") as k:
        month_data = json.loads(k.read())

    (yesterday_rx, yesterday_tx), (today_rx, today_tx) = process_traffic(
        day_data["interfaces"][0]["traffic"]["day"]
    )
    (last_month_rx, last_month_tx), (this_month_rx, this_month_tx) = process_traffic(
        month_data["interfaces"][0]["traffic"]["month"]
    )

    json_body = [
        {
            "measurement": f"stats_{interface}",
            "time": NOW,
            "fields": {
                "yesterday_rx": yesterday_rx,
                "yesterday_tx": yesterday_tx,
                "yesterday_total": int(yesterday_rx) + int(yesterday_tx),
                "today_rx": today_rx,
                "today_tx": today_tx,
                "today_total": int(today_rx) + int(today_tx),
                "last_month_rx": last_month_rx,
                "last_month_tx": last_month_tx,
                "last_month_total": int(last_month_rx) + int(last_month_tx),
                "this_month_rx": this_month_rx,
                "this_month_tx": this_month_tx,
                "this_month_total": int(this_month_rx) + int(this_month_tx),
            },
        }
    ]

    CLIENT.write_points(json_body)


def main():
    for interface in INTERFACES:
        process_iface(interface)

if __name__ == "__main__":
    main()