diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..361c2f8 --- /dev/null +++ b/.env.example @@ -0,0 +1,25 @@ +REPORT_MODE=redis #http,redis +SOCKET_TIMEOUT=10 + +HOST=127.0.0.1 +PORT=6379 +PASSWORD="" +SSL=False + +REPORT_TIME=60 +TIMEOUT=259200 +RETENTION_TIME=86400 + +SERVER_TOKEN="" +SERVER_URL="http://127.0.0.1" + +IPV4_API="https://v4.ipv6-test.com/api/myip.php" +IPV6_API="https://v6.ipv6-test.com/api/myip.php" +IP_API="http://ip-api.com/json?fields=country,countryCode" + + +DISK_EXCLUDE=/run,/sys,/boot,/dev,/proc,/gdrive,/var/lib +DISK_FS_EXCLUDE=tmpfs,overlay +DISK_OPTS_EXCLUDE=ro + +#PROCFS_PATH=/rootfs/proc # mount for docker. \ No newline at end of file diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 0000000..8a1bf01 --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,292 @@ +name: GitHub CI +on: + workflow_dispatch: + +concurrency: + group: ${{ github.ref }}-${{ github.workflow }} + cancel-in-progress: true + +jobs: + linux32_build: + name: Linux x86 Build + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - name: Build + run: docker run --rm -v $GITHUB_WORKSPACE:/root/workdir multiarch/ubuntu-core:i386-bionic /bin/sh -c "apt update && apt install -y bash zlib1g zlib1g-dev make cmake git python3 python3-pip && cd /root/workdir && chmod +x build-script/build.release.sh && bash build-script/build.release.sh" + + - name: Package Release + run: tar czf server_monitor_linux32.tar.gz monitor + + - name: Upload + uses: actions/upload-artifact@v3 + with: + path: | + server_monitor_*.tar.gz + + - name: Draft Release + uses: softprops/action-gh-release@v1 + if: ${{ github.event_name != 'pull_request' && startsWith(github.ref, 'refs/tags/') }} + with: + files: server_monitor_linux32.tar.gz + draft: true + tag_name: Alpha + + linux64_build: + name: Linux x86_64 Build + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - name: Build + run: docker run -v $GITHUB_WORKSPACE:/root/workdir multiarch/ubuntu-core:amd64-focal /bin/sh -c "apt update && apt install -y bash git python3 python3-pip && cd /root/workdir && chmod +x build-script/build.release.sh && bash build-script/build.release.sh" + + - name: Package Release + run: tar czf server_monitor_linux64.tar.gz monitor + + - name: Upload + uses: actions/upload-artifact@v3 + with: + path: | + server_monitor_*.tar.gz + + - name: Draft Release + uses: softprops/action-gh-release@v1 + if: ${{ github.event_name != 'pull_request' && startsWith(github.ref, 'refs/tags/') }} + with: + files: server_monitor_linux64.tar.gz + draft: true + tag_name: Alpha + + # armv7_build: + # name: Linux armv7 Build + # runs-on: ubuntu-latest + # steps: + # - uses: actions/checkout@v3 + # - name: Build + # run: | + # docker run --rm --privileged multiarch/qemu-user-static:register --reset + # docker run -v $GITHUB_WORKSPACE:/root/workdir multiarch/ubuntu-core:armv7-focal /bin/sh -c "apt update && apt install -y bash git python3 python3-pip && cd /root/workdir && chmod +x build-script/build.release.sh && bash build-script/build.release.sh" + # - name: Package Release + # run: tar czf server_monitor_armv7.tar.gz monitor + + # - name: Upload + # uses: actions/upload-artifact@v3 + # with: + # path: | + # server_monitor_*.tar.gz + + # - name: Draft Release + # uses: softprops/action-gh-release@v1 + # if: ${{ github.event_name != 'pull_request' && startsWith(github.ref, 'refs/tags/') }} + # with: + # files: server_monitor_armv7.tar.gz + # draft: true + # tag_name: Alpha + + arm64_build: + name: Linux arm64 Build + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - name: Build + run: | + docker run --rm --privileged multiarch/qemu-user-static:register --reset + docker run -v $GITHUB_WORKSPACE:/root/workdir multiarch/ubuntu-core:arm64-focal /bin/sh -c "apt update && apt install -y bash git python3 python3-pip && cd /root/workdir && chmod +x build-script/build.release.sh && bash build-script/build.release.sh" + - name: Package Release + run: tar czf server_monitor_arm64.tar.gz monitor + + - name: Upload + uses: actions/upload-artifact@v3 + with: + path: | + server_monitor_*.tar.gz + + - name: Draft Release + uses: softprops/action-gh-release@v1 + if: ${{ github.event_name != 'pull_request' && startsWith(github.ref, 'refs/tags/') }} + with: + files: server_monitor_arm64.tar.gz + draft: true + tag_name: Alpha + + armhf_build: + name: Linux armhf Build + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - name: Build + run: | + docker run --rm --privileged multiarch/qemu-user-static:register --reset + docker run -v $GITHUB_WORKSPACE:/root/workdir multiarch/ubuntu-core:armhf-focal /bin/sh -c "apt update && apt install -y bash git python3 python3-pip && cd /root/workdir && chmod +x build-script/build.release.sh && bash build-script/build.release.sh" + - name: Package Release + run: tar czf server_monitor_armhf.tar.gz monitor + + - name: Upload + uses: actions/upload-artifact@v3 + with: + path: | + server_monitor_*.tar.gz + + - name: Draft Release + uses: softprops/action-gh-release@v1 + if: ${{ github.event_name != 'pull_request' && startsWith(github.ref, 'refs/tags/') }} + with: + files: server_monitor_armhf.tar.gz + draft: true + tag_name: Alpha + + # macos_build: + # name: macOS Build + # runs-on: macos-latest + # steps: + # - uses: actions/checkout@v3 + # - name: Add commit id into version + # if: ${{ !startsWith(github.ref, 'refs/tags/') }} + # run: SHA=$(git rev-parse --short HEAD) && sed -i -e 's/\(v[0-9]\.[0-9]\.[0-9]\)/\1-'"$SHA"'/' src/version.h + # - name: Build + # run: bash build-script/build.macos.release.sh + + # - name: Package Release + # run: tar czf server_monitor_darwin64.tar.gz monitor + + # - name: Upload + # uses: actions/upload-artifact@v3 + # with: + # path: | + # server_monitor_*.tar.gz + + # - name: Draft Release + # uses: softprops/action-gh-release@v1 + # if: ${{ github.event_name != 'pull_request' && startsWith(github.ref, 'refs/tags/') }} + # with: + # files: server_monitor_darwin64.tar.gz + # draft: true + # tag_name: Alpha + + # windows64_build: + # name: Windows x86_64 Build + # runs-on: windows-latest + # defaults: + # run: + # shell: msys2 {0} + # steps: + # - uses: actions/checkout@v3 + # - uses: actions/setup-node@v3 + # with: + # node-version: "16" + # - uses: msys2/setup-msys2@v2 + # with: + # update: true + # install: base-devel git mingw-w64-x86_64-gcc mingw-w64-x86_64-cmake mingw-w64-x86_64-libevent mingw-w64-x86_64-pcre2 patch + # msystem: MINGW64 + # path-type: inherit + # - name: Add commit id into version + # if: ${{ !startsWith(github.ref, 'refs/tags/') }} + # run: SHA=$(git rev-parse --short HEAD) && sed -i 's/\(v[0-9]\.[0-9]\.[0-9]\)/\1-'"$SHA"'/' src/version.h + # - name: Build + # run: bash build-script/build.windows.release.sh + + # - name: Package Release + # run: 7z a server_monitor_win64.7z monitor/ + + # - name: Upload + # uses: actions/upload-artifact@v3 + # with: + # path: | + # server_monitor_*.7z + + # - name: Draft Release + # uses: softprops/action-gh-release@v1 + # if: ${{ github.event_name != 'pull_request' && startsWith(github.ref, 'refs/tags/') }} + # with: + # files: server_monitor_win64.7z + # draft: true + # tag_name: Alpha + + # windows32_build: + # name: Windows x86 Build + # runs-on: windows-latest + # defaults: + # run: + # shell: msys2 {0} + # steps: + # - uses: actions/checkout@v3 + # - uses: actions/setup-node@v3 + # with: + # node-version: "16" + # - uses: msys2/setup-msys2@v2 + # with: + # update: true + # install: base-devel git mingw-w64-i686-gcc mingw-w64-i686-cmake mingw-w64-i686-libevent mingw-w64-i686-pcre2 patch + # msystem: MINGW32 + # path-type: inherit + # - name: Add commit id into version + # if: ${{ !startsWith(github.ref, 'refs/tags/') }} + # run: SHA=$(git rev-parse --short HEAD) && sed -i 's/\(v[0-9]\.[0-9]\.[0-9]\)/\1-'"$SHA"'/' src/version.h + # - name: Build + # run: bash build-script/build.windows.release.sh + + # - name: Package Release + # run: 7z a server_monitor_win32.7z monitor/ + + # - name: Upload + # uses: actions/upload-artifact@v3 + # with: + # path: | + # server_monitor_*.7z + + # - name: Draft Release + # uses: softprops/action-gh-release@v1 + # if: ${{ github.event_name != 'pull_request' && startsWith(github.ref, 'refs/tags/') }} + # with: + # files: server_monitor_win32.7z + # draft: true + # tag_name: Alpha + + Upload-Release: + permissions: write-all + if: ${{ github.ref_type=='branch' }} + needs: + [ + linux64_build, + linux32_build, +# armv7_build, + armhf_build, + arm64_build, +# macos_build, +# windows64_build, +# windows32_build, + ] + runs-on: ubuntu-latest + steps: + - name: Delete current release assets + uses: andreaswilli/delete-release-assets-action@v2.0.0 + with: + github_token: ${{ secrets.GITHUB_TOKEN }} + tag: Alpha + deleteOnlyFromDrafts: false + + - uses: actions/download-artifact@v3 + with: + name: artifact + path: bin/ + + - name: Display structure of downloaded files + run: ls -R + working-directory: bin + + - name: Tag Repo + uses: richardsimko/update-tag@v1.0.6 + with: + tag_name: Alpha + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + - name: Upload Release + uses: softprops/action-gh-release@v1 + if: ${{ success() }} + with: + tag: Alpha + tag_name: Alpha + files: bin/* + generate_release_notes: true diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..1c4ebb6 --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +.idea +.env +.uuid +dist/ +build/ \ No newline at end of file diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..2a7bffd --- /dev/null +++ b/Dockerfile @@ -0,0 +1,11 @@ +FROM python:3-slim +MAINTAINER LittleJake https://github.com/LittleJake/ + +WORKDIR /usr/src/app + +COPY requirements.txt ./ +RUN pip install --no-cache-dir -r requirements.txt + +COPY . . + +CMD [ "python", "./report.py" ] diff --git a/README.md b/README.md index f951220..443849f 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,71 @@ -# server-monitor-script server-monitor-script +======= + +Apache 2.0 GitHub Repo stars + + + +[Server Monitor](https://github.com/LittleJake/server-monitor/)的探针节点脚本,支持多种系统。 + +数据上报至Redis服务器,Linux、Windows已通过测试。 + +可注册使用免费Redis服务器:[redislab](https://redis.com/)、[aiven.io](https://console.aiven.io/)。 + +### 安装 + + +#### Docker + +```bash +git clone https://github.com/LittleJake/server-monitor-script/ + +# 编辑.env.example文件保存为.env文件 +cp .env.example .env +vim .env + +docker build -t server-monitor-script:latest ./ +docker run -v /:/rootfs:ro --name monitor -d server-monitor-script:latest + +``` + +#### Linux + +```bash +git clone https://github.com/LittleJake/server-monitor-script/ + +pip3 install -r requirements.txt + +# 编辑.env.example文件保存为.env文件 +cp .env.example .env +vim .env + +# 安装服务CentOS/Debian/Ubuntu +bash install_service_linux.sh + +``` + +#### Windows + +```cmd +git clone https://github.com/LittleJake/server-monitor-script/ + +pip3 install -r requirements.txt +# 编辑.env.example文件保存为.env文件 +copy .env.example .env +notepad .env + +# 运行服务 +python3 report.py + +``` + + +### Sponsors + +Thanks for the amazing VM server provided by [DartNode](https://dartnode.com?via=1). + + + +Thanks for the open source project license provided by [JetBrains](https://www.jetbrains.com/). + + JetBrains Logo (Main) logo. diff --git a/build-script/build.release.sh b/build-script/build.release.sh new file mode 100644 index 0000000..dfcacf5 --- /dev/null +++ b/build-script/build.release.sh @@ -0,0 +1,17 @@ +#!/bin/bash +set -xe + +apt install -y gcc python3-dev python3-psutil python3-dotenv python3-wheel g++ python3-setuptools + +pip3 install -r requirements.txt + +pip3 install pyinstaller + +pyinstaller --onefile report.py + +cd dist +chmod +rx report +chmod +r ./* +cd .. +mv .env.example dist/ +mv dist monitor diff --git a/install_service_linux.sh b/install_service_linux.sh new file mode 100644 index 0000000..bbb6fb6 --- /dev/null +++ b/install_service_linux.sh @@ -0,0 +1,55 @@ +#!/bin/bash + +case `uname` in + Linux ) + LINUX=1 + which apk && { + echo "Alpine" + mkdir -p /usr/local/monitor + \cp -f .env /usr/local/monitor + \cp -f report.py /usr/local/monitor + + cat > /etc/local.d/monitor.start << EOF +cd /usr/local/monitor/ +nohup /usr/bin/python3 /usr/local/monitor/report.py & + +EOF + + chmod +x /etc/local.d/monitor.start + rc-update add local + rc-service local start + } + (which yum || which apt-get) && { + echo "CentOS or Debian" + mkdir -p /usr/local/monitor + \cp -f report.py /usr/local/monitor + \cp -f .env /usr/local/monitor + + cat > /lib/systemd/system/monitor.service << EOF +[Unit] +Description=server monitor +After=network.target + +[Service] +User=root +ExecStart=/usr/bin/python3 /usr/local/monitor/report.py +WorkingDirectory=/usr/local/monitor/ +Restart=always + +[Install] +WantedBy=multi-user.target + +EOF + systemctl daemon-reload + systemctl start monitor + systemctl enable monitor + } + ;; + Darwin ) + DARWIN=1 + ;; + * ) + # Handle AmigaOS, CPM, and modified cable modems. + ;; +esac + diff --git a/report.py b/report.py new file mode 100644 index 0000000..ef8c838 --- /dev/null +++ b/report.py @@ -0,0 +1,388 @@ +import sys +import json +import math +import re +import redis +import logging +import time +import requests +import psutil +import uuid +import os +import cpuinfo +import distro +import platform +from datetime import timedelta +from dotenv import load_dotenv, find_dotenv + +# get .env location for pyinstaller +extDataDir = os.getcwd() +if getattr(sys, 'frozen', False): + extDataDir = sys._MEIPASS +load_dotenv(dotenv_path=os.path.join(extDataDir, '.env')) + +# get .env +DEBUG_LEVEL = os.getenv("DEBUG_LEVEL", '20') +HOST = os.getenv("HOST", "127.0.0.1") +PORT = os.getenv("PORT", "6379") +PASSWORD = os.getenv("PASSWORD", "") +SSL = os.getenv("SSL", 'False').lower() in ('true', '1', 't') +IPV4_API = os.getenv('IPV4_API', "http://v4.ipv6-test.com/api/myip.php") +IPV6_API = os.getenv('IPV6_API', "http://v6.ipv6-test.com/api/myip.php") +IP_API = os.getenv('IP_API', "http://ip-api.com/json?fields=country,countryCode") +REPORT_TIME = int(os.getenv('REPORT_TIME', '60')) +DATA_TIMEOUT = int(os.getenv('DATA_TIMEOUT', '259200')) +RETENTION_TIME = int(os.getenv('RETENTION_TIME', '86400')) +DISK_EXCLUDE = os.getenv('DISK_EXCLUDE','/run,/sys,/boot,/dev,/proc,/var/lib').split(",") +DISK_FS_EXCLUDE = os.getenv('DISK_FS_EXCLUDE', 'tmpfs,overlay').split(",") +DISK_OPTS_EXCLUDE = os.getenv('DISK_OPTS_EXCLUDE', 'ro').split(",") +PROCFS_PATH = os.getenv('PROCFS_PATH', '/proc') +SERVER_URL = os.getenv('SERVER_URL', "") +REPORT_MODE = os.getenv('REPORT_MODE', "redis").lower() +SERVER_TOKEN = os.getenv('SERVER_TOKEN', "") +SOCKET_TIMEOUT = int(os.getenv('SOCKET_TIMEOUT', "10")) + + +logging.basicConfig(level=int(DEBUG_LEVEL), format="%(asctime)s - %(message)s") +UUID = str(uuid.uuid4()).replace("-", "") +if os.path.isfile('.uuid'): + with open('.uuid', 'r') as fp: + UUID = fp.read().strip() +else: + with open('.uuid', 'w') as fp: + fp.write(UUID) + +logging.info("Your UUID is: %s" % UUID) +SERVER_URL_INFO = "%s/api/report/info/%s" % (SERVER_URL, UUID) +SERVER_URL_COLLECTION = "%s/api/report/collection/%s" % (SERVER_URL, UUID) +SERVER_URL_HASH = "%s/api/report/hash/%s" % (SERVER_URL, UUID) + +IPV4 = None +IPV6 = None +COUNTRY = None +TIME = math.floor(time.time()) +psutil.PROCFS_PATH = PROCFS_PATH + +if REPORT_MODE == "redis": + conn = redis.Redis(host=HOST, password=PASSWORD, port=PORT, ssl=SSL, retry_on_timeout=SOCKET_TIMEOUT) + +def net_io_counters(): + try: + return psutil.net_io_counters() + except Exception as e: + logging.error(e) + return None + + +def disk_io_counters(): + try: + return psutil.disk_io_counters() + except Exception as e: + logging.error(e) + return None + + +def get_network(): + global NET_FORMER + if NET_FORMER is None: return {} + + net_temp = net_io_counters() + + network = {'RX': { + 'bytes': (net_temp.bytes_recv - NET_FORMER.bytes_recv) if (net_temp.bytes_recv - NET_FORMER.bytes_recv) > 0 else 0, + 'packets': (net_temp.packets_recv - NET_FORMER.packets_recv) if (net_temp.packets_recv - NET_FORMER.packets_recv) > 0 else 0, + }, 'TX': { + 'bytes': (net_temp.bytes_sent - NET_FORMER.bytes_sent) if (net_temp.bytes_sent - NET_FORMER.bytes_sent) > 0 else 0, + 'packets': (net_temp.packets_sent - NET_FORMER.packets_sent) if (net_temp.packets_sent - NET_FORMER.packets_sent) > 0 else 0, + }} + + NET_FORMER = net_temp + return network + + +def get_io(): + global IO_FORMER + if IO_FORMER is None: return {} + + io_temp = psutil.disk_io_counters() + + io = {'read': { + 'count': (io_temp.read_count - IO_FORMER.read_count) if (io_temp.read_count - IO_FORMER.read_count) > 0 else 0, + 'bytes': (io_temp.read_bytes - IO_FORMER.read_bytes) if (io_temp.read_bytes - IO_FORMER.read_bytes) > 0 else 0, + 'time': (io_temp.read_time - IO_FORMER.read_time) if (io_temp.read_time - IO_FORMER.read_time) > 0 else 0, + }, 'write': { + 'count': (io_temp.write_count - IO_FORMER.write_count) if (io_temp.write_count - IO_FORMER.write_count) > 0 else 0, + 'bytes': (io_temp.write_bytes - IO_FORMER.write_bytes) if (io_temp.write_bytes - IO_FORMER.write_bytes) > 0 else 0, + 'time': (io_temp.write_time - IO_FORMER.write_time) if (io_temp.write_time - IO_FORMER.write_time) > 0 else 0, + }} + + IO_FORMER = io_temp + return io + +def get_process_num(): + return len(psutil.pids()) + + +def get_cpu_name(): + return CPU_INFO.get('brand_raw', CPU_INFO.get('arch_string_raw', 'Unknown')) + +def get_load_average(): + try: + avg = psutil.getloadavg() + except: return "Not support" + return "%.2f, %.2f, %.2f" % avg + +def get_cpu_core(): + return str(psutil.cpu_count()) + + +def get_temp(): + # thermal temp + result = {} + try: + for sensor_type, sensors in psutil.sensors_temperatures().items(): + for sensor in sensors: + result[sensor_type+":"+sensor.label] = sensor.current + except: + pass + return result + +def get_battery(): + # battery temp + result = {} + try: + result["percent"] = psutil.sensors_battery().percent + except: + pass + return result + +def get_fan(): + result = {} + try: + for sensor_type, sensors in psutil.sensors_fans().items(): + for sensor in sensors: + result[sensor_type+":"+sensor.label] = sensor.current + except: + pass + return result + +def get_mem_info(): + info = {'Mem': { + 'total': '%.2f' % (psutil.virtual_memory().total*1.0/1048576), + 'used': '%.2f' % (psutil.virtual_memory().used*1.0/1048576), + 'free': '%.2f' % (psutil.virtual_memory().free*1.0/1048576), + 'percent': psutil.virtual_memory().percent, + }, 'Swap': { + 'total': '%.2f' % (psutil.swap_memory().total*1.0/1048576), + 'used': '%.2f' % (psutil.swap_memory().used*1.0/1048576), + 'free': '%.2f' % (psutil.swap_memory().free*1.0/1048576), + 'percent': psutil.swap_memory().percent, + }} + return info + + +def get_sys_version(): + # System and version''' + if distro.name() != "": + return " ".join([distro.name(), distro.version(), distro.codename()]) + else: + return " ".join([platform.system(), platform.release()]) + + +def get_disk_partitions(): + parts = psutil.disk_partitions(True) + result = [] + for part in parts: + try: + result.append(part) + for i in DISK_EXCLUDE: + if part.mountpoint.find(i) != -1 or part.fstype in DISK_FS_EXCLUDE or len(set(part.opts.split(",")) & set(DISK_OPTS_EXCLUDE)) > 0: + result.remove(part) + break + except: result.remove(part) + + return result + + +def get_disk_info(): + disks = {} + + for partition in get_disk_partitions(): + try: + disk = psutil.disk_usage(partition.mountpoint) + disks[partition.mountpoint] = { + 'total': '%.2f' % (disk.total*1.0/1048576), + 'used': '%.2f' % (disk.used*1.0/1048576), + 'free': '%.2f' % (disk.free*1.0/1048576), + 'percent': disk.percent + } + except Exception as e: logging.error(e) + + return disks + + +def get_request(url=''): + i = 5 + while i > 0: + try: + resp = requests.get(url=url, timeout=5) + if resp.status_code == 200: + return resp + except: + i = i - 1 + + return None + +def get_ipv4(): + # interface ipv4 + global IPV4 + if IPV4 is None: + resp = get_request(IPV4_API) + if resp is not None and re.match("[0-9]*\\.[0-9]*\\.[0-9]*",resp.text) is not None: + IPV4 = resp.text + else: + IPV4 = "None" + + return IPV4 + + +def get_ipv6(): + # interface ipv6 + global IPV6 + if IPV6 is None: + resp = get_request(IPV6_API) + if resp is not None and re.match("[a-zA-Z0-9]*:",resp.text) is not None: + IPV6 = resp.text + else: + IPV6 = "None" + + return IPV6 + + +def get_country(): + global COUNTRY + if COUNTRY is None: + resp = get_request(IP_API) + if resp is not None: + j = resp.json() + COUNTRY = (j["country"], j["countryCode"]) + return COUNTRY + + return ("Unknown", "Unknown") + else: + return COUNTRY + + +def get_connections(): + return "TCP: %d, UDP: %d" % (len(psutil.net_connections("tcp")), len(psutil.net_connections("udp"))) + +def get_throughput(): + rx = NET_FORMER.bytes_recv/1073741824 + tx = NET_FORMER.bytes_sent/1073741824 + + return "{} / {}".format("↓%.2f TB" % (rx/1024) if rx > 1024 else "↓%.2f GB" % rx, + "↑%.2f TB" % (tx/1024) if tx > 1024 else "↑%.2f GB" % tx) + +def get_uptime(): + t = int(time.time() - psutil.boot_time()) + delta = timedelta(seconds=t) + return str(delta) + + +def get_load(): + return dict(psutil.cpu_times_percent()._asdict()) + +def get_aggregate_stat_json(): + return json.dumps(get_aggregate_stat()) + + +def get_aggregate_stat(): + info = { + 'Disk': get_disk_info(), + 'Memory': get_mem_info(), + 'Load': get_load(), + 'Network': get_network(), + 'Thermal': get_temp(), + 'Battery': get_battery(), + 'Fan': get_fan(), + 'IO': get_io(), + } + logging.debug(info) + return info + + +def report_once(): + """ip""" + global IP, TIME + logging.info("Reporting...") + IP = get_ipv4() + TIME = time.time() + COUNTRY = get_country() + logging.debug("{}x {}".format(get_cpu_core(), get_cpu_name())) + logging.debug(get_sys_version()) + logging.debug(re.sub("[0-9]*\\.[0-9]*\\.[0-9]*", "*.*.*", get_ipv4())) + logging.debug(re.sub("[a-zA-Z0-9]*:", "*:", get_ipv6())) + logging.debug(get_uptime()) + logging.debug(get_connections()) + logging.debug(get_process_num()) + logging.debug(get_load_average()) + logging.debug(TIME) + logging.debug(COUNTRY[0]) + logging.debug(COUNTRY[1]) + logging.debug(get_throughput()) + + info = { + "CPU": "{}x {}".format(get_cpu_core(), get_cpu_name()), + "System Version": get_sys_version(), + "IPV4": re.sub("[0-9]*\\.[0-9]*\\.[0-9]*", "*.*.*", get_ipv4()), + "IPV6": re.sub("[a-zA-Z0-9]*:", "*:", get_ipv6()), + 'Uptime': get_uptime(), + 'Connection': get_connections(), + 'Process': get_process_num(), + 'Load Average': get_load_average(), + "Update Time": TIME, + "Country": COUNTRY[0], + "Country Code": "CN" if COUNTRY[1] in ("TW", "HK", "MO") else COUNTRY[1], + "Throughput": get_throughput(), + } + + if REPORT_MODE == 'redis': + with conn.pipeline(transaction=False) as pipeline: + pipeline.hset(name="system_monitor:hashes", mapping={UUID: IP}) + pipeline.hset(name="system_monitor:info:" + UUID, mapping=info) + pipeline.zadd("system_monitor:collection:" + UUID, {get_aggregate_stat_json(): TIME}) + + pipeline.zremrangebyscore("system_monitor:collection:" + UUID, 0, TIME - RETENTION_TIME) + pipeline.expire("system_monitor:hashes", DATA_TIMEOUT) + pipeline.expire("system_monitor:info:" + UUID, DATA_TIMEOUT) + pipeline.expire("system_monitor:collection:" + UUID, DATA_TIMEOUT) + pipeline.execute() + + elif REPORT_MODE == 'http': + try: + if SERVER_TOKEN == "": + raise Exception("Please generate server token using `php think token add --uuid %s` on your central server." % UUID) + req = requests.post(url=SERVER_URL_HASH, data={'ip': IPV4}, headers={'authorization': SERVER_TOKEN}, timeout=SOCKET_TIMEOUT) + if req.status_code != 200: raise Exception(req) + req = requests.post(url=SERVER_URL_INFO, json=info, headers={'authorization': SERVER_TOKEN}, timeout=SOCKET_TIMEOUT) + if req.status_code != 200: raise Exception(req) + req = requests.post(url=SERVER_URL_COLLECTION, json=get_aggregate_stat(), headers={'authorization': SERVER_TOKEN}, timeout=SOCKET_TIMEOUT) + if req.status_code != 200: raise Exception(req) + except Exception as e: + raise Exception("[HTTP%d]: %s, %s" % (e.args[0].status_code, e.args[0].text, e.args[0].url)) + + logging.info("Finish Reporting!") + + + +NET_FORMER = net_io_counters() +IO_FORMER = disk_io_counters() +CPU_INFO = cpuinfo.get_cpu_info() + +while True: + try: + report_once() + except Exception as e: + logging.error(e) + logging.error("ERROR OCCUR.") + time.sleep(REPORT_TIME) diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..8b8f500 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,8 @@ +py-cpuinfo +distro +psutil +requests +redis +python-dotenv +urllib3>=2.2.2 # not directly required, pinned by Snyk to avoid a vulnerability +zipp>=3.19.1 # not directly required, pinned by Snyk to avoid a vulnerability \ No newline at end of file