Compare commits

..
2 Commits
6 changed files with 60 additions and 13 deletions
+1
View File
@@ -2,3 +2,4 @@ __pycache__/
*.pyc
dist/
.DS_Store
.data/
+13 -5
View File
@@ -1,6 +1,6 @@
# PIGWay Agent Console Plugin
# PIGWay Raspberry Pi Console Plugin
独立维护的树莓派接入插件:连接现有 PIGWay Device Agent,不在树莓派安装新进程。设备 API、状态转换、配置字段、服务/进程监控、硬件界面及其翻译均由本仓库维护。
独立维护的树莓派接入插件:连接现有 PIGWay Raspberry Pi Agent,不在树莓派安装新进程。设备 API、状态转换、配置字段、服务/进程监控、硬件界面及其翻译均由本仓库维护。
依赖 Python 标准库;接入 Console JSON-RPC v1 与可信前端扩展 `console-ui-v1`。插件 ID 保持 `pigway-agent`,已有设备可直接继续使用。
@@ -8,11 +8,19 @@
```sh
mkdir -p dist
python3 package.py plugin dist/pigway-agent-1.0.0.zip
python3 package.py plugin dist/pigway-agent-1.1.0.zip
```
在控制中心的机器管理页面安装 ZIP,再启用。开发时可用 `PIGWAY_ADAPTER_PATH=/absolute/path/pigway-console-adapter-agent/plugin` 加载源码;Docker 推荐使用持久化数据卷中的安装包。
在控制中心的机器管理页面安装 ZIP,再启用。开发时可用 `PIGWAY_ADAPTER_PATH=/absolute/path/pigway-console-adapter-raspberry-pi/plugin` 加载源码;Docker 推荐使用持久化数据卷中的安装包。
包根目录只有 manifest、worker 与 ui;打包工具、文档和 Git 元数据不进入插件包。前端扩展通过 Console 的共享状态、请求、翻译与基础显示函数接入,不注入额外安装步骤。只应安装可信插件:前端模块与主页面共享 JavaScript 执行环境,加载失败、同步异常及 Promise 拒绝可降级,但不能隔离死循环或恶意代码;后端独立进程的隔离也不是安全沙箱。
正式仓库:https://tea.pigway.com/way/pigway-console-adapter-agent 。首次独立版本为 v1.0.0,需要 Console v2.0.0。与 Console 的集成回归使用后者 tests 下的模拟 Agent,不操作真实设备。
正式仓库:https://tea.pigway.com/way/pigway-console-adapter-raspberry-pi 。首次独立版本为 v1.0.0,需要 Console v2.0.0。与 Console 的集成回归使用后者 tests 下的模拟 Agent,不操作真实设备。
## Naming and compatibility
This project targets Raspberry Pi. Other Linux platforms are not covered by its support claim and may have separately maintained adapters. Repository naming does not change the existing `pigway-agent` adapter ID or `pigway-pi-control` service, configuration and API identifiers.
## v1.1.0 历史指标
将现有 Agent API 的内存/磁盘字节数、开机时长与链路速度补充到标准 metrics,Console v2.1.0 可直接统一记录历史。不需要更新或新增树莓派进程。现有设备端没有上传下载流量计数,因此本版没有实际流量曲线;链路速度仅代表协商速率。
+5 -4
View File
@@ -1,7 +1,7 @@
7d856a7ec552a841097cd14f2f3e357efcd05c13f70eb98a415789e615f6fbf2 .gitignore
f577e72ab33e2cc2dcb2fde5f598adab9dd0ae0602cef517c8281eb39bf00eb6 README.md
3cfd361f7663a9a9c93b507d17bb1c7e98003d61316826f0a4ba4a93594f72d4 .gitignore
2a476aea1b4fcaf43ace0a7f297e4f5c3cd5014b79992bb5bedf84faa194071f README.md
d081c599d821149853368b1a8637c57a16995344406ff5ec9d7619b66e0479b3 package.py
b7c560b9f529ef8dcdf74f1bc74a36745fc005c2432ff80325e1c9e87573e7b2 plugin/manifest.json
cf40d1302a8398497e4d6724ec0a22b24420e32ed2697ba3f4d039d193cf38ea plugin/manifest.json
e5c04001dbd8ed8bba614f7b341237fc6487a9a8fe2f28ccb6f59278725b0a69 plugin/ui/config.js
c98f34119436e3020ef20b325f91617943aefe662119cf76738e0226c2e91cbb plugin/ui/device.js
89b9d7994e7477e18c3f43e6f0975e04d5106056ba581b2964db0f39183d6f8a plugin/ui/hardware.js
@@ -11,4 +11,5 @@ fadb6bf24d6fcf569fc4157847a3585a1008aad99ce5a0e2b87c9c0658f6d225 plugin/ui/over
5d967b5d4e62eed7022bc04fe29e4ed670f2e288cf20adae0c25233f94a29079 plugin/ui/services.js
56e67d2839cbb865133e3b5f796a07f36595c0f6c30480f8e2fac79e7d51e03f plugin/ui/styles.css
0b79b9c5b5f23727164e05b127caca72255416309166ba200ccbd15a9667473c plugin/ui/translations.js
c12c7b8a3c2fdb5eaef8ca535fcae04d960d4c89c86740529166f3e25cd8c7c9 plugin/worker.py
0c37e4604a30c13923b45316d21c841164c48e8b632c3a3f8febd778b434c2f8 plugin/worker.py
9ffd557bb01ccda24970af1cfc5ec395721d5b1e613bfc013feb27d074582b7e tests/test_metrics.py
+2 -2
View File
@@ -1,7 +1,7 @@
{
"id": "pigway-agent",
"name": "PIGWay Agent",
"version": "1.0.0",
"name": "PIGWay Raspberry Pi Agent",
"version": "1.1.0",
"protocol_version": 1,
"entrypoint": "worker.py",
"runtime": "python3",
+10 -2
View File
@@ -1,5 +1,5 @@
#!/usr/bin/env python3
"""Official Agent adapter. This process alone knows Agent endpoints/authentication."""
"""Official Raspberry Pi adapter. This process alone knows Agent endpoints/authentication."""
import concurrent.futures
import json
import sys
@@ -61,11 +61,19 @@ def collect(device):
raise AdapterError('INVALID_RESPONSE')
metrics = []
units = {'cpu_percent': 'percent', 'memory_percent': 'percent', 'disk_percent': 'percent',
'temperature_c': 'celsius', 'cpu_freq_mhz': 'megahertz'}
'temperature_c': 'celsius', 'cpu_freq_mhz': 'megahertz',
'memory_total_bytes': 'bytes', 'memory_used_bytes': 'bytes',
'disk_total_bytes': 'bytes', 'disk_used_bytes': 'bytes'}
for key, unit in units.items():
number = value['system'].get(key)
if isinstance(number, (int, float)) and not isinstance(number, bool):
metrics.append({'resource_id': 'host', 'key': key, 'value': number, 'unit': unit})
uptime = value.get('uptime_seconds')
if isinstance(uptime, (int, float)) and not isinstance(uptime, bool):
metrics.append({'resource_id': 'host', 'key': 'uptime_seconds', 'value': uptime, 'unit': 'seconds'})
speed = value.get('network', {}).get('link_speed_mbps')
if isinstance(speed, (int, float)) and not isinstance(speed, bool):
metrics.append({'resource_id': 'host', 'key': 'network_link_speed_mbps', 'value': speed, 'unit': 'megabits_per_second'})
return {'schema_version': 1, 'observed_at': value.get('timestamp'),
'identity': {'reported_name': value['machine'].get('name'), 'category': 'host',
'details': value.get('device', {})},
+29
View File
@@ -0,0 +1,29 @@
import sys
import unittest
from pathlib import Path
from unittest.mock import patch
sys.path.insert(0,str(Path(__file__).resolve().parents[1]/'plugin'))
import worker
class MetricsTests(unittest.TestCase):
def test_existing_agent_fields_become_standard_history_metrics(self):
value = {'timestamp': 123, 'machine': {'name':'Pi'}, 'system': {
'cpu_percent': 10, 'memory_total_bytes': 8000, 'memory_used_bytes': 4000,
'disk_total_bytes': 16000, 'disk_used_bytes': 5000},
'uptime_seconds': 1000, 'network': {'link_speed_mbps':1000},
'display': {'alerts': []}}
with patch.object(worker,'request',return_value=value): result=worker.collect({})
values = {m['key']:m['value'] for m in result['metrics']}
self.assertEqual(values['memory_used_bytes'],4000)
self.assertEqual(values['uptime_seconds'],1000)
self.assertEqual(values['network_link_speed_mbps'],1000)
self.assertNotIn('network_rx_bytes_per_second',values)
self.assertTrue(result['alerts_complete'])
def test_absent_or_boolean_fields_not_invented(self):
value={'system': {'cpu_percent': True}, 'machine':{}, 'network': {'link_speed_mbps': None}}
with patch.object(worker,'request',return_value=value): result=worker.collect({})
self.assertEqual(result['metrics'],[])
if __name__=='__main__': unittest.main()