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
+=======
+
+
+
+
+
+[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/).
+
+
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