refactor!: rename the project from Monitor to MoShiro
Namespaces, assemblies, projects and directories, the solution, binaries, images, systemd units, config paths, environment variables, protocol method names, the media type and the raw chunk magic all move to MoShiro/moshiro. Existing data and deployments are not compatible and must be recreated. Also fix the architecture tests after the solution layout change and make the update tests independent of their order.
This commit is contained in:
1 parent
17b35223aa
commit
83e5120377
309 files changed
+1505
-1499
No files matched your search
@@ -23,4 +23,4 @@ steps:
|
|||||||
environment:
|
environment:
|
||||||
NUGET_PACKAGES: /home/justme/.nuget/packages
|
NUGET_PACKAGES: /home/justme/.nuget/packages
|
||||||
commands:
|
commands:
|
||||||
- cd backend && dotnet test --solution Monitor.slnx -c Release
|
- cd backend && dotnet test --solution MoShiro.slnx -c Release
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
## Project
|
## Project
|
||||||
|
|
||||||
Monitor is a self-hosted network path quality monitor. Backend coordinates Agents and Probes, stores measurements, and serves the REST/SSE API. `web/display` is the read-only overview; `web/admin` manages nodes, tests, and settings.
|
MoShiro is a self-hosted network path quality monitor. Backend coordinates Agents and Probes, stores measurements, and serves the REST/SSE API. `web/display` is the read-only overview; `web/admin` manages nodes, tests, and settings.
|
||||||
|
|
||||||
## Branch and workspace safety
|
## Branch and workspace safety
|
||||||
|
|
||||||
@@ -10,12 +10,12 @@ Monitor is a self-hosted network path quality monitor. Backend coordinates Agent
|
|||||||
- A push to `main` builds images only if its commit message contains `[CI]` (for a merged PR, put it in the PR or merge commit title). Without `[CI]` nothing is built; use `[CI SKIP]` to skip the pipeline entirely. When squashing or pushing to `main`, add `[CI]` unless a build is explicitly not wanted.
|
- A push to `main` builds images only if its commit message contains `[CI]` (for a merged PR, put it in the PR or merge commit title). Without `[CI]` nothing is built; use `[CI SKIP]` to skip the pipeline entirely. When squashing or pushing to `main`, add `[CI]` unless a build is explicitly not wanted.
|
||||||
- Before editing, inspect `git status`. Preserve pre-existing user changes: do not reset, clean, stash, or include unrelated files in a commit.
|
- Before editing, inspect `git status`. Preserve pre-existing user changes: do not reset, clean, stash, or include unrelated files in a commit.
|
||||||
- Do not commit credentials, API keys, registry passwords, or generated runtime data. CI credentials belong in Woodpecker secrets (`zot_secret`, `gitea_token`, `release_signing_key` for signing release manifests, `release_api_token` with the `release` scope). The release signing private key never goes into the repository or the panel; only the public key in `packaging/update/` does.
|
- Do not commit credentials, API keys, registry passwords, or generated runtime data. CI credentials belong in Woodpecker secrets (`zot_secret`, `gitea_token`, `release_signing_key` for signing release manifests, `release_api_token` with the `release` scope). The release signing private key never goes into the repository or the panel; only the public key in `packaging/update/` does.
|
||||||
- Keep development OCI images in the `monitor-dev/` repository path and release images in `monitor/`. Do not let a development build overwrite release tags such as `latest`.
|
- Keep development OCI images in the `moshiro-dev/` repository path and release images in `moshiro/`. Do not let a development build overwrite release tags such as `latest`.
|
||||||
|
|
||||||
## Project layout
|
## Project layout
|
||||||
|
|
||||||
- `backend/src/Monitor.Backend/`: service entry point and composition.
|
- `backend/src/MoShiro.Backend/`: service entry point and composition.
|
||||||
- `backend/src/Monitor.Backend.Api/`: REST endpoints, contracts, and OpenAPI.
|
- `backend/src/MoShiro.Backend.Api/`: REST endpoints, contracts, and OpenAPI.
|
||||||
- `backend/agent/`, `backend/probe/`: node applications and their READMEs.
|
- `backend/agent/`, `backend/probe/`: node applications and their READMEs.
|
||||||
- `backend/fleet/`, `backend/shared/`: reusable node framework, measurement engine, protocols, and data formats.
|
- `backend/fleet/`, `backend/shared/`: reusable node framework, measurement engine, protocols, and data formats.
|
||||||
- `web/display/`, `web/admin/`: Vite applications; `web/shared/`: API client, generated types, charts, and shared UI.
|
- `web/display/`, `web/admin/`: Vite applications; `web/shared/`: API client, generated types, charts, and shared UI.
|
||||||
@@ -25,12 +25,12 @@ Monitor is a self-hosted network path quality monitor. Backend coordinates Agent
|
|||||||
|
|
||||||
Requirements: .NET SDK 10 and Node.js/npm. The SDK selection is defined by the root `global.json`; NuGet package versions are centralized in the root `Directory.Packages.props`.
|
Requirements: .NET SDK 10 and Node.js/npm. The SDK selection is defined by the root `global.json`; NuGet package versions are centralized in the root `Directory.Packages.props`.
|
||||||
|
|
||||||
- All .NET projects: `dotnet build Monitor.slnx -c Release` from the repository root.
|
- All .NET projects: `dotnet build MoShiro.slnx -c Release` from the repository root.
|
||||||
- Backend build: `cd backend && dotnet build Monitor.slnx -c Release`
|
- Backend build: `cd backend && dotnet build MoShiro.slnx -c Release`
|
||||||
- Backend tests: `cd backend && dotnet test --solution Monitor.slnx -c Release`
|
- Backend tests: `cd backend && dotnet test --solution MoShiro.slnx -c Release`
|
||||||
- Frontend dependencies/build: `npm ci --prefix web && npm run build --prefix web`
|
- Frontend dependencies/build: `npm ci --prefix web && npm run build --prefix web`
|
||||||
- Frontend style check: `npm run lint:css --prefix web`
|
- Frontend style check: `npm run lint:css --prefix web`
|
||||||
- Local backend: from `backend/`, run `dotnet run --project src/Monitor.Backend -- --urls http://localhost:5000`.
|
- Local backend: from `backend/`, run `dotnet run --project src/MoShiro.Backend -- --urls http://localhost:5000`.
|
||||||
- Local web apps: from the repository root, run `npm ci --prefix web` then `npm run dev --prefix web`. The current dev config uses display port 5173 and admin port 5175; `/admin/` is proxied through the display server. Check the Vite configs when working on another branch because ports can differ.
|
- Local web apps: from the repository root, run `npm ci --prefix web` then `npm run dev --prefix web`. The current dev config uses display port 5173 and admin port 5175; `/admin/` is proxied through the display server. Check the Vite configs when working on another branch because ports can differ.
|
||||||
- After changing API contracts, regenerate/check `backend/openapi/v1.json` and run `npm run gen:api --prefix web` so frontend API types stay aligned.
|
- After changing API contracts, regenerate/check `backend/openapi/v1.json` and run `npm run gen:api --prefix web` so frontend API types stay aligned.
|
||||||
|
|
||||||
|
|||||||
@@ -2,9 +2,9 @@
|
|||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
|
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
|
||||||
<!-- Native SQLite source is pinned separately from the JIT/test binary package. -->
|
<!-- Native SQLite source is pinned separately from the JIT/test binary package. -->
|
||||||
<MonitorSqliteSourceVersion>3530400</MonitorSqliteSourceVersion>
|
<MoShiroSqliteSourceVersion>3530400</MoShiroSqliteSourceVersion>
|
||||||
<MonitorSqliteSourceYear>2026</MonitorSqliteSourceYear>
|
<MoShiroSqliteSourceYear>2026</MoShiroSqliteSourceYear>
|
||||||
<MonitorSqliteSourceSha256>1e71ddf93849c6a6ecf58b827c0692073d2dd7ee40196158068f7b29f422e87d</MonitorSqliteSourceSha256>
|
<MoShiroSqliteSourceSha256>1e71ddf93849c6a6ecf58b827c0692073d2dd7ee40196158068f7b29f422e87d</MoShiroSqliteSourceSha256>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageVersion Include="Microsoft.CodeAnalysis.BannedApiAnalyzers" Version="5.6.0" />
|
<PackageVersion Include="Microsoft.CodeAnalysis.BannedApiAnalyzers" Version="5.6.0" />
|
||||||
|
|||||||
+17
-17
@@ -17,36 +17,36 @@
|
|||||||
<Project Path="backend/fleet/tools/fleet-enroll/fleet-enroll.csproj" />
|
<Project Path="backend/fleet/tools/fleet-enroll/fleet-enroll.csproj" />
|
||||||
</Folder>
|
</Folder>
|
||||||
<Folder Name="/backend/shared/">
|
<Folder Name="/backend/shared/">
|
||||||
<Project Path="backend/shared/Monitor.Measurement/Monitor.Measurement.csproj" />
|
<Project Path="backend/shared/MoShiro.Measurement/MoShiro.Measurement.csproj" />
|
||||||
<Project Path="backend/shared/Monitor.Protocol/Monitor.Protocol.csproj" />
|
<Project Path="backend/shared/MoShiro.Protocol/MoShiro.Protocol.csproj" />
|
||||||
</Folder>
|
</Folder>
|
||||||
<Folder Name="/backend/shared/tests/">
|
<Folder Name="/backend/shared/tests/">
|
||||||
<Project Path="backend/shared/tests/Monitor.Measurement.Tests/Monitor.Measurement.Tests.csproj" />
|
<Project Path="backend/shared/tests/MoShiro.Measurement.Tests/MoShiro.Measurement.Tests.csproj" />
|
||||||
<Project Path="backend/shared/tests/Monitor.Protocol.Tests/Monitor.Protocol.Tests.csproj" />
|
<Project Path="backend/shared/tests/MoShiro.Protocol.Tests/MoShiro.Protocol.Tests.csproj" />
|
||||||
</Folder>
|
</Folder>
|
||||||
<Folder Name="/backend/agent/src/">
|
<Folder Name="/backend/agent/src/">
|
||||||
<Project Path="backend/agent/src/Monitor.Agent/Monitor.Agent.csproj" />
|
<Project Path="backend/agent/src/MoShiro.Agent/MoShiro.Agent.csproj" />
|
||||||
</Folder>
|
</Folder>
|
||||||
<Folder Name="/backend/probe/src/">
|
<Folder Name="/backend/probe/src/">
|
||||||
<Project Path="backend/probe/src/Monitor.Probe/Monitor.Probe.csproj" />
|
<Project Path="backend/probe/src/MoShiro.Probe/MoShiro.Probe.csproj" />
|
||||||
</Folder>
|
</Folder>
|
||||||
<Folder Name="/backend/probe/tests/">
|
<Folder Name="/backend/probe/tests/">
|
||||||
<Project Path="backend/probe/tests/Monitor.Probe.Tests/Monitor.Probe.Tests.csproj" />
|
<Project Path="backend/probe/tests/MoShiro.Probe.Tests/MoShiro.Probe.Tests.csproj" />
|
||||||
</Folder>
|
</Folder>
|
||||||
<Folder Name="/backend/src/">
|
<Folder Name="/backend/src/">
|
||||||
<Project Path="backend/src/Monitor.Backend.Analysis/Monitor.Backend.Analysis.csproj" />
|
<Project Path="backend/src/MoShiro.Backend.Analysis/MoShiro.Backend.Analysis.csproj" />
|
||||||
<Project Path="backend/src/Monitor.Backend.Api/Monitor.Backend.Api.csproj" />
|
<Project Path="backend/src/MoShiro.Backend.Api/MoShiro.Backend.Api.csproj" />
|
||||||
<Project Path="backend/src/Monitor.Backend.Application/Monitor.Backend.Application.csproj" />
|
<Project Path="backend/src/MoShiro.Backend.Application/MoShiro.Backend.Application.csproj" />
|
||||||
<Project Path="backend/src/Monitor.Backend.Gateway/Monitor.Backend.Gateway.csproj" />
|
<Project Path="backend/src/MoShiro.Backend.Gateway/MoShiro.Backend.Gateway.csproj" />
|
||||||
<Project Path="backend/src/Monitor.Backend.Storage.Sqlite/Monitor.Backend.Storage.Sqlite.csproj" />
|
<Project Path="backend/src/MoShiro.Backend.Storage.Sqlite/MoShiro.Backend.Storage.Sqlite.csproj" />
|
||||||
<Project Path="backend/src/Monitor.Backend/Monitor.Backend.csproj" />
|
<Project Path="backend/src/MoShiro.Backend/MoShiro.Backend.csproj" />
|
||||||
</Folder>
|
</Folder>
|
||||||
<Folder Name="/backend/tests/">
|
<Folder Name="/backend/tests/">
|
||||||
<Project Path="backend/tests/Monitor.Backend.Analysis.Tests/Monitor.Backend.Analysis.Tests.csproj" />
|
<Project Path="backend/tests/MoShiro.Backend.Analysis.Tests/MoShiro.Backend.Analysis.Tests.csproj" />
|
||||||
<Project Path="backend/tests/Monitor.Backend.Tests/Monitor.Backend.Tests.csproj" />
|
<Project Path="backend/tests/MoShiro.Backend.Tests/MoShiro.Backend.Tests.csproj" />
|
||||||
<Project Path="backend/tests/Monitor.TestSupport/Monitor.TestSupport.csproj" />
|
<Project Path="backend/tests/MoShiro.TestSupport/MoShiro.TestSupport.csproj" />
|
||||||
</Folder>
|
</Folder>
|
||||||
<Folder Name="/ci/">
|
<Folder Name="/ci/">
|
||||||
<Project Path="ci/Monitor.Tar/Monitor.Tar.csproj" />
|
<Project Path="ci/MoShiro.Tar/MoShiro.Tar.csproj" />
|
||||||
</Folder>
|
</Folder>
|
||||||
</Solution>
|
</Solution>
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
# Monitor
|
# MoShiro
|
||||||
|
|
||||||
Monitor 是一个自托管的网络线路质量观测工具。它在 Probe 与多个 Agent 之间执行主动网络测量,记录逐包和逐事件结果;Backend 保存原始数据并生成可查询的线路指标,Web 界面用于查看、比较和管理节点。
|
MoShiro 是一个自托管的网络线路质量观测工具。它在 Probe 与多个 Agent 之间执行主动网络测量,记录逐包和逐事件结果;Backend 保存原始数据并生成可查询的线路指标,Web 界面用于查看、比较和管理节点。
|
||||||
|
|
||||||
它面向需要比较不同网络出口、评估线路稳定性,或观察特定网络服务体验的场景。Agent 与 Probe 专注于测量和上报;它们不提供远程终端、命令执行或文件管理能力。
|
它面向需要比较不同网络出口、评估线路稳定性,或观察特定网络服务体验的场景。Agent 与 Probe 专注于测量和上报;它们不提供远程终端、命令执行或文件管理能力。
|
||||||
|
|
||||||
@@ -48,10 +48,10 @@ Monitor 是一个自托管的网络线路质量观测工具。它在 Probe 与
|
|||||||
从仓库根目录构建全部 .NET 项目(Backend、Agent、Probe、Fleet、测试和 CI 工具):
|
从仓库根目录构建全部 .NET 项目(Backend、Agent、Probe、Fleet、测试和 CI 工具):
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
dotnet build Monitor.slnx -c Release
|
dotnet build MoShiro.slnx -c Release
|
||||||
```
|
```
|
||||||
|
|
||||||
NuGet 包版本统一在根目录 `Directory.Packages.props` 管理,项目中的 `PackageReference` 不写版本号。AotAnywhere 的 MSBuild SDK 版本在根目录 `global.json` 的 `msbuild-sdks` 中管理。`backend/Monitor.slnx` 保留用于只构建后端目录内的项目。
|
NuGet 包版本统一在根目录 `Directory.Packages.props` 管理,项目中的 `PackageReference` 不写版本号。AotAnywhere 的 MSBuild SDK 版本在根目录 `global.json` 的 `msbuild-sdks` 中管理。`backend/MoShiro.slnx` 保留用于只构建后端目录内的项目。
|
||||||
|
|
||||||
安装前端依赖并构建展示端和管理端(在仓库根目录运行):
|
安装前端依赖并构建展示端和管理端(在仓库根目录运行):
|
||||||
|
|
||||||
@@ -72,7 +72,7 @@ Backend、Agent 和 Probe 均以 OCI 镜像发布,支持 Linux/amd64 与 Linux
|
|||||||
|
|
||||||
```sh
|
```sh
|
||||||
cd backend
|
cd backend
|
||||||
dotnet run --project src/Monitor.Backend -- --urls http://localhost:5000
|
dotnet run --project src/MoShiro.Backend -- --urls http://localhost:5000
|
||||||
```
|
```
|
||||||
|
|
||||||
2. **启动前端。** 保持 Backend 运行,在另一个终端的仓库根目录运行:
|
2. **启动前端。** 保持 Backend 运行,在另一个终端的仓库根目录运行:
|
||||||
@@ -88,7 +88,7 @@ npm run dev --prefix web
|
|||||||
|
|
||||||
- Pull request 和 `dev` 分支 push:先跑 Backend tests,通过后再构建前端。
|
- Pull request 和 `dev` 分支 push:先跑 Backend tests,通过后再构建前端。
|
||||||
- 在需要跳过 Backend tests 的提交信息后加 `[SKIP TESTS]`;Gitea pull request 需要把标记写进 PR 标题。前端构建仍会运行。
|
- 在需要跳过 Backend tests 的提交信息后加 `[SKIP TESTS]`;Gitea pull request 需要把标记写进 PR 标题。前端构建仍会运行。
|
||||||
- `main` 上的提交信息包含 `[CI]` 时,并行构建 Backend、Agent、Probe 的 Linux/amd64 镜像,推送到 Zot 的 `monitor-dev/` 仓库;普通 main push 不构建。
|
- `main` 上的提交信息包含 `[CI]` 时,并行构建 Backend、Agent、Probe 的 Linux/amd64 镜像,推送到 Zot 的 `moshiro-dev/` 仓库;普通 main push 不构建。
|
||||||
- 推送 `vMAJOR.MINOR.PATCH` tag 会触发全平台正式构建和 Release 发布。
|
- 推送 `vMAJOR.MINOR.PATCH` tag 会触发全平台正式构建和 Release 发布。
|
||||||
|
|
||||||
完整触发条件、开发镜像标签和发布路径见 [`docs/deployment.md`](docs/deployment.md)。
|
完整触发条件、开发镜像标签和发布路径见 [`docs/deployment.md`](docs/deployment.md)。
|
||||||
@@ -120,7 +120,7 @@ npm run dev --prefix web
|
|||||||
## 测试
|
## 测试
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
dotnet test --solution Monitor.slnx
|
dotnet test --solution MoShiro.slnx
|
||||||
```
|
```
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
|
|||||||
@@ -17,33 +17,33 @@
|
|||||||
<Project Path="fleet/tools/fleet-enroll/fleet-enroll.csproj" />
|
<Project Path="fleet/tools/fleet-enroll/fleet-enroll.csproj" />
|
||||||
</Folder>
|
</Folder>
|
||||||
<Folder Name="/shared/">
|
<Folder Name="/shared/">
|
||||||
<Project Path="shared/Monitor.Measurement/Monitor.Measurement.csproj" />
|
<Project Path="shared/MoShiro.Measurement/MoShiro.Measurement.csproj" />
|
||||||
<Project Path="shared/Monitor.Protocol/Monitor.Protocol.csproj" />
|
<Project Path="shared/MoShiro.Protocol/MoShiro.Protocol.csproj" />
|
||||||
</Folder>
|
</Folder>
|
||||||
<Folder Name="/shared/tests/">
|
<Folder Name="/shared/tests/">
|
||||||
<Project Path="shared/tests/Monitor.Measurement.Tests/Monitor.Measurement.Tests.csproj" />
|
<Project Path="shared/tests/MoShiro.Measurement.Tests/MoShiro.Measurement.Tests.csproj" />
|
||||||
<Project Path="shared/tests/Monitor.Protocol.Tests/Monitor.Protocol.Tests.csproj" />
|
<Project Path="shared/tests/MoShiro.Protocol.Tests/MoShiro.Protocol.Tests.csproj" />
|
||||||
</Folder>
|
</Folder>
|
||||||
<Folder Name="/agent/src/">
|
<Folder Name="/agent/src/">
|
||||||
<Project Path="agent/src/Monitor.Agent/Monitor.Agent.csproj" />
|
<Project Path="agent/src/MoShiro.Agent/MoShiro.Agent.csproj" />
|
||||||
</Folder>
|
</Folder>
|
||||||
<Folder Name="/probe/src/">
|
<Folder Name="/probe/src/">
|
||||||
<Project Path="probe/src/Monitor.Probe/Monitor.Probe.csproj" />
|
<Project Path="probe/src/MoShiro.Probe/MoShiro.Probe.csproj" />
|
||||||
</Folder>
|
</Folder>
|
||||||
<Folder Name="/probe/tests/">
|
<Folder Name="/probe/tests/">
|
||||||
<Project Path="probe/tests/Monitor.Probe.Tests/Monitor.Probe.Tests.csproj" />
|
<Project Path="probe/tests/MoShiro.Probe.Tests/MoShiro.Probe.Tests.csproj" />
|
||||||
</Folder>
|
</Folder>
|
||||||
<Folder Name="/backend/src/">
|
<Folder Name="/backend/src/">
|
||||||
<Project Path="src/Monitor.Backend.Analysis/Monitor.Backend.Analysis.csproj" />
|
<Project Path="src/MoShiro.Backend.Analysis/MoShiro.Backend.Analysis.csproj" />
|
||||||
<Project Path="src/Monitor.Backend.Api/Monitor.Backend.Api.csproj" />
|
<Project Path="src/MoShiro.Backend.Api/MoShiro.Backend.Api.csproj" />
|
||||||
<Project Path="src/Monitor.Backend.Application/Monitor.Backend.Application.csproj" />
|
<Project Path="src/MoShiro.Backend.Application/MoShiro.Backend.Application.csproj" />
|
||||||
<Project Path="src/Monitor.Backend.Gateway/Monitor.Backend.Gateway.csproj" />
|
<Project Path="src/MoShiro.Backend.Gateway/MoShiro.Backend.Gateway.csproj" />
|
||||||
<Project Path="src/Monitor.Backend.Storage.Sqlite/Monitor.Backend.Storage.Sqlite.csproj" />
|
<Project Path="src/MoShiro.Backend.Storage.Sqlite/MoShiro.Backend.Storage.Sqlite.csproj" />
|
||||||
<Project Path="src/Monitor.Backend/Monitor.Backend.csproj" />
|
<Project Path="src/MoShiro.Backend/MoShiro.Backend.csproj" />
|
||||||
</Folder>
|
</Folder>
|
||||||
<Folder Name="/backend/tests/">
|
<Folder Name="/backend/tests/">
|
||||||
<Project Path="tests/Monitor.Backend.Analysis.Tests/Monitor.Backend.Analysis.Tests.csproj" />
|
<Project Path="tests/MoShiro.Backend.Analysis.Tests/MoShiro.Backend.Analysis.Tests.csproj" />
|
||||||
<Project Path="tests/Monitor.Backend.Tests/Monitor.Backend.Tests.csproj" />
|
<Project Path="tests/MoShiro.Backend.Tests/MoShiro.Backend.Tests.csproj" />
|
||||||
<Project Path="tests/Monitor.TestSupport/Monitor.TestSupport.csproj" />
|
<Project Path="tests/MoShiro.TestSupport/MoShiro.TestSupport.csproj" />
|
||||||
</Folder>
|
</Folder>
|
||||||
</Solution>
|
</Solution>
|
||||||
+12
-12
@@ -1,17 +1,17 @@
|
|||||||
# Backend
|
# Backend
|
||||||
|
|
||||||
Backend 是 Monitor 的控制面和数据服务,负责 Agent 注册与连接、任务编排、原始测量数据持久化、指标分析,以及供前端使用的 REST API 和 SSE 实时数据流。
|
Backend 是 MoShiro 的控制面和数据服务,负责 Agent 注册与连接、任务编排、原始测量数据持久化、指标分析,以及供前端使用的 REST API 和 SSE 实时数据流。
|
||||||
|
|
||||||
## 组件
|
## 组件
|
||||||
|
|
||||||
| 路径 | 职责 |
|
| 路径 | 职责 |
|
||||||
| --- | --- |
|
| --- | --- |
|
||||||
| `src/Monitor.Backend/` | 应用入口与依赖组合 |
|
| `src/MoShiro.Backend/` | 应用入口与依赖组合 |
|
||||||
| `src/Monitor.Backend.Api/` | Public、Admin API 和 OpenAPI |
|
| `src/MoShiro.Backend.Api/` | Public、Admin API 和 OpenAPI |
|
||||||
| `src/Monitor.Backend.Gateway/` | Fleet RPC、节点状态与测量数据接收 |
|
| `src/MoShiro.Backend.Gateway/` | Fleet RPC、节点状态与测量数据接收 |
|
||||||
| `src/Monitor.Backend.Application/` | 业务用例、查询、任务和后台服务 |
|
| `src/MoShiro.Backend.Application/` | 业务用例、查询、任务和后台服务 |
|
||||||
| `src/Monitor.Backend.Analysis/` | 指标计算、累加器和服务模型 |
|
| `src/MoShiro.Backend.Analysis/` | 指标计算、累加器和服务模型 |
|
||||||
| `src/Monitor.Backend.Storage.Sqlite/` | 段文件与 SQLite 存储 |
|
| `src/MoShiro.Backend.Storage.Sqlite/` | 段文件与 SQLite 存储 |
|
||||||
| `fleet/` | 通用节点身份、注册和连接框架 |
|
| `fleet/` | 通用节点身份、注册和连接框架 |
|
||||||
| `shared/` | 测量引擎、协议和数据格式 |
|
| `shared/` | 测量引擎、协议和数据格式 |
|
||||||
| `tests/` | 集成测试和测试支持代码 |
|
| `tests/` | 集成测试和测试支持代码 |
|
||||||
@@ -26,14 +26,14 @@ Backend 是 Monitor 的控制面和数据服务,负责 Agent 注册与连接
|
|||||||
|
|
||||||
```sh
|
```sh
|
||||||
cd backend
|
cd backend
|
||||||
dotnet build Monitor.slnx -c Release
|
dotnet build MoShiro.slnx -c Release
|
||||||
dotnet test --solution Monitor.slnx
|
dotnet test --solution MoShiro.slnx
|
||||||
```
|
```
|
||||||
|
|
||||||
发布当前平台的 NativeAOT Backend:
|
发布当前平台的 NativeAOT Backend:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
dotnet publish src/Monitor.Backend -c Release -r <RID> -p:PublishAot=true
|
dotnet publish src/MoShiro.Backend -c Release -r <RID> -p:PublishAot=true
|
||||||
```
|
```
|
||||||
|
|
||||||
例如 `<RID>` 可为 `win-x64`、`linux-x64` 或 `linux-arm64`。NativeAOT 发布需要对应平台的 C/C++ 工具链。仓库根目录的 `scripts/publish.sh backend <RID>` 可构建单个平台的 Backend 可执行文件;容器发布由 Woodpecker tag 流水线完成,详见 [`../docs/deployment.md`](../docs/deployment.md)。
|
例如 `<RID>` 可为 `win-x64`、`linux-x64` 或 `linux-arm64`。NativeAOT 发布需要对应平台的 C/C++ 工具链。仓库根目录的 `scripts/publish.sh backend <RID>` 可构建单个平台的 Backend 可执行文件;容器发布由 Woodpecker tag 流水线完成,详见 [`../docs/deployment.md`](../docs/deployment.md)。
|
||||||
@@ -46,9 +46,9 @@ dotnet publish src/Monitor.Backend -c Release -r <RID> -p:PublishAot=true
|
|||||||
|
|
||||||
```sh
|
```sh
|
||||||
cd backend
|
cd backend
|
||||||
dotnet run --project src/Monitor.Backend -- --urls http://localhost:5000
|
dotnet run --project src/MoShiro.Backend -- --urls http://localhost:5000
|
||||||
```
|
```
|
||||||
|
|
||||||
首次启动时可用 `Monitor__InitialAdminUser` 和 `Monitor__InitialAdminPassword` 设置初始管理员。数据目录、公开地址、Cookie 和 CORS 等设置通过 `Monitor` 配置节传入;完整配置说明见 [`../docs/deployment.md`](../docs/deployment.md)。站点默认需要登录,可在管理端修改公开设置。
|
首次启动时可用 `MoShiro__InitialAdminUser` 和 `MoShiro__InitialAdminPassword` 设置初始管理员。数据目录、公开地址、Cookie 和 CORS 等设置通过 `MoShiro` 配置节传入;完整配置说明见 [`../docs/deployment.md`](../docs/deployment.md)。站点默认需要登录,可在管理端修改公开设置。
|
||||||
|
|
||||||
OpenAPI 文档由构建生成到 `openapi/v1.json`。API 设计和端点说明见 [`../docs/api.md`](../docs/api.md);项目总览见仓库根目录的 [`README.md`](../README.md)。
|
OpenAPI 文档由构建生成到 `openapi/v1.json`。API 设计和端点说明见 [`../docs/api.md`](../docs/api.md);项目总览见仓库根目录的 [`README.md`](../README.md)。
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
# Monitor Agent
|
# MoShiro Agent
|
||||||
|
|
||||||
Agent 是部署在 VPS 或其他可达网络节点上的采集进程。它向 Backend 注册并保持连接,接受 Probe 的测量会话,执行线路任务中的 Agent 侧工作,再将两端数据合并后上报。
|
Agent 是部署在 VPS 或其他可达网络节点上的采集进程。它向 Backend 注册并保持连接,接受 Probe 的测量会话,执行线路任务中的 Agent 侧工作,再将两端数据合并后上报。
|
||||||
|
|
||||||
@@ -18,14 +18,14 @@ Probe ──TLS 控制 / UDP、TCP 测量──> Agent ──长连接与数据
|
|||||||
|
|
||||||
```sh
|
```sh
|
||||||
cd backend
|
cd backend
|
||||||
dotnet build agent/src/Monitor.Agent -c Release
|
dotnet build agent/src/MoShiro.Agent -c Release
|
||||||
dotnet build fleet/tools/fleet-enroll -c Release
|
dotnet build fleet/tools/fleet-enroll -c Release
|
||||||
```
|
```
|
||||||
|
|
||||||
发布 NativeAOT 程序需要对应平台的 C/C++ 工具链;`<RID>` 应与构建环境和目标平台匹配:
|
发布 NativeAOT 程序需要对应平台的 C/C++ 工具链;`<RID>` 应与构建环境和目标平台匹配:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
dotnet publish agent/src/Monitor.Agent -c Release -r <RID> -p:PublishAot=true
|
dotnet publish agent/src/MoShiro.Agent -c Release -r <RID> -p:PublishAot=true
|
||||||
dotnet publish fleet/tools/fleet-enroll -c Release -r <RID> -p:PublishAot=true
|
dotnet publish fleet/tools/fleet-enroll -c Release -r <RID> -p:PublishAot=true
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -36,7 +36,7 @@ dotnet publish fleet/tools/fleet-enroll -c Release -r <RID> -p:PublishAot=true
|
|||||||
先在管理端创建加入令牌,然后在 Agent 主机上运行一次性注册工具:
|
先在管理端创建加入令牌,然后在 Agent 主机上运行一次性注册工具:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
fleet-enroll --server https://<backend> --join <token> --name <agent-name> --identity-dir /etc/monitor
|
fleet-enroll --server https://<backend> --join <token> --name <agent-name> --identity-dir /etc/moshiro
|
||||||
```
|
```
|
||||||
|
|
||||||
注册工具会在身份目录写入节点身份和证书。准备本地 `agent.json`,其中 `fleet.server` 指向 Backend,`fleet.identity_dir` 指向上述目录;`listen`、`limits` 和 `reporting` 可按节点环境调整。配置文件不会由 Backend 修改。限制目标网段和测量预算时,应按实际网络与机器能力配置;`allowed_target_cidrs` 默认为空,不允许 Agent 执行目标探测任务。
|
注册工具会在身份目录写入节点身份和证书。准备本地 `agent.json`,其中 `fleet.server` 指向 Backend,`fleet.identity_dir` 指向上述目录;`listen`、`limits` 和 `reporting` 可按节点环境调整。配置文件不会由 Backend 修改。限制目标网段和测量预算时,应按实际网络与机器能力配置;`allowed_target_cidrs` 默认为空,不允许 Agent 执行目标探测任务。
|
||||||
@@ -44,12 +44,12 @@ fleet-enroll --server https://<backend> --join <token> --name <agent-name> --ide
|
|||||||
启动 Agent:
|
启动 Agent:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
monitor-agent --config /etc/monitor/agent.json
|
moshiro-agent --config /etc/moshiro/agent.json
|
||||||
```
|
```
|
||||||
|
|
||||||
容器节点的运行方式(podman + systemd)和宿主机更新器见 [`../../docs/deployment.md`](../../docs/deployment.md)。也可使用 OCI 镜像 `registry.oeo.one/monitor/monitor-agent:<version>`,自动选择 Linux amd64 或 arm64 镜像。镜像同时包含 `fleet-enroll`;可通过 `--entrypoint /usr/local/bin/fleet-enroll` 执行首次注册。容器默认读取命名 volume 中的 `/etc/monitor/agent.json` 与身份文件;建议使用宿主网络,并按 ICMP 测量需求添加 `NET_RAW` 能力。镜像以 UID/GID 1654 运行,新命名 volume 由引擎按镜像目录初始化权限。
|
容器节点的运行方式(podman + systemd)和宿主机更新器见 [`../../docs/deployment.md`](../../docs/deployment.md)。也可使用 OCI 镜像 `registry.oeo.one/moshiro/moshiro-agent:<version>`,自动选择 Linux amd64 或 arm64 镜像。镜像同时包含 `fleet-enroll`;可通过 `--entrypoint /usr/local/bin/fleet-enroll` 执行首次注册。容器默认读取命名 volume 中的 `/etc/moshiro/agent.json` 与身份文件;建议使用宿主网络,并按 ICMP 测量需求添加 `NET_RAW` 能力。镜像以 UID/GID 1654 运行,新命名 volume 由引擎按镜像目录初始化权限。
|
||||||
|
|
||||||
不传 `--config` 时默认读取 `/etc/monitor/agent.json`。Agent 默认监听控制端口 `7443/tcp`,数据端口 `7443/udp` 和 `7444/tcp`;防火墙和面板中的节点端口配置需与本地配置一致。`reporting.host_stats` 可关闭主机状态上报;启用时只上报资源使用等汇总数据,不包含主机名、进程或 IP。
|
不传 `--config` 时默认读取 `/etc/moshiro/agent.json`。Agent 默认监听控制端口 `7443/tcp`,数据端口 `7443/udp` 和 `7444/tcp`;防火墙和面板中的节点端口配置需与本地配置一致。`reporting.host_stats` 可关闭主机状态上报;启用时只上报资源使用等汇总数据,不包含主机名、进程或 IP。
|
||||||
|
|
||||||
## 设计说明
|
## 设计说明
|
||||||
|
|
||||||
|
|||||||
+26
-26
@@ -4,15 +4,15 @@ using Fleet.Core;
|
|||||||
using Fleet.Hosting;
|
using Fleet.Hosting;
|
||||||
using Fleet.Link;
|
using Fleet.Link;
|
||||||
using Fleet.Rpc;
|
using Fleet.Rpc;
|
||||||
using Monitor.Measurement;
|
using MoShiro.Measurement;
|
||||||
using Monitor.Protocol;
|
using MoShiro.Protocol;
|
||||||
|
|
||||||
namespace Monitor.Agent;
|
namespace MoShiro.Agent;
|
||||||
|
|
||||||
/// <summary>Agent 的组装点:数据面、点对点监听、与 Backend 的链路都在这里接起来。没有全局状态,测试可以在同一进程里启动多个。</summary>
|
/// <summary>Agent 的组装点:数据面、点对点监听、与 Backend 的链路都在这里接起来。没有全局状态,测试可以在同一进程里启动多个。</summary>
|
||||||
public sealed class AgentApp : IDisposable
|
public sealed class AgentApp : IDisposable
|
||||||
{
|
{
|
||||||
public const string Version = Monitor.Protocol.BuildInfo.Version;
|
public const string Version = MoShiro.Protocol.BuildInfo.Version;
|
||||||
|
|
||||||
private readonly AgentConfig _config;
|
private readonly AgentConfig _config;
|
||||||
private readonly LoadedNodeIdentity _identity;
|
private readonly LoadedNodeIdentity _identity;
|
||||||
@@ -35,7 +35,7 @@ public sealed class AgentApp : IDisposable
|
|||||||
private readonly TestRunner _tests;
|
private readonly TestRunner _tests;
|
||||||
private readonly FleetLink _link;
|
private readonly FleetLink _link;
|
||||||
|
|
||||||
/// <param name="dataDirectory">配置目录(容器里的 <c>/etc/monitor</c>):宿主机更新器和节点在这里交换清单、请求和状态。</param>
|
/// <param name="dataDirectory">配置目录(容器里的 <c>/etc/moshiro</c>):宿主机更新器和节点在这里交换清单、请求和状态。</param>
|
||||||
public AgentApp(AgentConfig config, LoadedNodeIdentity identity, string dataDirectory, TimeProvider time, INodeLog log)
|
public AgentApp(AgentConfig config, LoadedNodeIdentity identity, string dataDirectory, TimeProvider time, INodeLog log)
|
||||||
{
|
{
|
||||||
_update = new UpdateBridge(dataDirectory, new ConfigStore(dataDirectory).Write, log);
|
_update = new UpdateBridge(dataDirectory, new ConfigStore(dataDirectory).Write, log);
|
||||||
@@ -185,48 +185,48 @@ public sealed class AgentApp : IDisposable
|
|||||||
HashSet<string>? allowed = _config.Policy.AllowMethods is { } list ? [.. list] : null;
|
HashSet<string>? allowed = _config.Policy.AllowMethods is { } list ? [.. list] : null;
|
||||||
bool Allow(string method) => allowed is null || allowed.Contains(method);
|
bool Allow(string method) => allowed is null || allowed.Contains(method);
|
||||||
|
|
||||||
if (Allow(MonitorMethods.TestRun))
|
if (Allow(MoShiroMethods.TestRun))
|
||||||
{
|
{
|
||||||
router.On(
|
router.On(
|
||||||
MonitorMethods.TestRun,
|
MoShiroMethods.TestRun,
|
||||||
MonitorJsonContext.Default.TestRunParams,
|
MoShiroJsonContext.Default.TestRunParams,
|
||||||
MonitorJsonContext.Default.TestRunResult,
|
MoShiroJsonContext.Default.TestRunResult,
|
||||||
(_, run, _) => ValueTask.FromResult(_tests.Start(run)));
|
(_, run, _) => ValueTask.FromResult(_tests.Start(run)));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Allow(MonitorMethods.TestCancel))
|
if (Allow(MoShiroMethods.TestCancel))
|
||||||
{
|
{
|
||||||
router.On(
|
router.On(
|
||||||
MonitorMethods.TestCancel,
|
MoShiroMethods.TestCancel,
|
||||||
MonitorJsonContext.Default.TestCancelParams,
|
MoShiroJsonContext.Default.TestCancelParams,
|
||||||
MonitorJsonContext.Default.TestRunResult,
|
MoShiroJsonContext.Default.TestRunResult,
|
||||||
(_, cancel, _) => ValueTask.FromResult(Result<TestRunResult>.Ok(new TestRunResult { Accepted = _tests.Cancel(cancel.TestId) })));
|
(_, cancel, _) => ValueTask.FromResult(Result<TestRunResult>.Ok(new TestRunResult { Accepted = _tests.Cancel(cancel.TestId) })));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Allow(MonitorMethods.UpdateOffer))
|
if (Allow(MoShiroMethods.UpdateOffer))
|
||||||
{
|
{
|
||||||
router.OnNotify(MonitorMethods.UpdateOffer, MonitorJsonContext.Default.UpdateOfferParams, (_, offer, _) =>
|
router.OnNotify(MoShiroMethods.UpdateOffer, MoShiroJsonContext.Default.UpdateOfferParams, (_, offer, _) =>
|
||||||
{
|
{
|
||||||
_update.WriteOffer(offer);
|
_update.WriteOffer(offer);
|
||||||
return ValueTask.CompletedTask;
|
return ValueTask.CompletedTask;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Allow(MonitorMethods.UpdateApply))
|
if (Allow(MoShiroMethods.UpdateApply))
|
||||||
{
|
{
|
||||||
router.OnNotify(MonitorMethods.UpdateApply, MonitorJsonContext.Default.UpdateOfferParams, (_, _, _) =>
|
router.OnNotify(MoShiroMethods.UpdateApply, MoShiroJsonContext.Default.UpdateOfferParams, (_, _, _) =>
|
||||||
{
|
{
|
||||||
_update.RequestApply();
|
_update.RequestApply();
|
||||||
return ValueTask.CompletedTask;
|
return ValueTask.CompletedTask;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Allow(MonitorMethods.ProbeDisconnect))
|
if (Allow(MoShiroMethods.ProbeDisconnect))
|
||||||
{
|
{
|
||||||
router.On(
|
router.On(
|
||||||
MonitorMethods.ProbeDisconnect,
|
MoShiroMethods.ProbeDisconnect,
|
||||||
MonitorJsonContext.Default.ProbeDisconnectParams,
|
MoShiroJsonContext.Default.ProbeDisconnectParams,
|
||||||
MonitorJsonContext.Default.TestRunResult,
|
MoShiroJsonContext.Default.TestRunResult,
|
||||||
async (_, request, _) => Result<TestRunResult>.Ok(new TestRunResult { Accepted = await _hub.DisconnectAsync(request.ProbeId.Value) }));
|
async (_, request, _) => Result<TestRunResult>.Ok(new TestRunResult { Accepted = await _hub.DisconnectAsync(request.ProbeId.Value) }));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -236,11 +236,11 @@ public sealed class AgentApp : IDisposable
|
|||||||
Identity = _identity.Identity,
|
Identity = _identity.Identity,
|
||||||
NodeId = _identity.NodeId,
|
NodeId = _identity.NodeId,
|
||||||
NodeVersion = Version,
|
NodeVersion = Version,
|
||||||
Capabilities = [MonitorCapabilities.Base, MonitorCapabilities.KindsPrefix + string.Join(',', BlockCatalog.AgentKinds)],
|
Capabilities = [MoShiroCapabilities.Base, MoShiroCapabilities.KindsPrefix + string.Join(',', BlockCatalog.AgentKinds)],
|
||||||
HelloMeta = HelloMeta,
|
HelloMeta = HelloMeta,
|
||||||
HeartbeatExtra = HeartbeatExtra,
|
HeartbeatExtra = HeartbeatExtra,
|
||||||
};
|
};
|
||||||
var link = new FleetLink(options, router, [new StateHandler<TaskSet>(TaskSet.StateType, MonitorJsonContext.Default.TaskSet, _tasks.ApplyAsync)], _outbox, _time, _log);
|
var link = new FleetLink(options, router, [new StateHandler<TaskSet>(TaskSet.StateType, MoShiroJsonContext.Default.TaskSet, _tasks.ApplyAsync)], _outbox, _time, _log);
|
||||||
|
|
||||||
// 链路重新连上之后补报:在线的 Probe 都告诉 Backend(Backend 重启或链路抖动期间错过了 probe_connected)
|
// 链路重新连上之后补报:在线的 Probe 都告诉 Backend(Backend 重启或链路抖动期间错过了 probe_connected)
|
||||||
link.OnConnected = async (_, _) =>
|
link.OnConnected = async (_, _) =>
|
||||||
@@ -257,7 +257,7 @@ public sealed class AgentApp : IDisposable
|
|||||||
{
|
{
|
||||||
var meta = new AgentHelloMeta
|
var meta = new AgentHelloMeta
|
||||||
{
|
{
|
||||||
Platform = Monitor.Measurement.PlatformDetector.Detect(),
|
Platform = MoShiro.Measurement.PlatformDetector.Detect(),
|
||||||
TlsSpki = _identity.File.TlsSpki,
|
TlsSpki = _identity.File.TlsSpki,
|
||||||
ProbeTcpPort = ControlPort,
|
ProbeTcpPort = ControlPort,
|
||||||
ProbeUdpPorts = [.. UdpPorts],
|
ProbeUdpPorts = [.. UdpPorts],
|
||||||
@@ -269,7 +269,7 @@ public sealed class AgentApp : IDisposable
|
|||||||
MaxConcurrentTests = _config.Limits.MaxConcurrentTests,
|
MaxConcurrentTests = _config.Limits.MaxConcurrentTests,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
return JsonSerializer.SerializeToElement(meta, MonitorJsonContext.Default.AgentHelloMeta);
|
return JsonSerializer.SerializeToElement(meta, MoShiroJsonContext.Default.AgentHelloMeta);
|
||||||
}
|
}
|
||||||
|
|
||||||
private long _lastIn;
|
private long _lastIn;
|
||||||
@@ -296,6 +296,6 @@ public sealed class AgentApp : IDisposable
|
|||||||
_lastIn = packetsIn;
|
_lastIn = packetsIn;
|
||||||
_lastOut = packetsOut;
|
_lastOut = packetsOut;
|
||||||
_lastAtMs = nowMs;
|
_lastAtMs = nowMs;
|
||||||
return JsonSerializer.SerializeToElement(extra, MonitorJsonContext.Default.AgentHeartbeatExtra);
|
return JsonSerializer.SerializeToElement(extra, MoShiroJsonContext.Default.AgentHeartbeatExtra);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+2
-2
@@ -1,6 +1,6 @@
|
|||||||
using System.Text.Json.Serialization;
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
namespace Monitor.Agent;
|
namespace MoShiro.Agent;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// <c>agent.json</c>:节点本地配置,面板无法修改。其中 <c>limits</c> 是本地硬上限:期望任务集超出这些范围时,
|
/// <c>agent.json</c>:节点本地配置,面板无法修改。其中 <c>limits</c> 是本地硬上限:期望任务集超出这些范围时,
|
||||||
@@ -40,7 +40,7 @@ public sealed class FleetSection
|
|||||||
public string Server { get; set; } = "";
|
public string Server { get; set; } = "";
|
||||||
|
|
||||||
[JsonPropertyName("identity_dir")]
|
[JsonPropertyName("identity_dir")]
|
||||||
public string IdentityDir { get; set; } = "/etc/monitor";
|
public string IdentityDir { get; set; } = "/etc/moshiro";
|
||||||
}
|
}
|
||||||
|
|
||||||
public sealed class ListenSection
|
public sealed class ListenSection
|
||||||
+4
-4
@@ -1,9 +1,9 @@
|
|||||||
using Fleet.Core;
|
using Fleet.Core;
|
||||||
using Fleet.Link;
|
using Fleet.Link;
|
||||||
using Monitor.Measurement;
|
using MoShiro.Measurement;
|
||||||
using Monitor.Protocol;
|
using MoShiro.Protocol;
|
||||||
|
|
||||||
namespace Monitor.Agent;
|
namespace MoShiro.Agent;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Probe 的列块到达之后:找到对应的流 → 与 Agent 在数据面记录的列合并 → 盖章(块头、时钟锚点)→ 编码 → 放进发件箱(通道 16)。
|
/// Probe 的列块到达之后:找到对应的流 → 与 Agent 在数据面记录的列合并 → 盖章(块头、时钟锚点)→ 编码 → 放进发件箱(通道 16)。
|
||||||
@@ -70,6 +70,6 @@ public sealed class ChunkAssembler
|
|||||||
/// <summary>Agent 自己采集的块(目标任务)直接发出。</summary>
|
/// <summary>Agent 自己采集的块(目标任务)直接发出。</summary>
|
||||||
public void Emit(RawChunk chunk)
|
public void Emit(RawChunk chunk)
|
||||||
{
|
{
|
||||||
_outbox.Enqueue(MonitorChannels.RawChunk, chunk.Header.ChunkId.Value, RawChunkCodec.Encode(chunk));
|
_outbox.Enqueue(MoShiroChannels.RawChunk, chunk.Header.ChunkId.Value, RawChunkCodec.Encode(chunk));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+3
-3
@@ -4,10 +4,10 @@ using System.Net.Sockets;
|
|||||||
using Fleet.Core;
|
using Fleet.Core;
|
||||||
using Fleet.Link;
|
using Fleet.Link;
|
||||||
using Fleet.Rpc;
|
using Fleet.Rpc;
|
||||||
using Monitor.Measurement;
|
using MoShiro.Measurement;
|
||||||
using Monitor.Protocol;
|
using MoShiro.Protocol;
|
||||||
|
|
||||||
namespace Monitor.Agent;
|
namespace MoShiro.Agent;
|
||||||
|
|
||||||
/// <summary>进程内计数(只有计数,没有主机信息):用于心跳和面板上的节点状态。数据面的包计数由共享的 <see cref="DataPlaneCounters"/> 提供。</summary>
|
/// <summary>进程内计数(只有计数,没有主机信息):用于心跳和面板上的节点状态。数据面的包计数由共享的 <see cref="DataPlaneCounters"/> 提供。</summary>
|
||||||
public sealed class AgentCounters
|
public sealed class AgentCounters
|
||||||
+2
-2
@@ -1,7 +1,7 @@
|
|||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
using Monitor.Protocol;
|
using MoShiro.Protocol;
|
||||||
|
|
||||||
namespace Monitor.Agent;
|
namespace MoShiro.Agent;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 主机资源占用的只读快照:CPU 占用、内存、根分区、网卡累计流量与瞬时网速、系统运行时长。
|
/// 主机资源占用的只读快照:CPU 占用、内存、根分区、网卡累计流量与瞬时网速、系统运行时长。
|
||||||
+4
-4
@@ -4,8 +4,8 @@
|
|||||||
Condition="'$(UseAotAnywhere)' == 'true'" />
|
Condition="'$(UseAotAnywhere)' == 'true'" />
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<OutputType>Exe</OutputType>
|
<OutputType>Exe</OutputType>
|
||||||
<AssemblyName>monitor-probe</AssemblyName>
|
<AssemblyName>moshiro-agent</AssemblyName>
|
||||||
<RootNamespace>Monitor.Probe</RootNamespace>
|
<RootNamespace>MoShiro.Agent</RootNamespace>
|
||||||
<IsAotCompatible>true</IsAotCompatible>
|
<IsAotCompatible>true</IsAotCompatible>
|
||||||
<PublishAot>true</PublishAot>
|
<PublishAot>true</PublishAot>
|
||||||
<PublishTrimmed>true</PublishTrimmed>
|
<PublishTrimmed>true</PublishTrimmed>
|
||||||
@@ -15,10 +15,10 @@
|
|||||||
<MetadataUpdaterSupport>false</MetadataUpdaterSupport>
|
<MetadataUpdaterSupport>false</MetadataUpdaterSupport>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<InternalsVisibleTo Include="Monitor.Backend.Tests" />
|
<InternalsVisibleTo Include="MoShiro.Backend.Tests" />
|
||||||
<ProjectReference Include="..\..\..\fleet\src\Fleet.Link\Fleet.Link.csproj" />
|
<ProjectReference Include="..\..\..\fleet\src\Fleet.Link\Fleet.Link.csproj" />
|
||||||
<ProjectReference Include="..\..\..\fleet\src\Fleet.Hosting\Fleet.Hosting.csproj" />
|
<ProjectReference Include="..\..\..\fleet\src\Fleet.Hosting\Fleet.Hosting.csproj" />
|
||||||
<ProjectReference Include="..\..\..\shared\Monitor.Measurement\Monitor.Measurement.csproj" />
|
<ProjectReference Include="..\..\..\shared\MoShiro.Measurement\MoShiro.Measurement.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<Import Project="Sdk.targets" Sdk="Microsoft.NET.Sdk" />
|
<Import Project="Sdk.targets" Sdk="Microsoft.NET.Sdk" />
|
||||||
<Import Project="Sdk.targets" Sdk="StuDev.AotAnywhere"
|
<Import Project="Sdk.targets" Sdk="StuDev.AotAnywhere"
|
||||||
+11
-11
@@ -2,10 +2,10 @@ using System.Text.Json;
|
|||||||
using Fleet.Core;
|
using Fleet.Core;
|
||||||
using Fleet.Link;
|
using Fleet.Link;
|
||||||
using Fleet.Rpc;
|
using Fleet.Rpc;
|
||||||
using Monitor.Measurement;
|
using MoShiro.Measurement;
|
||||||
using Monitor.Protocol;
|
using MoShiro.Protocol;
|
||||||
|
|
||||||
namespace Monitor.Agent;
|
namespace MoShiro.Agent;
|
||||||
|
|
||||||
/// <summary>Agent → Backend 的上行:事件和测试状态是尽力而为的通知(链路断开时丢弃;列块走发件箱,不会丢)。</summary>
|
/// <summary>Agent → Backend 的上行:事件和测试状态是尽力而为的通知(链路断开时丢弃;列块走发件箱,不会丢)。</summary>
|
||||||
public sealed class BackendUplink
|
public sealed class BackendUplink
|
||||||
@@ -31,7 +31,7 @@ public sealed class BackendUplink
|
|||||||
}
|
}
|
||||||
|
|
||||||
var parameters = new AgentEventParams { Kind = kind, AtUtcMs = _time.GetUtcNow().ToUnixTimeMilliseconds(), Detail = detail, Probe = probe };
|
var parameters = new AgentEventParams { Kind = kind, AtUtcMs = _time.GetUtcNow().ToUnixTimeMilliseconds(), Detail = detail, Probe = probe };
|
||||||
await SafeAsync(() => session.NotifyAsync(MonitorMethods.Event, parameters, MonitorJsonContext.Default.AgentEventParams, CancellationToken.None));
|
await SafeAsync(() => session.NotifyAsync(MoShiroMethods.Event, parameters, MoShiroJsonContext.Default.AgentEventParams, CancellationToken.None));
|
||||||
}
|
}
|
||||||
|
|
||||||
public async ValueTask SendHostAsync(HostSnapshot snapshot)
|
public async ValueTask SendHostAsync(HostSnapshot snapshot)
|
||||||
@@ -42,7 +42,7 @@ public sealed class BackendUplink
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
await SafeAsync(() => session.NotifyAsync(MonitorMethods.Host, snapshot, MonitorJsonContext.Default.HostSnapshot, CancellationToken.None));
|
await SafeAsync(() => session.NotifyAsync(MoShiroMethods.Host, snapshot, MoShiroJsonContext.Default.HostSnapshot, CancellationToken.None));
|
||||||
}
|
}
|
||||||
|
|
||||||
public async ValueTask SendTestStatusAsync(TestId test, string status, int? phase = null, string? errorCode = null)
|
public async ValueTask SendTestStatusAsync(TestId test, string status, int? phase = null, string? errorCode = null)
|
||||||
@@ -54,7 +54,7 @@ public sealed class BackendUplink
|
|||||||
}
|
}
|
||||||
|
|
||||||
var parameters = new TestStatusParams { TestId = test, Status = status, Phase = phase, ErrorCode = errorCode };
|
var parameters = new TestStatusParams { TestId = test, Status = status, Phase = phase, ErrorCode = errorCode };
|
||||||
await SafeAsync(() => session.NotifyAsync(MonitorMethods.TestStatus, parameters, MonitorJsonContext.Default.TestStatusParams, CancellationToken.None));
|
await SafeAsync(() => session.NotifyAsync(MoShiroMethods.TestStatus, parameters, MoShiroJsonContext.Default.TestStatusParams, CancellationToken.None));
|
||||||
}
|
}
|
||||||
|
|
||||||
private static async ValueTask SafeAsync(Func<ValueTask> send)
|
private static async ValueTask SafeAsync(Func<ValueTask> send)
|
||||||
@@ -115,7 +115,7 @@ public sealed class PeerHub
|
|||||||
var order = new Queue<Guid>();
|
var order = new Queue<Guid>();
|
||||||
rpc.OnBinary = async (frame, token) =>
|
rpc.OnBinary = async (frame, token) =>
|
||||||
{
|
{
|
||||||
if (!OutboxFrame.TryParse(frame, out byte channel, out Guid id, out ReadOnlyMemory<byte> payload) || channel != MonitorChannels.ColumnBlock)
|
if (!OutboxFrame.TryParse(frame, out byte channel, out Guid id, out ReadOnlyMemory<byte> payload) || channel != MoShiroChannels.ColumnBlock)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -251,7 +251,7 @@ public sealed class PeerHub
|
|||||||
{
|
{
|
||||||
Type = LineTaskSet.StateType,
|
Type = LineTaskSet.StateType,
|
||||||
Version = set.Version,
|
Version = set.Version,
|
||||||
Doc = JsonSerializer.SerializeToElement(set, MonitorJsonContext.Default.LineTaskSet),
|
Doc = JsonSerializer.SerializeToElement(set, MoShiroJsonContext.Default.LineTaskSet),
|
||||||
};
|
};
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@@ -270,8 +270,8 @@ public sealed class PeerHub
|
|||||||
private static RpcRouter<PeerContext> BuildRouter() =>
|
private static RpcRouter<PeerContext> BuildRouter() =>
|
||||||
new RpcRouter<PeerContext>()
|
new RpcRouter<PeerContext>()
|
||||||
.On(
|
.On(
|
||||||
MonitorMethods.Clock,
|
MoShiroMethods.Clock,
|
||||||
MonitorJsonContext.Default.ClockParams,
|
MoShiroJsonContext.Default.ClockParams,
|
||||||
MonitorJsonContext.Default.ClockResult,
|
MoShiroJsonContext.Default.ClockResult,
|
||||||
(context, _, _) => ValueTask.FromResult(Result<ClockResult>.Ok(new ClockResult { AgentMonoUs = context.Clock.NowUs() })));
|
(context, _, _) => ValueTask.FromResult(Result<ClockResult>.Ok(new ClockResult { AgentMonoUs = context.Clock.NowUs() })));
|
||||||
}
|
}
|
||||||
+2
-2
@@ -1,6 +1,6 @@
|
|||||||
using Fleet.Core;
|
using Fleet.Core;
|
||||||
using Fleet.Hosting;
|
using Fleet.Hosting;
|
||||||
using Monitor.Agent;
|
using MoShiro.Agent;
|
||||||
|
|
||||||
TimeProvider time = TimeProvider.System;
|
TimeProvider time = TimeProvider.System;
|
||||||
INodeLog log = new ConsoleNodeLog(time);
|
INodeLog log = new ConsoleNodeLog(time);
|
||||||
@@ -11,7 +11,7 @@ if (PlatformCheck.VerifyCrypto() is { } problem)
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
string configPath = "/etc/monitor/agent.json";
|
string configPath = "/etc/moshiro/agent.json";
|
||||||
for (int i = 0; i < args.Length - 1; i++)
|
for (int i = 0; i < args.Length - 1; i++)
|
||||||
{
|
{
|
||||||
if (args[i] == "--config")
|
if (args[i] == "--config")
|
||||||
+3
-3
@@ -1,10 +1,10 @@
|
|||||||
using System.Net;
|
using System.Net;
|
||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
using Fleet.Core;
|
using Fleet.Core;
|
||||||
using Monitor.Measurement;
|
using MoShiro.Measurement;
|
||||||
using Monitor.Protocol;
|
using MoShiro.Protocol;
|
||||||
|
|
||||||
namespace Monitor.Agent;
|
namespace MoShiro.Agent;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 目标任务(Agent 自己探测一个地址,没有对端):用和线路任务同一套积木(<see cref="BlockCatalog"/> 里的拨号方,例如 icmp_echo、connect),
|
/// 目标任务(Agent 自己探测一个地址,没有对端):用和线路任务同一套积木(<see cref="BlockCatalog"/> 里的拨号方,例如 icmp_echo、connect),
|
||||||
+3
-3
@@ -1,10 +1,10 @@
|
|||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
using Fleet.Core;
|
using Fleet.Core;
|
||||||
using Fleet.Rpc;
|
using Fleet.Rpc;
|
||||||
using Monitor.Measurement;
|
using MoShiro.Measurement;
|
||||||
using Monitor.Protocol;
|
using MoShiro.Protocol;
|
||||||
|
|
||||||
namespace Monitor.Agent;
|
namespace MoShiro.Agent;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 期望任务集(<c>task_set</c>)的调和器:每个任务先经过 <see cref="BudgetGuard"/>(参数校验 + 本地硬上限)。
|
/// 期望任务集(<c>task_set</c>)的调和器:每个任务先经过 <see cref="BudgetGuard"/>(参数校验 + 本地硬上限)。
|
||||||
+17
-17
@@ -1,15 +1,15 @@
|
|||||||
using System.Collections.Concurrent;
|
using System.Collections.Concurrent;
|
||||||
using Fleet.Core;
|
using Fleet.Core;
|
||||||
using Fleet.Rpc;
|
using Fleet.Rpc;
|
||||||
using Monitor.Measurement;
|
using MoShiro.Measurement;
|
||||||
using Monitor.Protocol;
|
using MoShiro.Protocol;
|
||||||
|
|
||||||
namespace Monitor.Agent;
|
namespace MoShiro.Agent;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 按需测试的编排(<c>monitor.test.run</c>):Agent 是时间的主人。每个阶段:
|
/// 按需测试的编排(<c>moshiro.test.run</c>):Agent 是时间的主人。每个阶段:
|
||||||
/// 登记数据面的流 → <c>monitor.test.phase</c>(Probe 准备好自己一侧的角色)→ 运行(单向数据流由 Agent 发包,回显类等待 Probe 发包)
|
/// 登记数据面的流 → <c>moshiro.test.phase</c>(Probe 准备好自己一侧的角色)→ 运行(单向数据流由 Agent 发包,回显类等待 Probe 发包)
|
||||||
/// → <c>monitor.test.phase_end</c>(Probe 收尾,回复它交出的列块个数)→ 等所有列块合并上传 → <c>phase_done</c>。
|
/// → <c>moshiro.test.phase_end</c>(Probe 收尾,回复它交出的列块个数)→ 等所有列块合并上传 → <c>phase_done</c>。
|
||||||
/// 同一时间只能运行 <c>max_concurrent_tests</c> 个测试;Probe 掉线或被取消时测试中止,已经上传的阶段数据保留。
|
/// 同一时间只能运行 <c>max_concurrent_tests</c> 个测试;Probe 掉线或被取消时测试中止,已经上传的阶段数据保留。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public sealed class TestRunner
|
public sealed class TestRunner
|
||||||
@@ -43,17 +43,17 @@ public sealed class TestRunner
|
|||||||
|
|
||||||
public int Running => _running.Count;
|
public int Running => _running.Count;
|
||||||
|
|
||||||
/// <summary>校验并接受一个测试(立即返回),实际运行在后台进行,状态通过 <c>monitor.test.status</c> 回报。</summary>
|
/// <summary>校验并接受一个测试(立即返回),实际运行在后台进行,状态通过 <c>moshiro.test.status</c> 回报。</summary>
|
||||||
public Result<TestRunResult> Start(TestRunParams run)
|
public Result<TestRunResult> Start(TestRunParams run)
|
||||||
{
|
{
|
||||||
if (_sessions.Get(run.ProbeId.Value) is not { } probe)
|
if (_sessions.Get(run.ProbeId.Value) is not { } probe)
|
||||||
{
|
{
|
||||||
return Result<TestRunResult>.Fail(MonitorErrors.ProbeOffline, $"probe {run.ProbeId.Value} is not connected to this agent");
|
return Result<TestRunResult>.Fail(MoShiroErrors.ProbeOffline, $"probe {run.ProbeId.Value} is not connected to this agent");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (run.Phases.Count == 0)
|
if (run.Phases.Count == 0)
|
||||||
{
|
{
|
||||||
return Result<TestRunResult>.Fail(MonitorErrors.ValidationFailed, "a test needs at least one phase");
|
return Result<TestRunResult>.Fail(MoShiroErrors.ValidationFailed, "a test needs at least one phase");
|
||||||
}
|
}
|
||||||
|
|
||||||
var resolved = new List<(TestPhase Phase, TaskDecision Decision)>();
|
var resolved = new List<(TestPhase Phase, TaskDecision Decision)>();
|
||||||
@@ -64,7 +64,7 @@ public sealed class TestRunner
|
|||||||
if (!decision.Accepted)
|
if (!decision.Accepted)
|
||||||
{
|
{
|
||||||
return Result<TestRunResult>.Fail(
|
return Result<TestRunResult>.Fail(
|
||||||
decision.Reason == RejectReasons.Unsupported ? FleetErrors.Unsupported : MonitorErrors.BudgetExceeded,
|
decision.Reason == RejectReasons.Unsupported ? FleetErrors.Unsupported : MoShiroErrors.BudgetExceeded,
|
||||||
$"phase {phase.Kind} refused: {decision.Reason}");
|
$"phase {phase.Kind} refused: {decision.Reason}");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -84,7 +84,7 @@ public sealed class TestRunner
|
|||||||
if (_running.Count >= _maxConcurrent || !_running.TryAdd(run.TestId.Value, running))
|
if (_running.Count >= _maxConcurrent || !_running.TryAdd(run.TestId.Value, running))
|
||||||
{
|
{
|
||||||
running.Dispose();
|
running.Dispose();
|
||||||
return Result<TestRunResult>.Fail(MonitorErrors.TestBusy, "this agent is already running a test");
|
return Result<TestRunResult>.Fail(MoShiroErrors.TestBusy, "this agent is already running a test");
|
||||||
}
|
}
|
||||||
|
|
||||||
_ = Task.Run(() => RunAsync(run.TestId, resolved, running), CancellationToken.None);
|
_ = Task.Run(() => RunAsync(run.TestId, resolved, running), CancellationToken.None);
|
||||||
@@ -154,7 +154,7 @@ public sealed class TestRunner
|
|||||||
catch (OperationCanceledException)
|
catch (OperationCanceledException)
|
||||||
{
|
{
|
||||||
finalStatus = running.Lost ? TestStatuses.Failed : TestStatuses.Aborted;
|
finalStatus = running.Lost ? TestStatuses.Failed : TestStatuses.Aborted;
|
||||||
errorCode = running.Lost ? MonitorErrors.ProbeOffline.Value : null;
|
errorCode = running.Lost ? MoShiroErrors.ProbeOffline.Value : null;
|
||||||
await TryAbortProbeAsync(running.Probe, test);
|
await TryAbortProbeAsync(running.Probe, test);
|
||||||
}
|
}
|
||||||
catch (TestFailure failure)
|
catch (TestFailure failure)
|
||||||
@@ -183,7 +183,7 @@ public sealed class TestRunner
|
|||||||
_streams.Add(stream);
|
_streams.Add(stream);
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
RpcSession<PeerContext> rpc = probe.Rpc ?? throw new TestFailure(MonitorErrors.ProbeOffline.Value);
|
RpcSession<PeerContext> rpc = probe.Rpc ?? throw new TestFailure(MoShiroErrors.ProbeOffline.Value);
|
||||||
var start = new TestPhaseStartParams
|
var start = new TestPhaseStartParams
|
||||||
{
|
{
|
||||||
TestId = test,
|
TestId = test,
|
||||||
@@ -194,7 +194,7 @@ public sealed class TestRunner
|
|||||||
DurationSeconds = durationSeconds,
|
DurationSeconds = durationSeconds,
|
||||||
Port = stream.Port,
|
Port = stream.Port,
|
||||||
};
|
};
|
||||||
Result<TestPhaseStartResult> accepted = await rpc.RequestAsync(MonitorMethods.TestPhase, start, MonitorJsonContext.Default.TestPhaseStartParams, MonitorJsonContext.Default.TestPhaseStartResult, ct);
|
Result<TestPhaseStartResult> accepted = await rpc.RequestAsync(MoShiroMethods.TestPhase, start, MoShiroJsonContext.Default.TestPhaseStartParams, MoShiroJsonContext.Default.TestPhaseStartResult, ct);
|
||||||
if (!accepted.TryGet(out TestPhaseStartResult? started, out FleetError? error) || !started.Accepted)
|
if (!accepted.TryGet(out TestPhaseStartResult? started, out FleetError? error) || !started.Accepted)
|
||||||
{
|
{
|
||||||
throw new TestFailure(error?.Code.Value ?? FleetErrors.Unsupported.Value);
|
throw new TestFailure(error?.Code.Value ?? FleetErrors.Unsupported.Value);
|
||||||
@@ -209,7 +209,7 @@ public sealed class TestRunner
|
|||||||
}
|
}
|
||||||
catch (BlockFaultException)
|
catch (BlockFaultException)
|
||||||
{
|
{
|
||||||
throw new TestFailure(MonitorErrors.ProbeOffline.Value);
|
throw new TestFailure(MoShiroErrors.ProbeOffline.Value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -219,7 +219,7 @@ public sealed class TestRunner
|
|||||||
|
|
||||||
long sent = stream.Running.Block.Produced;
|
long sent = stream.Running.Block.Produced;
|
||||||
var end = new TestPhaseEndParams { TestId = test, Phase = index, StreamId = stream.Id, SentCount = sent };
|
var end = new TestPhaseEndParams { TestId = test, Phase = index, StreamId = stream.Id, SentCount = sent };
|
||||||
Result<TestPhaseEndResult> ended = await rpc.RequestAsync(MonitorMethods.TestPhaseEnd, end, MonitorJsonContext.Default.TestPhaseEndParams, MonitorJsonContext.Default.TestPhaseEndResult, ct, s_phaseSlack);
|
Result<TestPhaseEndResult> ended = await rpc.RequestAsync(MoShiroMethods.TestPhaseEnd, end, MoShiroJsonContext.Default.TestPhaseEndParams, MoShiroJsonContext.Default.TestPhaseEndResult, ct, s_phaseSlack);
|
||||||
if (!ended.TryGet(out TestPhaseEndResult? finished, out FleetError? endError))
|
if (!ended.TryGet(out TestPhaseEndResult? finished, out FleetError? endError))
|
||||||
{
|
{
|
||||||
throw new TestFailure(endError.Code.Value);
|
throw new TestFailure(endError.Code.Value);
|
||||||
@@ -254,7 +254,7 @@ public sealed class TestRunner
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
await rpc.NotifyAsync(MonitorMethods.TestAbort, new TestAbortParams { TestId = test }, MonitorJsonContext.Default.TestAbortParams, CancellationToken.None);
|
await rpc.NotifyAsync(MoShiroMethods.TestAbort, new TestAbortParams { TestId = test }, MoShiroJsonContext.Default.TestAbortParams, CancellationToken.None);
|
||||||
}
|
}
|
||||||
catch (Exception ex) when (ex is ObjectDisposedException or InvalidOperationException or IOException)
|
catch (Exception ex) when (ex is ObjectDisposedException or InvalidOperationException or IOException)
|
||||||
{
|
{
|
||||||
@@ -3,7 +3,7 @@ using System.Text.Json.Serialization;
|
|||||||
|
|
||||||
namespace Fleet.Core;
|
namespace Fleet.Core;
|
||||||
|
|
||||||
/// <summary>节点 ID(带类型前缀的 ULID)。Monitor 中的 AgentId 就是 NodeId。</summary>
|
/// <summary>节点 ID(带类型前缀的 ULID)。MoShiro 中的 AgentId 就是 NodeId。</summary>
|
||||||
[JsonConverter(typeof(NodeIdJsonConverter))]
|
[JsonConverter(typeof(NodeIdJsonConverter))]
|
||||||
public readonly record struct NodeId(string Value)
|
public readonly record struct NodeId(string Value)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ public sealed class FleetLinkOptions
|
|||||||
|
|
||||||
public string NodeVersion { get; init; } = "0.0.0";
|
public string NodeVersion { get; init; } = "0.0.0";
|
||||||
|
|
||||||
/// <summary>框架之外的能力,例如 <c>monitor.v1</c> 与 <c>monitor.kinds:udp_echo,tcp_echo</c>。</summary>
|
/// <summary>框架之外的能力,例如 <c>moshiro.v1</c> 与 <c>moshiro.kinds:udp_echo,tcp_echo</c>。</summary>
|
||||||
public IReadOnlyList<string> Capabilities { get; init; } = [];
|
public IReadOnlyList<string> Capabilities { get; init; } = [];
|
||||||
|
|
||||||
/// <summary>附加到 <c>fleet.hello</c> 的产品元数据。</summary>
|
/// <summary>附加到 <c>fleet.hello</c> 的产品元数据。</summary>
|
||||||
|
|||||||
@@ -58,7 +58,7 @@ public sealed class HelloParams
|
|||||||
[JsonPropertyName("now_utc_ms")]
|
[JsonPropertyName("now_utc_ms")]
|
||||||
public long NowUtcMs { get; set; }
|
public long NowUtcMs { get; set; }
|
||||||
|
|
||||||
/// <summary>产品自定义的元数据(例如 Monitor 的 tls_spki、probe_ports、local_limits 摘要)。</summary>
|
/// <summary>产品自定义的元数据(例如 MoShiro 的 tls_spki、probe_ports、local_limits 摘要)。</summary>
|
||||||
[JsonPropertyName("meta")]
|
[JsonPropertyName("meta")]
|
||||||
public JsonElement? Meta { get; set; }
|
public JsonElement? Meta { get; set; }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ namespace Fleet.Server;
|
|||||||
|
|
||||||
public sealed class FleetServerOptions
|
public sealed class FleetServerOptions
|
||||||
{
|
{
|
||||||
/// <summary>节点 ID 的类型前缀,Monitor 使用 <c>agt</c>。</summary>
|
/// <summary>节点 ID 的类型前缀,MoShiro 使用 <c>agt</c>。</summary>
|
||||||
public string NodeIdPrefix { get; set; } = "node";
|
public string NodeIdPrefix { get; set; } = "node";
|
||||||
|
|
||||||
public int MaxFrameBytes { get; set; } = 16 << 20;
|
public int MaxFrameBytes { get; set; } = 16 << 20;
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ using Fleet.Core;
|
|||||||
|
|
||||||
namespace Fleet.Server;
|
namespace Fleet.Server;
|
||||||
|
|
||||||
/// <summary>Fleet 服务端需要的存储。产品可以自己实现(Monitor 把它放进 monitor.db)。</summary>
|
/// <summary>Fleet 服务端需要的存储。产品可以自己实现(MoShiro 把它放进 moshiro.db)。</summary>
|
||||||
public interface IFleetStore
|
public interface IFleetStore
|
||||||
{
|
{
|
||||||
ValueTask<JoinTokenRecord?> FindJoinTokenAsync(ReadOnlyMemory<byte> tokenSha256, CancellationToken ct);
|
ValueTask<JoinTokenRecord?> FindJoinTokenAsync(ReadOnlyMemory<byte> tokenSha256, CancellationToken ct);
|
||||||
|
|||||||
@@ -50,7 +50,7 @@ public sealed class FleetServerBuilder
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>注册产品的 RPC 方法(<c>monitor.*</c> 等)。处理函数通过 <see cref="IServiceProvider"/> 在调用时解析服务,避免构造期的循环依赖。</summary>
|
/// <summary>注册产品的 RPC 方法(<c>moshiro.*</c> 等)。处理函数通过 <see cref="IServiceProvider"/> 在调用时解析服务,避免构造期的循环依赖。</summary>
|
||||||
public FleetServerBuilder MapMethods(Action<RpcRouter<NodeCallContext>, IServiceProvider> configure)
|
public FleetServerBuilder MapMethods(Action<RpcRouter<NodeCallContext>, IServiceProvider> configure)
|
||||||
{
|
{
|
||||||
_routeConfigurators.Add(configure);
|
_routeConfigurators.Add(configure);
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ using System.Text.Json.Nodes;
|
|||||||
using Fleet.Core;
|
using Fleet.Core;
|
||||||
using Fleet.Enrollment.Client;
|
using Fleet.Enrollment.Client;
|
||||||
|
|
||||||
// fleet-enroll --server https://panel --join jt_xxx --name jp-1 --identity-dir /etc/monitor [--meta '{"probe_tcp_port":7443,...}'] [--version 1.0]
|
// fleet-enroll --server https://panel --join jt_xxx --name jp-1 --identity-dir /etc/moshiro [--meta '{"probe_tcp_port":7443,...}'] [--version 1.0]
|
||||||
// 一次性运行:生成身份、注册、写入身份目录,然后退出。
|
// 一次性运行:生成身份、注册、写入身份目录,然后退出。
|
||||||
string? Arg(string name) => args.SkipWhile(a => a != name).Skip(1).FirstOrDefault();
|
string? Arg(string name) => args.SkipWhile(a => a != name).Skip(1).FirstOrDefault();
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"openapi": "3.1.1",
|
"openapi": "3.1.1",
|
||||||
"info": {
|
"info": {
|
||||||
"title": "monitor-backend | v1",
|
"title": "moshiro-backend | v1",
|
||||||
"version": "1.0.0"
|
"version": "1.0.0"
|
||||||
},
|
},
|
||||||
"paths": {
|
"paths": {
|
||||||
@@ -3168,7 +3168,7 @@
|
|||||||
"tags": [
|
"tags": [
|
||||||
"admin-data"
|
"admin-data"
|
||||||
],
|
],
|
||||||
"summary": "Download a raw chunk (application/vnd.monitor.rawchunk)",
|
"summary": "Download a raw chunk (application/vnd.moshiro.rawchunk)",
|
||||||
"operationId": "raw_chunks_get",
|
"operationId": "raw_chunks_get",
|
||||||
"parameters": [
|
"parameters": [
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
# Monitor Probe
|
# MoShiro Probe
|
||||||
|
|
||||||
Probe 是网络测量的发起端,通常运行在家宽或其他需要观测的网络出口。它连接 Backend(身份、Agent 列表、状态)和 Agent(测量),执行线路任务,并把本端采集的原始数据交给 Agent 汇总上报。Probe 不提供远程命令或文件管理功能。
|
Probe 是网络测量的发起端,通常运行在家宽或其他需要观测的网络出口。它连接 Backend(身份、Agent 列表、状态)和 Agent(测量),执行线路任务,并把本端采集的原始数据交给 Agent 汇总上报。Probe 不提供远程命令或文件管理功能。
|
||||||
|
|
||||||
@@ -13,7 +13,7 @@ Backend ──任务──> Agent <──测量会话──> Probe
|
|||||||
|
|
||||||
Probe 使用面板生成的 `probe.json` 读取自身身份、面板地址(`server`)和本地测试限制。连上面板之后,面板下发要连接的 Agent 列表;Probe 将列表缓存到同目录的 `agents.json`,面板暂时连不上时靠它继续测量。`probe.json` 含私钥,请限制文件访问权限,不要提交到版本控制。
|
Probe 使用面板生成的 `probe.json` 读取自身身份、面板地址(`server`)和本地测试限制。连上面板之后,面板下发要连接的 Agent 列表;Probe 将列表缓存到同目录的 `agents.json`,面板暂时连不上时靠它继续测量。`probe.json` 含私钥,请限制文件访问权限,不要提交到版本控制。
|
||||||
|
|
||||||
容器节点的运行方式(podman + systemd)和宿主机更新器见 [`../../docs/deployment.md`](../../docs/deployment.md);Probe 也可以用 OCI 镜像 `registry.oeo.one/monitor/monitor-probe:<version>`,容器默认读取 `/etc/monitor/probe.json`,命名 volume 保存身份配置和缓存,镜像以 UID/GID 1654 运行。
|
容器节点的运行方式(podman + systemd)和宿主机更新器见 [`../../docs/deployment.md`](../../docs/deployment.md);Probe 也可以用 OCI 镜像 `registry.oeo.one/moshiro/moshiro-probe:<version>`,容器默认读取 `/etc/moshiro/probe.json`,命名 volume 保存身份配置和缓存,镜像以 UID/GID 1654 运行。
|
||||||
|
|
||||||
## 构建
|
## 构建
|
||||||
|
|
||||||
@@ -21,33 +21,33 @@ Probe 使用面板生成的 `probe.json` 读取自身身份、面板地址(`se
|
|||||||
|
|
||||||
```sh
|
```sh
|
||||||
cd backend
|
cd backend
|
||||||
dotnet build probe/src/Monitor.Probe -c Release
|
dotnet build probe/src/MoShiro.Probe -c Release
|
||||||
```
|
```
|
||||||
|
|
||||||
发布 NativeAOT 程序需要对应平台的 C/C++ 工具链;`<RID>` 应与构建环境和目标平台匹配:
|
发布 NativeAOT 程序需要对应平台的 C/C++ 工具链;`<RID>` 应与构建环境和目标平台匹配:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
dotnet publish probe/src/Monitor.Probe -c Release -r <RID> -p:PublishAot=true
|
dotnet publish probe/src/MoShiro.Probe -c Release -r <RID> -p:PublishAot=true
|
||||||
```
|
```
|
||||||
|
|
||||||
也可在仓库根目录运行 `bash scripts/publish.sh probe <RID>`,再使用 `scripts/package.sh probe <RID>` 生成部署包。支持平台和安装流程见 [`../../docs/deployment.md`](../../docs/deployment.md)。
|
也可在仓库根目录运行 `bash scripts/publish.sh probe <RID>`,再使用 `scripts/package.sh probe <RID>` 生成部署包。支持平台和安装流程见 [`../../docs/deployment.md`](../../docs/deployment.md)。
|
||||||
|
|
||||||
## 配置与运行
|
## 配置与运行
|
||||||
|
|
||||||
在管理端创建 Probe 并下载一次性身份配置,将文件保存为目标机上的 `/etc/monitor/probe.json`。随后运行:
|
在管理端创建 Probe 并下载一次性身份配置,将文件保存为目标机上的 `/etc/moshiro/probe.json`。随后运行:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
monitor-probe --config /etc/monitor/probe.json
|
moshiro-probe --config /etc/moshiro/probe.json
|
||||||
```
|
```
|
||||||
|
|
||||||
不传 `--config` 时默认读取 `/etc/monitor/probe.json`。配置中的 `limits` 是本地硬上限,Backend 无法远程放宽;路由器等低性能设备可调低测试码率和包速率,避免 Probe 自身成为测量瓶颈。部署时可使用仓库的 `packaging/` 文件和安装脚本。
|
不传 `--config` 时默认读取 `/etc/moshiro/probe.json`。配置中的 `limits` 是本地硬上限,Backend 无法远程放宽;路由器等低性能设备可调低测试码率和包速率,避免 Probe 自身成为测量瓶颈。部署时可使用仓库的 `packaging/` 文件和安装脚本。
|
||||||
|
|
||||||
也可使用 OCI 镜像 `registry.oeo.one/monitor/monitor-probe:<version>`,自动选择 Linux amd64 或 arm64 镜像。容器默认读取 `/etc/monitor/probe.json`,并在同一目录维护 `agents.json` 缓存,因此挂载目录必须允许 UID/GID 1654 读写。建议使用宿主网络,并按 ICMP 测量需求添加 `NET_RAW` 能力。
|
也可使用 OCI 镜像 `registry.oeo.one/moshiro/moshiro-probe:<version>`,自动选择 Linux amd64 或 arm64 镜像。容器默认读取 `/etc/moshiro/probe.json`,并在同一目录维护 `agents.json` 缓存,因此挂载目录必须允许 UID/GID 1654 读写。建议使用宿主网络,并按 ICMP 测量需求添加 `NET_RAW` 能力。
|
||||||
|
|
||||||
## 设计说明
|
## 设计说明
|
||||||
|
|
||||||
- Probe 只执行编译进程序的测量模块。它不落盘测量数据,也不能执行命令或自行更新(更新由宿主机上的更新器完成,Probe 只写清单和请求文件)。
|
- Probe 只执行编译进程序的测量模块。它不落盘测量数据,也不能执行命令或自行更新(更新由宿主机上的更新器完成,Probe 只写清单和请求文件)。
|
||||||
- 测量模块由 `backend/shared/Monitor.Measurement/` 与 Agent 共用;Probe 负责发起连接、调度任务和记录本端数据。
|
- 测量模块由 `backend/shared/MoShiro.Measurement/` 与 Agent 共用;Probe 负责发起连接、调度任务和记录本端数据。
|
||||||
- 测量模型和协议细节见 [`../../docs/measurement.md`](../../docs/measurement.md) 与 [`../../docs/protocol.md`](../../docs/protocol.md);整体架构见 [`../../docs/architecture.md`](../../docs/architecture.md)。
|
- 测量模型和协议细节见 [`../../docs/measurement.md`](../../docs/measurement.md) 与 [`../../docs/protocol.md`](../../docs/protocol.md);整体架构见 [`../../docs/architecture.md`](../../docs/architecture.md)。
|
||||||
|
|
||||||
Docker 与 rootless/rootful Podman 共用 `packaging/compose/probe/compose.yaml`,配置、身份和缓存放在命名 volume 中,无需宿主配置目录、keep-id 或手动 chown。首次导入和自动更新见部署文档。
|
Docker 与 rootless/rootful Podman 共用 `packaging/compose/probe/compose.yaml`,配置、身份和缓存放在命名 volume 中,无需宿主配置目录、keep-id 或手动 chown。首次导入和自动更新见部署文档。
|
||||||
+13
-13
@@ -6,10 +6,10 @@ using Fleet.Core;
|
|||||||
using Fleet.Identity;
|
using Fleet.Identity;
|
||||||
using Fleet.Link;
|
using Fleet.Link;
|
||||||
using Fleet.Rpc;
|
using Fleet.Rpc;
|
||||||
using Monitor.Measurement;
|
using MoShiro.Measurement;
|
||||||
using Monitor.Protocol;
|
using MoShiro.Protocol;
|
||||||
|
|
||||||
namespace Monitor.Probe;
|
namespace MoShiro.Probe;
|
||||||
|
|
||||||
/// <summary>所有连接共用的东西。</summary>
|
/// <summary>所有连接共用的东西。</summary>
|
||||||
public sealed record ConnectionEnv(NodeIdentity Identity, string ProbeId, ProbeLimits Limits, AgentDirectory Directory, MonoClock Clock, TimeProvider Time, INodeLog Log);
|
public sealed record ConnectionEnv(NodeIdentity Identity, string ProbeId, ProbeLimits Limits, AgentDirectory Directory, MonoClock Clock, TimeProvider Time, INodeLog Log);
|
||||||
@@ -154,7 +154,7 @@ public sealed class AgentConnection
|
|||||||
for (int i = 0; i < rounds; i++)
|
for (int i = 0; i < rounds; i++)
|
||||||
{
|
{
|
||||||
long sent = _env.Clock.NowUs();
|
long sent = _env.Clock.NowUs();
|
||||||
Result<ClockResult> reply = await rpc.RequestAsync(MonitorMethods.Clock, new ClockParams { ProbeMonoUs = sent }, MonitorJsonContext.Default.ClockParams, MonitorJsonContext.Default.ClockResult, ct);
|
Result<ClockResult> reply = await rpc.RequestAsync(MoShiroMethods.Clock, new ClockParams { ProbeMonoUs = sent }, MoShiroJsonContext.Default.ClockParams, MoShiroJsonContext.Default.ClockResult, ct);
|
||||||
if (reply.TryGet(out ClockResult? value, out _))
|
if (reply.TryGet(out ClockResult? value, out _))
|
||||||
{
|
{
|
||||||
clock.Add(sent, value.AgentMonoUs, _env.Clock.NowUs());
|
clock.Add(sent, value.AgentMonoUs, _env.Clock.NowUs());
|
||||||
@@ -189,7 +189,7 @@ public sealed class AgentConnection
|
|||||||
var result = new StateSyncResult { AppliedVersion = sync.Version };
|
var result = new StateSyncResult { AppliedVersion = sync.Version };
|
||||||
if (sync.Type == LineTaskSet.StateType)
|
if (sync.Type == LineTaskSet.StateType)
|
||||||
{
|
{
|
||||||
LineTaskSet? set = sync.Doc.Deserialize(MonitorJsonContext.Default.LineTaskSet);
|
LineTaskSet? set = sync.Doc.Deserialize(MoShiroJsonContext.Default.LineTaskSet);
|
||||||
if (set is null)
|
if (set is null)
|
||||||
{
|
{
|
||||||
return Result<StateSyncResult>.Fail(FleetErrors.InvalidParams, "line_task_set could not be parsed");
|
return Result<StateSyncResult>.Fail(FleetErrors.InvalidParams, "line_task_set could not be parsed");
|
||||||
@@ -201,18 +201,18 @@ public sealed class AgentConnection
|
|||||||
return Result<StateSyncResult>.Ok(result);
|
return Result<StateSyncResult>.Ok(result);
|
||||||
})
|
})
|
||||||
.On(
|
.On(
|
||||||
MonitorMethods.TestPhase,
|
MoShiroMethods.TestPhase,
|
||||||
MonitorJsonContext.Default.TestPhaseStartParams,
|
MoShiroJsonContext.Default.TestPhaseStartParams,
|
||||||
MonitorJsonContext.Default.TestPhaseStartResult,
|
MoShiroJsonContext.Default.TestPhaseStartResult,
|
||||||
(session, start, _) => ValueTask.FromResult(Result<TestPhaseStartResult>.Ok(new TestPhaseStartResult { Accepted = session.StartPhase(start) })))
|
(session, start, _) => ValueTask.FromResult(Result<TestPhaseStartResult>.Ok(new TestPhaseStartResult { Accepted = session.StartPhase(start) })))
|
||||||
.On(
|
.On(
|
||||||
MonitorMethods.TestPhaseEnd,
|
MoShiroMethods.TestPhaseEnd,
|
||||||
MonitorJsonContext.Default.TestPhaseEndParams,
|
MoShiroJsonContext.Default.TestPhaseEndParams,
|
||||||
MonitorJsonContext.Default.TestPhaseEndResult,
|
MoShiroJsonContext.Default.TestPhaseEndResult,
|
||||||
async (session, end, ct) => Result<TestPhaseEndResult>.Ok(new TestPhaseEndResult { Fragments = await session.EndPhaseAsync(end, ct) }))
|
async (session, end, ct) => Result<TestPhaseEndResult>.Ok(new TestPhaseEndResult { Fragments = await session.EndPhaseAsync(end, ct) }))
|
||||||
.OnNotify(
|
.OnNotify(
|
||||||
MonitorMethods.TestAbort,
|
MoShiroMethods.TestAbort,
|
||||||
MonitorJsonContext.Default.TestAbortParams,
|
MoShiroJsonContext.Default.TestAbortParams,
|
||||||
async (session, abort, _) => await session.AbortAsync(abort.TestId))
|
async (session, abort, _) => await session.AbortAsync(abort.TestId))
|
||||||
.OnNotify(
|
.OnNotify(
|
||||||
FleetMethods.OutboxAck,
|
FleetMethods.OutboxAck,
|
||||||
+2
-2
@@ -1,9 +1,9 @@
|
|||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
using Fleet.Core;
|
using Fleet.Core;
|
||||||
using Fleet.Hosting;
|
using Fleet.Hosting;
|
||||||
using Monitor.Protocol;
|
using MoShiro.Protocol;
|
||||||
|
|
||||||
namespace Monitor.Probe;
|
namespace MoShiro.Probe;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Probe 要连接的 Agent 列表:由面板作为期望状态(<c>probe_agents</c>)直接下发;面板连不上时用上一次保存的 <c>agents.json</c>。
|
/// Probe 要连接的 Agent 列表:由面板作为期望状态(<c>probe_agents</c>)直接下发;面板连不上时用上一次保存的 <c>agents.json</c>。
|
||||||
+5
-5
@@ -1,13 +1,13 @@
|
|||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
using Fleet.Core;
|
using Fleet.Core;
|
||||||
using Fleet.Link;
|
using Fleet.Link;
|
||||||
using Monitor.Measurement;
|
using MoShiro.Measurement;
|
||||||
using Monitor.Protocol;
|
using MoShiro.Protocol;
|
||||||
|
|
||||||
namespace Monitor.Probe;
|
namespace MoShiro.Probe;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 估计 Agent 单调时钟 − Probe 单调时钟:每次 <c>monitor.clock</c> 往返取中点,在最近 8 次里取往返最短的一次
|
/// 估计 Agent 单调时钟 − Probe 单调时钟:每次 <c>moshiro.clock</c> 往返取中点,在最近 8 次里取往返最短的一次
|
||||||
/// (往返越短,对称性假设的误差越小)。Probe 的列块头里带着这个偏移,Backend 据此对齐两端的时间戳。
|
/// (往返越短,对称性假设的误差越小)。Probe 的列块头里带着这个偏移,Backend 据此对齐两端的时间戳。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public sealed class ClockEstimator
|
public sealed class ClockEstimator
|
||||||
@@ -82,6 +82,6 @@ public sealed class FragmentShipper : IFragmentSink
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_outbox.Enqueue(MonitorChannels.ColumnBlock, header.ChunkId.Value, RawChunkCodec.Encode(new RawChunk(header, streams)));
|
_outbox.Enqueue(MoShiroChannels.ColumnBlock, header.ChunkId.Value, RawChunkCodec.Encode(new RawChunk(header, streams)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+4
-4
@@ -4,8 +4,8 @@
|
|||||||
Condition="'$(UseAotAnywhere)' == 'true'" />
|
Condition="'$(UseAotAnywhere)' == 'true'" />
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<OutputType>Exe</OutputType>
|
<OutputType>Exe</OutputType>
|
||||||
<AssemblyName>monitor-agent</AssemblyName>
|
<AssemblyName>moshiro-probe</AssemblyName>
|
||||||
<RootNamespace>Monitor.Agent</RootNamespace>
|
<RootNamespace>MoShiro.Probe</RootNamespace>
|
||||||
<IsAotCompatible>true</IsAotCompatible>
|
<IsAotCompatible>true</IsAotCompatible>
|
||||||
<PublishAot>true</PublishAot>
|
<PublishAot>true</PublishAot>
|
||||||
<PublishTrimmed>true</PublishTrimmed>
|
<PublishTrimmed>true</PublishTrimmed>
|
||||||
@@ -15,10 +15,10 @@
|
|||||||
<MetadataUpdaterSupport>false</MetadataUpdaterSupport>
|
<MetadataUpdaterSupport>false</MetadataUpdaterSupport>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<InternalsVisibleTo Include="Monitor.Backend.Tests" />
|
<InternalsVisibleTo Include="MoShiro.Backend.Tests" />
|
||||||
<ProjectReference Include="..\..\..\fleet\src\Fleet.Link\Fleet.Link.csproj" />
|
<ProjectReference Include="..\..\..\fleet\src\Fleet.Link\Fleet.Link.csproj" />
|
||||||
<ProjectReference Include="..\..\..\fleet\src\Fleet.Hosting\Fleet.Hosting.csproj" />
|
<ProjectReference Include="..\..\..\fleet\src\Fleet.Hosting\Fleet.Hosting.csproj" />
|
||||||
<ProjectReference Include="..\..\..\shared\Monitor.Measurement\Monitor.Measurement.csproj" />
|
<ProjectReference Include="..\..\..\shared\MoShiro.Measurement\MoShiro.Measurement.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<Import Project="Sdk.targets" Sdk="Microsoft.NET.Sdk" />
|
<Import Project="Sdk.targets" Sdk="Microsoft.NET.Sdk" />
|
||||||
<Import Project="Sdk.targets" Sdk="StuDev.AotAnywhere"
|
<Import Project="Sdk.targets" Sdk="StuDev.AotAnywhere"
|
||||||
+10
-10
@@ -4,10 +4,10 @@ using System.Text.Json;
|
|||||||
using Fleet.Identity;
|
using Fleet.Identity;
|
||||||
using Fleet.Link;
|
using Fleet.Link;
|
||||||
using Fleet.Rpc;
|
using Fleet.Rpc;
|
||||||
using Monitor.Measurement;
|
using MoShiro.Measurement;
|
||||||
using Monitor.Protocol;
|
using MoShiro.Protocol;
|
||||||
|
|
||||||
namespace Monitor.Probe;
|
namespace MoShiro.Probe;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Probe 的组装点:到面板的长连接(身份、Agent 列表、状态)+ 每个 Agent 一个测量连接(认证、重连、角色)。没有全局状态,测试可以在同一进程里启动多个。
|
/// Probe 的组装点:到面板的长连接(身份、Agent 列表、状态)+ 每个 Agent 一个测量连接(认证、重连、角色)。没有全局状态,测试可以在同一进程里启动多个。
|
||||||
@@ -15,7 +15,7 @@ namespace Monitor.Probe;
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public sealed class ProbeApp : IDisposable
|
public sealed class ProbeApp : IDisposable
|
||||||
{
|
{
|
||||||
public const string Version = Monitor.Protocol.BuildInfo.Version;
|
public const string Version = MoShiro.Protocol.BuildInfo.Version;
|
||||||
|
|
||||||
private readonly ConnectionEnv _env;
|
private readonly ConnectionEnv _env;
|
||||||
private readonly Dictionary<string, Running> _connections = [];
|
private readonly Dictionary<string, Running> _connections = [];
|
||||||
@@ -38,25 +38,25 @@ public sealed class ProbeApp : IDisposable
|
|||||||
Identity = identity,
|
Identity = identity,
|
||||||
NodeId = new NodeId(file.ProbeId),
|
NodeId = new NodeId(file.ProbeId),
|
||||||
NodeVersion = Version,
|
NodeVersion = Version,
|
||||||
Capabilities = [MonitorCapabilities.Base],
|
Capabilities = [MoShiroCapabilities.Base],
|
||||||
HeartbeatExtra = () => JsonSerializer.SerializeToElement(new ProbeHeartbeatExtra { Update = update.ReadStatus() }, MonitorJsonContext.Default.ProbeHeartbeatExtra),
|
HeartbeatExtra = () => JsonSerializer.SerializeToElement(new ProbeHeartbeatExtra { Update = update.ReadStatus() }, MoShiroJsonContext.Default.ProbeHeartbeatExtra),
|
||||||
HelloMeta = () => JsonSerializer.SerializeToElement(new ProbeHelloMeta { Platform = PlatformDetector.Detect() }, MonitorJsonContext.Default.ProbeHelloMeta),
|
HelloMeta = () => JsonSerializer.SerializeToElement(new ProbeHelloMeta { Platform = PlatformDetector.Detect() }, MoShiroJsonContext.Default.ProbeHelloMeta),
|
||||||
};
|
};
|
||||||
var list = new StateHandler<ProbeAgentList>(
|
var list = new StateHandler<ProbeAgentList>(
|
||||||
ProbeAgentList.StateType,
|
ProbeAgentList.StateType,
|
||||||
MonitorJsonContext.Default.ProbeAgentList,
|
MoShiroJsonContext.Default.ProbeAgentList,
|
||||||
(version, doc, _) =>
|
(version, doc, _) =>
|
||||||
{
|
{
|
||||||
directory.Apply(doc);
|
directory.Apply(doc);
|
||||||
return ValueTask.FromResult(new StateSyncResult { AppliedVersion = version });
|
return ValueTask.FromResult(new StateSyncResult { AppliedVersion = version });
|
||||||
});
|
});
|
||||||
var router = new RpcRouter<LinkContext>()
|
var router = new RpcRouter<LinkContext>()
|
||||||
.OnNotify(MonitorMethods.UpdateOffer, MonitorJsonContext.Default.UpdateOfferParams, (_, offer, _) =>
|
.OnNotify(MoShiroMethods.UpdateOffer, MoShiroJsonContext.Default.UpdateOfferParams, (_, offer, _) =>
|
||||||
{
|
{
|
||||||
update.WriteOffer(offer);
|
update.WriteOffer(offer);
|
||||||
return ValueTask.CompletedTask;
|
return ValueTask.CompletedTask;
|
||||||
})
|
})
|
||||||
.OnNotify(MonitorMethods.UpdateApply, MonitorJsonContext.Default.UpdateOfferParams, (_, _, _) =>
|
.OnNotify(MoShiroMethods.UpdateApply, MoShiroJsonContext.Default.UpdateOfferParams, (_, _, _) =>
|
||||||
{
|
{
|
||||||
update.RequestApply();
|
update.RequestApply();
|
||||||
return ValueTask.CompletedTask;
|
return ValueTask.CompletedTask;
|
||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
using System.Text.Json.Serialization;
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
namespace Monitor.Probe;
|
namespace MoShiro.Probe;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// <c>probe.json</c>:面板 <c>POST /api/admin/probe/identity</c> 生成的文件(只能下载一次)。
|
/// <c>probe.json</c>:面板 <c>POST /api/admin/probe/identity</c> 生成的文件(只能下载一次)。
|
||||||
+2
-2
@@ -1,6 +1,6 @@
|
|||||||
using Fleet.Core;
|
using Fleet.Core;
|
||||||
using Fleet.Hosting;
|
using Fleet.Hosting;
|
||||||
using Monitor.Probe;
|
using MoShiro.Probe;
|
||||||
|
|
||||||
TimeProvider time = TimeProvider.System;
|
TimeProvider time = TimeProvider.System;
|
||||||
INodeLog log = new ConsoleNodeLog(time);
|
INodeLog log = new ConsoleNodeLog(time);
|
||||||
@@ -11,7 +11,7 @@ if (PlatformCheck.VerifyCrypto() is { } problem)
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
string configPath = "/etc/monitor/probe.json";
|
string configPath = "/etc/moshiro/probe.json";
|
||||||
for (int i = 0; i < args.Length - 1; i++)
|
for (int i = 0; i < args.Length - 1; i++)
|
||||||
{
|
{
|
||||||
if (args[i] == "--config")
|
if (args[i] == "--config")
|
||||||
+1
-1
@@ -5,7 +5,7 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="xunit.v3" />
|
<PackageReference Include="xunit.v3" />
|
||||||
<ProjectReference Include="..\..\src\Monitor.Probe\Monitor.Probe.csproj" />
|
<ProjectReference Include="..\..\src\MoShiro.Probe\MoShiro.Probe.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Using Include="Xunit" />
|
<Using Include="Xunit" />
|
||||||
+2
-2
@@ -1,8 +1,8 @@
|
|||||||
using Fleet.Core;
|
using Fleet.Core;
|
||||||
using Fleet.Hosting;
|
using Fleet.Hosting;
|
||||||
using Monitor.Protocol;
|
using MoShiro.Protocol;
|
||||||
|
|
||||||
namespace Monitor.Probe.Tests;
|
namespace MoShiro.Probe.Tests;
|
||||||
|
|
||||||
public sealed class ProbeTests
|
public sealed class ProbeTests
|
||||||
{
|
{
|
||||||
+2
-2
@@ -1,7 +1,7 @@
|
|||||||
using System.Net;
|
using System.Net;
|
||||||
using Monitor.Protocol;
|
using MoShiro.Protocol;
|
||||||
|
|
||||||
namespace Monitor.Measurement;
|
namespace MoShiro.Measurement;
|
||||||
|
|
||||||
/// <summary>积木的公共部分:环境、参数、把行切成列块交出去。</summary>
|
/// <summary>积木的公共部分:环境、参数、把行切成列块交出去。</summary>
|
||||||
public abstract class BlockBase : IBlock
|
public abstract class BlockBase : IBlock
|
||||||
+2
-2
@@ -1,6 +1,6 @@
|
|||||||
using Monitor.Protocol;
|
using MoShiro.Protocol;
|
||||||
|
|
||||||
namespace Monitor.Measurement;
|
namespace MoShiro.Measurement;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 一种采集方法(Kind)的两个角色:拨号方(Probe)和监听方(Agent),以及它能不能作为目标任务(Agent 自己探测一个地址,没有对端)运行。
|
/// 一种采集方法(Kind)的两个角色:拨号方(Probe)和监听方(Agent),以及它能不能作为目标任务(Agent 自己探测一个地址,没有对端)运行。
|
||||||
+2
-2
@@ -1,7 +1,7 @@
|
|||||||
using System.Net.Sockets;
|
using System.Net.Sockets;
|
||||||
using Monitor.Protocol;
|
using MoShiro.Protocol;
|
||||||
|
|
||||||
namespace Monitor.Measurement;
|
namespace MoShiro.Measurement;
|
||||||
|
|
||||||
/// <summary>批量传输的收发循环(两端共用)。</summary>
|
/// <summary>批量传输的收发循环(两端共用)。</summary>
|
||||||
internal static class BulkIo
|
internal static class BulkIo
|
||||||
+2
-2
@@ -1,8 +1,8 @@
|
|||||||
using System.Net;
|
using System.Net;
|
||||||
using System.Net.Sockets;
|
using System.Net.Sockets;
|
||||||
using Monitor.Protocol;
|
using MoShiro.Protocol;
|
||||||
|
|
||||||
namespace Monitor.Measurement;
|
namespace MoShiro.Measurement;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 回显积木的行表(拨号方,行的主导方):发出时记 size、t1,回复到达时按序号回填 t4,到点把已经稳定的行切成列块。
|
/// 回显积木的行表(拨号方,行的主导方):发出时记 size、t1,回复到达时按序号回填 t4,到点把已经稳定的行切成列块。
|
||||||
+2
-2
@@ -1,9 +1,9 @@
|
|||||||
using System.Net.NetworkInformation;
|
using System.Net.NetworkInformation;
|
||||||
using System.Net.Sockets;
|
using System.Net.Sockets;
|
||||||
using System.Security.Authentication;
|
using System.Security.Authentication;
|
||||||
using Monitor.Protocol;
|
using MoShiro.Protocol;
|
||||||
|
|
||||||
namespace Monitor.Measurement;
|
namespace MoShiro.Measurement;
|
||||||
|
|
||||||
/// <summary>什么也不做的监听方:connect 的另一端就是 TCP 监听器本身(接受连接、认证失败就关闭),不需要积木,但需要占一个流编号。</summary>
|
/// <summary>什么也不做的监听方:connect 的另一端就是 TCP 监听器本身(接受连接、认证失败就关闭),不需要积木,但需要占一个流编号。</summary>
|
||||||
public sealed class IdleListener : BlockBase
|
public sealed class IdleListener : BlockBase
|
||||||
+2
-2
@@ -1,8 +1,8 @@
|
|||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.Net;
|
using System.Net;
|
||||||
using Monitor.Protocol;
|
using MoShiro.Protocol;
|
||||||
|
|
||||||
namespace Monitor.Measurement;
|
namespace MoShiro.Measurement;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 单向数据流(UDP train),监听方(Agent,行的主导方):按声明的码率均匀发包,发出的每个包 (seq, size, t_send) 记在按序号的记录里,
|
/// 单向数据流(UDP train),监听方(Agent,行的主导方):按声明的码率均匀发包,发出的每个包 (seq, size, t_send) 记在按序号的记录里,
|
||||||
+2
-2
@@ -1,9 +1,9 @@
|
|||||||
using System.Net;
|
using System.Net;
|
||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
using Fleet.Core;
|
using Fleet.Core;
|
||||||
using Monitor.Protocol;
|
using MoShiro.Protocol;
|
||||||
|
|
||||||
namespace Monitor.Measurement;
|
namespace MoShiro.Measurement;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 数据面会话:<c>session_id</c> 和密钥(由点对点握手里的 ECDH 派生)。两端的数据面包都用它认证。
|
/// 数据面会话:<c>session_id</c> 和密钥(由点对点握手里的 ECDH 派生)。两端的数据面包都用它认证。
|
||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
namespace Monitor.Measurement;
|
namespace MoShiro.Measurement;
|
||||||
|
|
||||||
/// <summary>节点的单调时钟(微秒):t1…t4、t_send、t_recv 都用它;Probe 和 Agent 各自一个,块头里的时钟锚点把它对应到 UTC。</summary>
|
/// <summary>节点的单调时钟(微秒):t1…t4、t_send、t_recv 都用它;Probe 和 Agent 各自一个,块头里的时钟锚点把它对应到 UTC。</summary>
|
||||||
public sealed class MonoClock
|
public sealed class MonoClock
|
||||||
+2
-2
@@ -1,6 +1,6 @@
|
|||||||
using Monitor.Protocol;
|
using MoShiro.Protocol;
|
||||||
|
|
||||||
namespace Monitor.Measurement;
|
namespace MoShiro.Measurement;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 一侧记录的行:只含这一侧拥有的列(见 <see cref="StreamSpec.ColumnsOf"/>),按连接键(通常是 <c>seq</c>)索引的环形缓冲,
|
/// 一侧记录的行:只含这一侧拥有的列(见 <see cref="StreamSpec.ColumnsOf"/>),按连接键(通常是 <c>seq</c>)索引的环形缓冲,
|
||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
namespace Monitor.Measurement;
|
namespace MoShiro.Measurement;
|
||||||
|
|
||||||
/// <summary>一个运行中的积木:自己的取消令牌和后台任务。Probe 和 Agent 共用。</summary>
|
/// <summary>一个运行中的积木:自己的取消令牌和后台任务。Probe 和 Agent 共用。</summary>
|
||||||
public sealed class RunningBlock : IDisposable
|
public sealed class RunningBlock : IDisposable
|
||||||
+2
-2
@@ -1,8 +1,8 @@
|
|||||||
using System.Net;
|
using System.Net;
|
||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
using Monitor.Protocol;
|
using MoShiro.Protocol;
|
||||||
|
|
||||||
namespace Monitor.Measurement;
|
namespace MoShiro.Measurement;
|
||||||
|
|
||||||
/// <summary>节点的本地硬上限(写在本机配置里,Backend 无法修改)。</summary>
|
/// <summary>节点的本地硬上限(写在本机配置里,Backend 无法修改)。</summary>
|
||||||
public sealed class NodeLimits
|
public sealed class NodeLimits
|
||||||
+2
-2
@@ -1,8 +1,8 @@
|
|||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
using Fleet.Core;
|
using Fleet.Core;
|
||||||
using Monitor.Protocol;
|
using MoShiro.Protocol;
|
||||||
|
|
||||||
namespace Monitor.Measurement;
|
namespace MoShiro.Measurement;
|
||||||
|
|
||||||
/// <summary>Agent 单调时钟(微秒)与 UTC(毫秒)的对应关系:块头里的时钟锚点。</summary>
|
/// <summary>Agent 单调时钟(微秒)与 UTC(毫秒)的对应关系:块头里的时钟锚点。</summary>
|
||||||
public readonly record struct ClockAnchor(long AgentMonoUs, long UtcMs);
|
public readonly record struct ClockAnchor(long AgentMonoUs, long UtcMs);
|
||||||
+5
-5
@@ -1,8 +1,8 @@
|
|||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
using Fleet.Core;
|
using Fleet.Core;
|
||||||
using Monitor.Protocol;
|
using MoShiro.Protocol;
|
||||||
|
|
||||||
namespace Monitor.Measurement;
|
namespace MoShiro.Measurement;
|
||||||
|
|
||||||
/// <summary>Agent 一侧为合并保留的按键记录(由正在运行的积木提供)。</summary>
|
/// <summary>Agent 一侧为合并保留的按键记录(由正在运行的积木提供)。</summary>
|
||||||
public interface IRecordSource
|
public interface IRecordSource
|
||||||
@@ -29,7 +29,7 @@ public static class FragmentMerger
|
|||||||
StreamSpec? streamSpec = spec.FindStream(stream.Name);
|
StreamSpec? streamSpec = spec.FindStream(stream.Name);
|
||||||
if (streamSpec is null)
|
if (streamSpec is null)
|
||||||
{
|
{
|
||||||
return Result<IReadOnlyList<RawStream>>.Fail(MonitorErrors.ValidationFailed, $"stream {stream.Name} is not declared by {spec.Id}");
|
return Result<IReadOnlyList<RawStream>>.Fail(MoShiroErrors.ValidationFailed, $"stream {stream.Name} is not declared by {spec.Id}");
|
||||||
}
|
}
|
||||||
|
|
||||||
Result<RawStream> merged = Join(streamSpec, stream, fragment.Header, records.Records(streamId, stream.Name));
|
Result<RawStream> merged = Join(streamSpec, stream, fragment.Header, records.Records(streamId, stream.Name));
|
||||||
@@ -71,7 +71,7 @@ public static class FragmentMerger
|
|||||||
RawColumn? key = probe.Find(spec.Key);
|
RawColumn? key = probe.Find(spec.Key);
|
||||||
if (key is null || probeColumns.Any(c => probe.Find(c.Name) is null))
|
if (key is null || probeColumns.Any(c => probe.Find(c.Name) is null))
|
||||||
{
|
{
|
||||||
return Result<RawStream>.Fail(MonitorErrors.ValidationFailed, $"a fragment of stream {spec.Name} needs the columns {string.Join(", ", new[] { spec.Key }.Concat(probeColumns.Select(c => c.Name)))}");
|
return Result<RawStream>.Fail(MoShiroErrors.ValidationFailed, $"a fragment of stream {spec.Name} needs the columns {string.Join(", ", new[] { spec.Key }.Concat(probeColumns.Select(c => c.Name)))}");
|
||||||
}
|
}
|
||||||
|
|
||||||
return spec.Driver == Side.Probe ? JoinOnProbeRows(spec, probe, key, agent) : JoinOnAgentRows(spec, probe, key, header, agent);
|
return spec.Driver == Side.Probe ? JoinOnProbeRows(spec, probe, key, agent) : JoinOnAgentRows(spec, probe, key, header, agent);
|
||||||
@@ -118,7 +118,7 @@ public static class FragmentMerger
|
|||||||
{
|
{
|
||||||
if (!TryGetRange(header, out uint from, out uint to))
|
if (!TryGetRange(header, out uint from, out uint to))
|
||||||
{
|
{
|
||||||
return Result<RawStream>.Fail(MonitorErrors.ValidationFailed, $"a fragment of stream {spec.Name} must declare its sequence range");
|
return Result<RawStream>.Fail(MoShiroErrors.ValidationFailed, $"a fragment of stream {spec.Name} must declare its sequence range");
|
||||||
}
|
}
|
||||||
|
|
||||||
// 行取自 Agent 的记录:范围内发出过的每一个包都有一行,Probe 没有报告到达的就是丢失
|
// 行取自 Agent 的记录:范围内发出过的每一个包都有一行,Probe 没有报告到达的就是丢失
|
||||||
+2
-2
@@ -3,8 +3,8 @@
|
|||||||
<IsAotCompatible>true</IsAotCompatible>
|
<IsAotCompatible>true</IsAotCompatible>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<InternalsVisibleTo Include="Monitor.Measurement.Tests" />
|
<InternalsVisibleTo Include="MoShiro.Measurement.Tests" />
|
||||||
<AdditionalFiles Include="$(MSBuildThisFileDirectory)../../BannedSymbols.Node.txt" />
|
<AdditionalFiles Include="$(MSBuildThisFileDirectory)../../BannedSymbols.Node.txt" />
|
||||||
<ProjectReference Include="..\Monitor.Protocol\Monitor.Protocol.csproj" />
|
<ProjectReference Include="..\MoShiro.Protocol\MoShiro.Protocol.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
||||||
+2
-2
@@ -5,9 +5,9 @@ using System.Net.Sockets;
|
|||||||
using System.Security.Authentication;
|
using System.Security.Authentication;
|
||||||
using System.Security.Cryptography;
|
using System.Security.Cryptography;
|
||||||
using System.Security.Cryptography.X509Certificates;
|
using System.Security.Cryptography.X509Certificates;
|
||||||
using Monitor.Protocol;
|
using MoShiro.Protocol;
|
||||||
|
|
||||||
namespace Monitor.Measurement;
|
namespace MoShiro.Measurement;
|
||||||
|
|
||||||
/// <summary>拨号方的一条数据面 TCP 连接(已经完成 TLS 和首帧认证)。</summary>
|
/// <summary>拨号方的一条数据面 TCP 连接(已经完成 TLS 和首帧认证)。</summary>
|
||||||
public sealed class TcpLink : IDisposable
|
public sealed class TcpLink : IDisposable
|
||||||
+2
-2
@@ -1,8 +1,8 @@
|
|||||||
using System.Buffers.Binary;
|
using System.Buffers.Binary;
|
||||||
using System.Net.Sockets;
|
using System.Net.Sockets;
|
||||||
using Monitor.Protocol;
|
using MoShiro.Protocol;
|
||||||
|
|
||||||
namespace Monitor.Measurement;
|
namespace MoShiro.Measurement;
|
||||||
|
|
||||||
/// <summary>内核对一条 TCP 连接的采样(Linux <c>TCP_INFO</c>)。</summary>
|
/// <summary>内核对一条 TCP 连接的采样(Linux <c>TCP_INFO</c>)。</summary>
|
||||||
public readonly record struct TcpInfoSample(long SrttUs, long RttVarUs, long Cwnd, long TotalRetrans, long SegsOut, long BytesAcked);
|
public readonly record struct TcpInfoSample(long SrttUs, long RttVarUs, long Cwnd, long TotalRetrans, long SegsOut, long BytesAcked);
|
||||||
+2
-2
@@ -2,9 +2,9 @@ using System.Collections.Concurrent;
|
|||||||
using System.Net;
|
using System.Net;
|
||||||
using System.Net.Sockets;
|
using System.Net.Sockets;
|
||||||
using Fleet.Core;
|
using Fleet.Core;
|
||||||
using Monitor.Protocol;
|
using MoShiro.Protocol;
|
||||||
|
|
||||||
namespace Monitor.Measurement;
|
namespace MoShiro.Measurement;
|
||||||
|
|
||||||
/// <summary>一个通过校验的 UDP 包,交给按 <c>stream_id</c> 登记的处理函数。处理函数在接收循环里同步执行,不能阻塞。</summary>
|
/// <summary>一个通过校验的 UDP 包,交给按 <c>stream_id</c> 登记的处理函数。处理函数在接收循环里同步执行,不能阻塞。</summary>
|
||||||
public readonly ref struct UdpDelivery
|
public readonly ref struct UdpDelivery
|
||||||
+2
-2
@@ -1,7 +1,7 @@
|
|||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using Monitor.Protocol;
|
using MoShiro.Protocol;
|
||||||
|
|
||||||
namespace Monitor.Measurement;
|
namespace MoShiro.Measurement;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 读出本机的系统名和架构,只用于在面板上展示。故意只取粗粒度的信息:
|
/// 读出本机的系统名和架构,只用于在面板上展示。故意只取粗粒度的信息:
|
||||||
+4
-4
@@ -1,12 +1,12 @@
|
|||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
using Fleet.Core;
|
using Fleet.Core;
|
||||||
using Monitor.Protocol;
|
using MoShiro.Protocol;
|
||||||
|
|
||||||
namespace Monitor.Measurement;
|
namespace MoShiro.Measurement;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 节点进程和宿主机更新器之间的文件交换(容器里 <c>/etc/monitor</c>,也就是数据目录):
|
/// 节点进程和宿主机更新器之间的文件交换(容器里 <c>/etc/moshiro</c>,也就是数据目录):
|
||||||
/// 节点把面板发来的清单和签名写进 <c>update/</c>,把「更新」请求写成 <c>update/apply.request</c>;更新器验签并更新,状态由宿主更新器写在只读挂载的 <c>update-status/status.json</c>。
|
/// 节点把面板发来的清单和签名写进 <c>update/</c>,把「更新」请求写成 <c>update/apply.request</c>;更新器验签并更新,状态由宿主更新器写在只读挂载的 <c>update-status/status.json</c>。
|
||||||
/// 节点进程不执行任何命令,也不信任清单的内容(验签在宿主机上)。没有安装更新器时目录写不了或没人读,调用只是返回 false。
|
/// 节点进程不执行任何命令,也不信任清单的内容(验签在宿主机上)。没有安装更新器时目录写不了或没人读,调用只是返回 false。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -88,7 +88,7 @@ public sealed class UpdateBridge
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return JsonSerializer.Deserialize(File.ReadAllBytes(_statusFile), MonitorJsonContext.Default.UpdateStatus);
|
return JsonSerializer.Deserialize(File.ReadAllBytes(_statusFile), MoShiroJsonContext.Default.UpdateStatus);
|
||||||
}
|
}
|
||||||
catch (Exception ex) when (ex is IOException or UnauthorizedAccessException or JsonException)
|
catch (Exception ex) when (ex is IOException or UnauthorizedAccessException or JsonException)
|
||||||
{
|
{
|
||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
namespace Monitor.Protocol;
|
namespace MoShiro.Protocol;
|
||||||
|
|
||||||
[Flags]
|
[Flags]
|
||||||
public enum Scope
|
public enum Scope
|
||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
namespace Monitor.Protocol;
|
namespace MoShiro.Protocol;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Kind 目录。新增一种采集方法 = 在这里加一个静态字段并放进 <see cref="All"/>;
|
/// Kind 目录。新增一种采集方法 = 在这里加一个静态字段并放进 <see cref="All"/>;
|
||||||
+1
-1
@@ -1,7 +1,7 @@
|
|||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
|
|
||||||
namespace Monitor.Protocol;
|
namespace MoShiro.Protocol;
|
||||||
|
|
||||||
public enum ParamType
|
public enum ParamType
|
||||||
{
|
{
|
||||||
+1
-1
@@ -1,7 +1,7 @@
|
|||||||
using System.Text.Json.Serialization;
|
using System.Text.Json.Serialization;
|
||||||
using Fleet.Core;
|
using Fleet.Core;
|
||||||
|
|
||||||
namespace Monitor.Protocol;
|
namespace MoShiro.Protocol;
|
||||||
|
|
||||||
[JsonConverter(typeof(ProbeIdJsonConverter))]
|
[JsonConverter(typeof(ProbeIdJsonConverter))]
|
||||||
public readonly record struct ProbeId(string Value)
|
public readonly record struct ProbeId(string Value)
|
||||||
+5
-5
@@ -7,7 +7,7 @@
|
|||||||
<ProjectReference Include="..\..\fleet\src\Fleet.Rpc\Fleet.Rpc.csproj" />
|
<ProjectReference Include="..\..\fleet\src\Fleet.Rpc\Fleet.Rpc.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<!--
|
<!--
|
||||||
构建信息:版本来自 $(Version),tag、提交、构建时间来自环境变量 MONITOR_TAG / MONITOR_COMMIT / MONITOR_BUILT_AT
|
构建信息:版本来自 $(Version),tag、提交、构建时间来自环境变量 MOSHIRO_TAG / MOSHIRO_COMMIT / MOSHIRO_BUILT_AT
|
||||||
(scripts/publish.sh 和 CI 会设置;本地开发不设置就是空,这样本地构建是可重复的、不会每次都重新编译)。
|
(scripts/publish.sh 和 CI 会设置;本地开发不设置就是空,这样本地构建是可重复的、不会每次都重新编译)。
|
||||||
生成一个带常量的源文件,NativeAOT 下不用反射。
|
生成一个带常量的源文件,NativeAOT 下不用反射。
|
||||||
-->
|
-->
|
||||||
@@ -16,14 +16,14 @@
|
|||||||
<BuildInfoFile>$(IntermediateOutputPath)BuildInfo.g.cs</BuildInfoFile>
|
<BuildInfoFile>$(IntermediateOutputPath)BuildInfo.g.cs</BuildInfoFile>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<BuildInfoLine Include="namespace Monitor.Protocol%3B" />
|
<BuildInfoLine Include="namespace MoShiro.Protocol%3B" />
|
||||||
<BuildInfoLine Include="/// <summary>这个程序是什么时候、从哪个版本构建出来的(构建时生成)。</summary>" />
|
<BuildInfoLine Include="/// <summary>这个程序是什么时候、从哪个版本构建出来的(构建时生成)。</summary>" />
|
||||||
<BuildInfoLine Include="public static class BuildInfo" />
|
<BuildInfoLine Include="public static class BuildInfo" />
|
||||||
<BuildInfoLine Include="{" />
|
<BuildInfoLine Include="{" />
|
||||||
<BuildInfoLine Include=" public const string Version = "$(Version)"%3B" />
|
<BuildInfoLine Include=" public const string Version = "$(Version)"%3B" />
|
||||||
<BuildInfoLine Include=" public const string Tag = "$(MONITOR_TAG)"%3B" />
|
<BuildInfoLine Include=" public const string Tag = "$(MOSHIRO_TAG)"%3B" />
|
||||||
<BuildInfoLine Include=" public const string Commit = "$(MONITOR_COMMIT)"%3B" />
|
<BuildInfoLine Include=" public const string Commit = "$(MOSHIRO_COMMIT)"%3B" />
|
||||||
<BuildInfoLine Include=" public const string BuiltAt = "$(MONITOR_BUILT_AT)"%3B" />
|
<BuildInfoLine Include=" public const string BuiltAt = "$(MOSHIRO_BUILT_AT)"%3B" />
|
||||||
<BuildInfoLine Include="}" />
|
<BuildInfoLine Include="}" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<WriteLinesToFile File="$(BuildInfoFile)" Lines="@(BuildInfoLine)" Overwrite="true" WriteOnlyWhenDifferent="true" />
|
<WriteLinesToFile File="$(BuildInfoFile)" Lines="@(BuildInfoLine)" Overwrite="true" WriteOnlyWhenDifferent="true" />
|
||||||
+3
-3
@@ -1,9 +1,9 @@
|
|||||||
using Fleet.Core;
|
using Fleet.Core;
|
||||||
|
|
||||||
namespace Monitor.Protocol;
|
namespace MoShiro.Protocol;
|
||||||
|
|
||||||
/// <summary>Monitor 的错误码(在 Fleet 错误码之上扩展)。REST、JSON-RPC、OpenAPI 共用同一份。</summary>
|
/// <summary>MoShiro 的错误码(在 Fleet 错误码之上扩展)。REST、JSON-RPC、OpenAPI 共用同一份。</summary>
|
||||||
public static class MonitorErrors
|
public static class MoShiroErrors
|
||||||
{
|
{
|
||||||
public static readonly ErrorCode ValidationFailed = new("validation_failed");
|
public static readonly ErrorCode ValidationFailed = new("validation_failed");
|
||||||
public static readonly ErrorCode BudgetExceeded = new("budget_exceeded");
|
public static readonly ErrorCode BudgetExceeded = new("budget_exceeded");
|
||||||
+7
-7
@@ -1,4 +1,4 @@
|
|||||||
namespace Monitor.Protocol;
|
namespace MoShiro.Protocol;
|
||||||
|
|
||||||
public static class ProtocolVersion
|
public static class ProtocolVersion
|
||||||
{
|
{
|
||||||
@@ -25,8 +25,8 @@ public static class ProtocolLimits
|
|||||||
public const long MaxLineBudgetBitsPerSecond = 100_000;
|
public const long MaxLineBudgetBitsPerSecond = 100_000;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>二进制通道号(0–15 属于 Fleet,Monitor 从 16 开始)。</summary>
|
/// <summary>二进制通道号(0–15 属于 Fleet,MoShiro 从 16 开始)。</summary>
|
||||||
public static class MonitorChannels
|
public static class MoShiroChannels
|
||||||
{
|
{
|
||||||
/// <summary>Agent → Backend:RawChunk(发件箱)。</summary>
|
/// <summary>Agent → Backend:RawChunk(发件箱)。</summary>
|
||||||
public const byte RawChunk = 16;
|
public const byte RawChunk = 16;
|
||||||
@@ -35,9 +35,9 @@ public static class MonitorChannels
|
|||||||
public const byte ColumnBlock = 17;
|
public const byte ColumnBlock = 17;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class MonitorCapabilities
|
public static class MoShiroCapabilities
|
||||||
{
|
{
|
||||||
public const string Base = "monitor.v1";
|
public const string Base = "moshiro.v1";
|
||||||
public const string KindsPrefix = "monitor.kinds:";
|
public const string KindsPrefix = "moshiro.kinds:";
|
||||||
public const string BlocksPrefix = "monitor.blocks:";
|
public const string BlocksPrefix = "moshiro.blocks:";
|
||||||
}
|
}
|
||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
namespace Monitor.Protocol;
|
namespace MoShiro.Protocol;
|
||||||
|
|
||||||
/// <summary>CRC-32C(Castagnoli),RawChunk 的完整性校验。</summary>
|
/// <summary>CRC-32C(Castagnoli),RawChunk 的完整性校验。</summary>
|
||||||
public static class Crc32c
|
public static class Crc32c
|
||||||
+1
-1
@@ -2,7 +2,7 @@ using System.Text.Json;
|
|||||||
using System.Text.Json.Serialization;
|
using System.Text.Json.Serialization;
|
||||||
using Fleet.Core;
|
using Fleet.Core;
|
||||||
|
|
||||||
namespace Monitor.Protocol;
|
namespace MoShiro.Protocol;
|
||||||
|
|
||||||
/// <summary>Agent 单调时钟与 UTC 的锚点,以及 Probe 时钟相对 Agent 时钟的偏移估计。</summary>
|
/// <summary>Agent 单调时钟与 UTC 的锚点,以及 Probe 时钟相对 Agent 时钟的偏移估计。</summary>
|
||||||
public sealed class ChunkClock
|
public sealed class ChunkClock
|
||||||
+6
-6
@@ -3,18 +3,18 @@ using System.IO.Compression;
|
|||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
using Fleet.Core;
|
using Fleet.Core;
|
||||||
|
|
||||||
namespace Monitor.Protocol;
|
namespace MoShiro.Protocol;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// RawChunk 的编解码(Probe、Agent、Backend、离线工具共用同一份代码)。
|
/// RawChunk 的编解码(Probe、Agent、Backend、离线工具共用同一份代码)。
|
||||||
/// 布局:<c>"MNRC" | u8 container_version | u32 crc32c(压缩数据) | Brotli( u32 header_len | header JSON | 各列数据 )</c>。
|
/// 布局:<c>"MSRC" | u8 container_version | u32 crc32c(压缩数据) | Brotli( u32 header_len | header JSON | 各列数据 )</c>。
|
||||||
/// 列数据:可空列先写有效位图,再写有效值;plain 为无符号 varint(有符号类型用 zigzag),delta 为与上一个有效值之差的 zigzag varint。
|
/// 列数据:可空列先写有效位图,再写有效值;plain 为无符号 varint(有符号类型用 zigzag),delta 为与上一个有效值之差的 zigzag varint。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static class RawChunkCodec
|
public static class RawChunkCodec
|
||||||
{
|
{
|
||||||
private const int PrefixBytes = 4 + 1 + 4;
|
private const int PrefixBytes = 4 + 1 + 4;
|
||||||
|
|
||||||
private static ReadOnlySpan<byte> Magic => "MNRC"u8;
|
private static ReadOnlySpan<byte> Magic => "MSRC"u8;
|
||||||
|
|
||||||
/// <summary>压缩之前的正文:<c>u32 header_len | header JSON | 各列数据</c>。布局变化时黄金字节测试会失败,提醒升级版本。</summary>
|
/// <summary>压缩之前的正文:<c>u32 header_len | header JSON | 各列数据</c>。布局变化时黄金字节测试会失败,提醒升级版本。</summary>
|
||||||
public static byte[] EncodeBody(RawChunk chunk)
|
public static byte[] EncodeBody(RawChunk chunk)
|
||||||
@@ -40,7 +40,7 @@ public static class RawChunkCodec
|
|||||||
header.Streams.Add(streamHeader);
|
header.Streams.Add(streamHeader);
|
||||||
}
|
}
|
||||||
|
|
||||||
byte[] headerJson = JsonSerializer.SerializeToUtf8Bytes(header, MonitorJsonContext.Default.RawChunkHeader);
|
byte[] headerJson = JsonSerializer.SerializeToUtf8Bytes(header, MoShiroJsonContext.Default.RawChunkHeader);
|
||||||
|
|
||||||
using var body = new MemoryStream();
|
using var body = new MemoryStream();
|
||||||
Span<byte> length = stackalloc byte[4];
|
Span<byte> length = stackalloc byte[4];
|
||||||
@@ -114,7 +114,7 @@ public static class RawChunkCodec
|
|||||||
RawChunkHeader? header;
|
RawChunkHeader? header;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
header = JsonSerializer.Deserialize(plain.AsSpan(4, (int)headerLength), MonitorJsonContext.Default.RawChunkHeader);
|
header = JsonSerializer.Deserialize(plain.AsSpan(4, (int)headerLength), MoShiroJsonContext.Default.RawChunkHeader);
|
||||||
}
|
}
|
||||||
catch (JsonException)
|
catch (JsonException)
|
||||||
{
|
{
|
||||||
@@ -159,7 +159,7 @@ public static class RawChunkCodec
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static Result<RawChunk> Corrupt(string message) =>
|
private static Result<RawChunk> Corrupt(string message) =>
|
||||||
Result<RawChunk>.Fail(MonitorErrors.RawCorrupt, message);
|
Result<RawChunk>.Fail(MoShiroErrors.RawCorrupt, message);
|
||||||
|
|
||||||
private static byte[]? Inflate(ReadOnlySpan<byte> payload)
|
private static byte[]? Inflate(ReadOnlySpan<byte> payload)
|
||||||
{
|
{
|
||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
namespace Monitor.Protocol;
|
namespace MoShiro.Protocol;
|
||||||
|
|
||||||
/// <summary>按 <see cref="StreamSpec"/> 逐行构造一个 <see cref="RawStream"/>(用于测试、模拟器和离线工具,不用于节点热路径)。</summary>
|
/// <summary>按 <see cref="StreamSpec"/> 逐行构造一个 <see cref="RawStream"/>(用于测试、模拟器和离线工具,不用于节点热路径)。</summary>
|
||||||
public sealed class StreamBuilder
|
public sealed class StreamBuilder
|
||||||
+19
-19
@@ -2,25 +2,25 @@ using System.Text.Json;
|
|||||||
using System.Text.Json.Serialization;
|
using System.Text.Json.Serialization;
|
||||||
using Fleet.Core;
|
using Fleet.Core;
|
||||||
|
|
||||||
namespace Monitor.Protocol;
|
namespace MoShiro.Protocol;
|
||||||
|
|
||||||
/// <summary>monitor.* 方法名。新增方法 = 在这里定义名称和 params/result,再在组装处加一行 <c>.On(...)</c>。</summary>
|
/// <summary>moshiro.* 方法名。新增方法 = 在这里定义名称和 params/result,再在组装处加一行 <c>.On(...)</c>。</summary>
|
||||||
public static class MonitorMethods
|
public static class MoShiroMethods
|
||||||
{
|
{
|
||||||
public const string TestRun = "monitor.test.run";
|
public const string TestRun = "moshiro.test.run";
|
||||||
public const string TestCancel = "monitor.test.cancel";
|
public const string TestCancel = "moshiro.test.cancel";
|
||||||
public const string ProbeDisconnect = "monitor.probe.disconnect";
|
public const string ProbeDisconnect = "moshiro.probe.disconnect";
|
||||||
public const string TestStatus = "monitor.test.status";
|
public const string TestStatus = "moshiro.test.status";
|
||||||
public const string Event = "monitor.event";
|
public const string Event = "moshiro.event";
|
||||||
public const string Host = "monitor.host";
|
public const string Host = "moshiro.host";
|
||||||
public const string UpdateOffer = "monitor.update.offer";
|
public const string UpdateOffer = "moshiro.update.offer";
|
||||||
public const string UpdateApply = "monitor.update.apply";
|
public const string UpdateApply = "moshiro.update.apply";
|
||||||
|
|
||||||
// Probe ↔ Agent(点对点会话)
|
// Probe ↔ Agent(点对点会话)
|
||||||
public const string Clock = "monitor.clock";
|
public const string Clock = "moshiro.clock";
|
||||||
public const string TestPhase = "monitor.test.phase";
|
public const string TestPhase = "moshiro.test.phase";
|
||||||
public const string TestPhaseEnd = "monitor.test.phase_end";
|
public const string TestPhaseEnd = "moshiro.test.phase_end";
|
||||||
public const string TestAbort = "monitor.test.abort";
|
public const string TestAbort = "moshiro.test.abort";
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class AgentEventKinds
|
public static class AgentEventKinds
|
||||||
@@ -159,7 +159,7 @@ public sealed class ProbeAgentList
|
|||||||
public List<AgentEndpoint> Agents { get; set; } = [];
|
public List<AgentEndpoint> Agents { get; set; } = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>注册元数据(Monitor 定义,经 Fleet 的 <c>meta</c> 字段带上来)。</summary>
|
/// <summary>注册元数据(MoShiro 定义,经 Fleet 的 <c>meta</c> 字段带上来)。</summary>
|
||||||
public sealed class AgentEnrollMeta
|
public sealed class AgentEnrollMeta
|
||||||
{
|
{
|
||||||
[JsonPropertyName("probe_tcp_port")]
|
[JsonPropertyName("probe_tcp_port")]
|
||||||
@@ -220,7 +220,7 @@ public sealed class PlatformInfo
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 面板给节点的更新清单(<c>monitor.update.offer</c>):<c>manifest</c> 是发布清单的原始文本,<c>signature</c> 是 base64 的签名。
|
/// 面板给节点的更新清单(<c>moshiro.update.offer</c>):<c>manifest</c> 是发布清单的原始文本,<c>signature</c> 是 base64 的签名。
|
||||||
/// 节点只是把它们原样放进数据目录;验签、拉取、重启都由宿主机上的更新器完成(节点进程不执行任何命令,也不信任清单的内容)。
|
/// 节点只是把它们原样放进数据目录;验签、拉取、重启都由宿主机上的更新器完成(节点进程不执行任何命令,也不信任清单的内容)。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public sealed class UpdateOfferParams
|
public sealed class UpdateOfferParams
|
||||||
@@ -436,7 +436,7 @@ public sealed class LineTaskSet
|
|||||||
public List<int> TcpPorts { get; set; } = [];
|
public List<int> TcpPorts { get; set; } = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary><c>monitor.clock</c>:Probe 用它估计与 Agent 单调时钟的偏移(对称往返假设)。</summary>
|
/// <summary><c>moshiro.clock</c>:Probe 用它估计与 Agent 单调时钟的偏移(对称往返假设)。</summary>
|
||||||
public sealed class ClockParams
|
public sealed class ClockParams
|
||||||
{
|
{
|
||||||
[JsonPropertyName("probe_mono_us")]
|
[JsonPropertyName("probe_mono_us")]
|
||||||
@@ -543,4 +543,4 @@ public static class FragmentKeys
|
|||||||
[JsonSerializable(typeof(AgentHelloMeta))]
|
[JsonSerializable(typeof(AgentHelloMeta))]
|
||||||
[JsonSerializable(typeof(AgentHeartbeatExtra))]
|
[JsonSerializable(typeof(AgentHeartbeatExtra))]
|
||||||
[JsonSerializable(typeof(RawChunkHeader))]
|
[JsonSerializable(typeof(RawChunkHeader))]
|
||||||
public sealed partial class MonitorJsonContext : JsonSerializerContext;
|
public sealed partial class MoShiroJsonContext : JsonSerializerContext;
|
||||||
+3
-3
@@ -3,7 +3,7 @@ using System.Text.Json;
|
|||||||
using System.Text.Json.Serialization;
|
using System.Text.Json.Serialization;
|
||||||
using Fleet.Core;
|
using Fleet.Core;
|
||||||
|
|
||||||
namespace Monitor.Protocol;
|
namespace MoShiro.Protocol;
|
||||||
|
|
||||||
/// <summary>Task = { id, scope, kind, target, params, schedule, enabled }。参数已经按 ParamSpec 校验并补全默认值。</summary>
|
/// <summary>Task = { id, scope, kind, target, params, schedule, enabled }。参数已经按 ParamSpec 校验并补全默认值。</summary>
|
||||||
public sealed class TaskSpec
|
public sealed class TaskSpec
|
||||||
@@ -79,9 +79,9 @@ public static class TaskSchedule
|
|||||||
{
|
{
|
||||||
return minutes >= 60
|
return minutes >= 60
|
||||||
? Result<int?>.Ok(minutes)
|
? Result<int?>.Ok(minutes)
|
||||||
: Result<int?>.Fail(MonitorErrors.ValidationFailed, "every:N requires N >= 60 minutes");
|
: Result<int?>.Fail(MoShiroErrors.ValidationFailed, "every:N requires N >= 60 minutes");
|
||||||
}
|
}
|
||||||
|
|
||||||
return Result<int?>.Fail(MonitorErrors.ValidationFailed, "schedule must be continuous, once or every:<minutes>");
|
return Result<int?>.Fail(MoShiroErrors.ValidationFailed, "schedule must be continuous, once or every:<minutes>");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+1
-1
@@ -2,7 +2,7 @@ using System.Buffers.Binary;
|
|||||||
using System.Security.Cryptography;
|
using System.Security.Cryptography;
|
||||||
using Fleet.Core;
|
using Fleet.Core;
|
||||||
|
|
||||||
namespace Monitor.Protocol;
|
namespace MoShiro.Protocol;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 数据面的 TCP 连接(Fleet 之外的线格式)。连接都由 Probe 发起;TLS 是可选的(ClientHello 以 0x16 开头,
|
/// 数据面的 TCP 连接(Fleet 之外的线格式)。连接都由 Probe 发起;TLS 是可选的(ClientHello 以 0x16 开头,
|
||||||
+1
-1
@@ -1,7 +1,7 @@
|
|||||||
using System.Buffers.Binary;
|
using System.Buffers.Binary;
|
||||||
using System.Security.Cryptography;
|
using System.Security.Cryptography;
|
||||||
|
|
||||||
namespace Monitor.Protocol;
|
namespace MoShiro.Protocol;
|
||||||
|
|
||||||
public enum UdpPacketType : byte
|
public enum UdpPacketType : byte
|
||||||
{
|
{
|
||||||
+2
-2
@@ -1,8 +1,8 @@
|
|||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
using Fleet.Core;
|
using Fleet.Core;
|
||||||
using Monitor.Protocol;
|
using MoShiro.Protocol;
|
||||||
|
|
||||||
namespace Monitor.Measurement.Tests;
|
namespace MoShiro.Measurement.Tests;
|
||||||
|
|
||||||
public sealed class MeasurementTests
|
public sealed class MeasurementTests
|
||||||
{
|
{
|
||||||
+1
-1
@@ -5,7 +5,7 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="xunit.v3" />
|
<PackageReference Include="xunit.v3" />
|
||||||
<ProjectReference Include="..\..\Monitor.Measurement\Monitor.Measurement.csproj" />
|
<ProjectReference Include="..\..\MoShiro.Measurement\MoShiro.Measurement.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Using Include="Xunit" />
|
<Using Include="Xunit" />
|
||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
namespace Monitor.Measurement.Tests;
|
namespace MoShiro.Measurement.Tests;
|
||||||
|
|
||||||
public sealed class PlatformDetectorTests
|
public sealed class PlatformDetectorTests
|
||||||
{
|
{
|
||||||
+6
-6
@@ -1,8 +1,8 @@
|
|||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
using Fleet.Core;
|
using Fleet.Core;
|
||||||
using Monitor.Protocol;
|
using MoShiro.Protocol;
|
||||||
|
|
||||||
namespace Monitor.Protocol.Tests;
|
namespace MoShiro.Protocol.Tests;
|
||||||
|
|
||||||
public sealed class CatalogTests
|
public sealed class CatalogTests
|
||||||
{
|
{
|
||||||
@@ -101,8 +101,8 @@ public sealed class CatalogTests
|
|||||||
Tasks = [new TaskSpec { Id = new TaskId("tsk_1"), Kind = "udp_echo", Params = new Dictionary<string, JsonElement> { ["rate_hz"] = Json("10") } }],
|
Tasks = [new TaskSpec { Id = new TaskId("tsk_1"), Kind = "udp_echo", Params = new Dictionary<string, JsonElement> { ["rate_hz"] = Json("10") } }],
|
||||||
Peers = [new PeerSpec { ProbeId = new ProbeId("prb_1"), PublicKey = "AAAA" }],
|
Peers = [new PeerSpec { ProbeId = new ProbeId("prb_1"), PublicKey = "AAAA" }],
|
||||||
};
|
};
|
||||||
string text = JsonSerializer.Serialize(set, MonitorJsonContext.Default.TaskSet);
|
string text = JsonSerializer.Serialize(set, MoShiroJsonContext.Default.TaskSet);
|
||||||
TaskSet back = JsonSerializer.Deserialize(text, MonitorJsonContext.Default.TaskSet)!;
|
TaskSet back = JsonSerializer.Deserialize(text, MoShiroJsonContext.Default.TaskSet)!;
|
||||||
|
|
||||||
Assert.Contains("\"id\":\"tsk_1\"", text);
|
Assert.Contains("\"id\":\"tsk_1\"", text);
|
||||||
Assert.Equal(7, back.Version);
|
Assert.Equal(7, back.Version);
|
||||||
@@ -114,7 +114,7 @@ public sealed class CatalogTests
|
|||||||
[Fact]
|
[Fact]
|
||||||
public void AgentEnrollMeta_ToleratesUnknownFields_AndKeepsDefaults()
|
public void AgentEnrollMeta_ToleratesUnknownFields_AndKeepsDefaults()
|
||||||
{
|
{
|
||||||
AgentEnrollMeta meta = JsonSerializer.Deserialize("{\"kinds\":[\"udp_echo\"],\"future\":1}", MonitorJsonContext.Default.AgentEnrollMeta)!;
|
AgentEnrollMeta meta = JsonSerializer.Deserialize("{\"kinds\":[\"udp_echo\"],\"future\":1}", MoShiroJsonContext.Default.AgentEnrollMeta)!;
|
||||||
Assert.Equal(7443, meta.ProbeTcpPort);
|
Assert.Equal(7443, meta.ProbeTcpPort);
|
||||||
Assert.Equal(["udp_echo"], meta.Kinds);
|
Assert.Equal(["udp_echo"], meta.Kinds);
|
||||||
}
|
}
|
||||||
@@ -122,7 +122,7 @@ public sealed class CatalogTests
|
|||||||
[Fact]
|
[Fact]
|
||||||
public void ErrorCodes_AreSnakeCase()
|
public void ErrorCodes_AreSnakeCase()
|
||||||
{
|
{
|
||||||
foreach (System.Reflection.FieldInfo field in typeof(MonitorErrors).GetFields())
|
foreach (System.Reflection.FieldInfo field in typeof(MoShiroErrors).GetFields())
|
||||||
{
|
{
|
||||||
string value = ((ErrorCode)field.GetValue(null)!).Value;
|
string value = ((ErrorCode)field.GetValue(null)!).Value;
|
||||||
Assert.Matches("^[a-z]+(_[a-z]+)*$", value);
|
Assert.Matches("^[a-z]+(_[a-z]+)*$", value);
|
||||||
+2
-2
@@ -5,8 +5,8 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="xunit.v3" />
|
<PackageReference Include="xunit.v3" />
|
||||||
<ProjectReference Include="..\..\Monitor.Protocol\Monitor.Protocol.csproj" />
|
<ProjectReference Include="..\..\MoShiro.Protocol\MoShiro.Protocol.csproj" />
|
||||||
<ProjectReference Include="..\..\..\tests\Monitor.TestSupport\Monitor.TestSupport.csproj" />
|
<ProjectReference Include="..\..\..\tests\MoShiro.TestSupport\MoShiro.TestSupport.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Using Include="Xunit" />
|
<Using Include="Xunit" />
|
||||||
+5
-5
@@ -1,10 +1,10 @@
|
|||||||
using System.Buffers.Binary;
|
using System.Buffers.Binary;
|
||||||
using System.IO.Compression;
|
using System.IO.Compression;
|
||||||
using Fleet.Core;
|
using Fleet.Core;
|
||||||
using Monitor.Protocol;
|
using MoShiro.Protocol;
|
||||||
using Monitor.TestSupport;
|
using MoShiro.TestSupport;
|
||||||
|
|
||||||
namespace Monitor.Protocol.Tests;
|
namespace MoShiro.Protocol.Tests;
|
||||||
|
|
||||||
public sealed class RawChunkCodecTests
|
public sealed class RawChunkCodecTests
|
||||||
{
|
{
|
||||||
@@ -103,7 +103,7 @@ public sealed class RawChunkCodecTests
|
|||||||
byte[] badCrc = (byte[])good.Clone();
|
byte[] badCrc = (byte[])good.Clone();
|
||||||
badCrc[^1] ^= 0xFF;
|
badCrc[^1] ^= 0xFF;
|
||||||
|
|
||||||
Assert.Equal(MonitorErrors.RawCorrupt, RawChunkCodec.Decode(badMagic).Error.Code);
|
Assert.Equal(MoShiroErrors.RawCorrupt, RawChunkCodec.Decode(badMagic).Error.Code);
|
||||||
Assert.Contains("version", RawChunkCodec.Decode(badVersion).Error.Message);
|
Assert.Contains("version", RawChunkCodec.Decode(badVersion).Error.Message);
|
||||||
Assert.Contains("crc", RawChunkCodec.Decode(badCrc).Error.Message);
|
Assert.Contains("crc", RawChunkCodec.Decode(badCrc).Error.Message);
|
||||||
Assert.False(RawChunkCodec.Decode(good.AsSpan(0, good.Length / 2)).IsSuccess);
|
Assert.False(RawChunkCodec.Decode(good.AsSpan(0, good.Length / 2)).IsSuccess);
|
||||||
@@ -190,7 +190,7 @@ public sealed class RawChunkCodecTests
|
|||||||
private static byte[] Wrap(byte[] payload)
|
private static byte[] Wrap(byte[] payload)
|
||||||
{
|
{
|
||||||
byte[] result = new byte[9 + payload.Length];
|
byte[] result = new byte[9 + payload.Length];
|
||||||
"MNRC"u8.CopyTo(result);
|
"MSRC"u8.CopyTo(result);
|
||||||
result[4] = ProtocolVersion.Container;
|
result[4] = ProtocolVersion.Container;
|
||||||
BinaryPrimitives.WriteUInt32LittleEndian(result.AsSpan(5), Crc32c.Compute(payload));
|
BinaryPrimitives.WriteUInt32LittleEndian(result.AsSpan(5), Crc32c.Compute(payload));
|
||||||
payload.CopyTo(result, 9);
|
payload.CopyTo(result, 9);
|
||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
namespace Monitor.Backend.Analysis;
|
namespace MoShiro.Backend.Analysis;
|
||||||
|
|
||||||
public enum AccKind : byte
|
public enum AccKind : byte
|
||||||
{
|
{
|
||||||
+1
-1
@@ -2,7 +2,7 @@ using System.Buffers.Binary;
|
|||||||
using System.Text;
|
using System.Text;
|
||||||
using Fleet.Core;
|
using Fleet.Core;
|
||||||
|
|
||||||
namespace Monitor.Backend.Analysis;
|
namespace MoShiro.Backend.Analysis;
|
||||||
|
|
||||||
/// <summary>一个时间桶里所有指标的累加器:<c>指标 id → 累加器</c>。</summary>
|
/// <summary>一个时间桶里所有指标的累加器:<c>指标 id → 累加器</c>。</summary>
|
||||||
public sealed class MetricSet
|
public sealed class MetricSet
|
||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
namespace Monitor.Backend.Analysis;
|
namespace MoShiro.Backend.Analysis;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 固定对数桶直方图:100 µs ~ 30 s,每 2 倍 8 个桶(相对误差 ≤ 4.5%)。
|
/// 固定对数桶直方图:100 µs ~ 30 s,每 2 倍 8 个桶(相对误差 ≤ 4.5%)。
|
||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
namespace Monitor.Backend.Analysis;
|
namespace MoShiro.Backend.Analysis;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 估计两端的时钟偏移和线性漂移:单向时延 x(t) = 偏移 + 漂移·t + 真实的时延变化。
|
/// 估计两端的时钟偏移和线性漂移:单向时延 x(t) = 偏移 + 漂移·t + 真实的时延变化。
|
||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
namespace Monitor.Backend.Analysis;
|
namespace MoShiro.Backend.Analysis;
|
||||||
|
|
||||||
/// <summary>变点检测(均值漂移):找到使前后两段均值差最显著的位置,用来判断 QoS 从什么时候开始限速。</summary>
|
/// <summary>变点检测(均值漂移):找到使前后两段均值差最显著的位置,用来判断 QoS 从什么时候开始限速。</summary>
|
||||||
public static class ChangePoint
|
public static class ChangePoint
|
||||||
+2
-2
@@ -1,7 +1,7 @@
|
|||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
using Monitor.Protocol;
|
using MoShiro.Protocol;
|
||||||
|
|
||||||
namespace Monitor.Backend.Analysis;
|
namespace MoShiro.Backend.Analysis;
|
||||||
|
|
||||||
/// <summary>算子把观测值交给这里;窗口评估器负责按时间分桶。</summary>
|
/// <summary>算子把观测值交给这里;窗口评估器负责按时间分桶。</summary>
|
||||||
public interface IObservationSink
|
public interface IObservationSink
|
||||||
+2
-2
@@ -1,8 +1,8 @@
|
|||||||
using System.Security.Cryptography;
|
using System.Security.Cryptography;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using Monitor.Protocol;
|
using MoShiro.Protocol;
|
||||||
|
|
||||||
namespace Monitor.Backend.Analysis;
|
namespace MoShiro.Backend.Analysis;
|
||||||
|
|
||||||
public enum Unit
|
public enum Unit
|
||||||
{
|
{
|
||||||
+2
-2
@@ -1,7 +1,7 @@
|
|||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
using Monitor.Protocol;
|
using MoShiro.Protocol;
|
||||||
|
|
||||||
namespace Monitor.Backend.Analysis;
|
namespace MoShiro.Backend.Analysis;
|
||||||
|
|
||||||
public sealed class TestStepResult
|
public sealed class TestStepResult
|
||||||
{
|
{
|
||||||
+2
-2
@@ -1,6 +1,6 @@
|
|||||||
using Monitor.Protocol;
|
using MoShiro.Protocol;
|
||||||
|
|
||||||
namespace Monitor.Backend.Analysis;
|
namespace MoShiro.Backend.Analysis;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 把原始块按时间分桶算成累加器。流式路径(1s 桶)和持久路径(分钟窗口)调用同一份代码,
|
/// 把原始块按时间分桶算成累加器。流式路径(1s 桶)和持久路径(分钟窗口)调用同一份代码,
|
||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
namespace Monitor.Backend.Analysis;
|
namespace MoShiro.Backend.Analysis;
|
||||||
|
|
||||||
/// <summary>线路基础指标(udp_echo / icmp_echo 的回显流)。新增指标 = 加一行声明。</summary>
|
/// <summary>线路基础指标(udp_echo / icmp_echo 的回显流)。新增指标 = 加一行声明。</summary>
|
||||||
public static class LinkMetrics
|
public static class LinkMetrics
|
||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
namespace Monitor.Backend.Analysis;
|
namespace MoShiro.Backend.Analysis;
|
||||||
|
|
||||||
public sealed record PresetDef(string Id, string Label, IReadOnlyList<string> MetricIds);
|
public sealed record PresetDef(string Id, string Label, IReadOnlyList<string> MetricIds);
|
||||||
|
|
||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
namespace Monitor.Backend.Analysis;
|
namespace MoShiro.Backend.Analysis;
|
||||||
|
|
||||||
/// <summary>连接建立、长连接、UDP 压测(udp_train)的指标。</summary>
|
/// <summary>连接建立、长连接、UDP 压测(udp_train)的指标。</summary>
|
||||||
public static class SessionMetrics
|
public static class SessionMetrics
|
||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
namespace Monitor.Backend.Analysis;
|
namespace MoShiro.Backend.Analysis;
|
||||||
|
|
||||||
/// <summary>TCP 指标:回显的 RTT 与卡顿、内核采样的重传、批量传输的吞吐。</summary>
|
/// <summary>TCP 指标:回显的 RTT 与卡顿、内核采样的重传、批量传输的吞吐。</summary>
|
||||||
public static class TcpMetrics
|
public static class TcpMetrics
|
||||||
+1
-1
@@ -3,6 +3,6 @@
|
|||||||
<IsAotCompatible>true</IsAotCompatible>
|
<IsAotCompatible>true</IsAotCompatible>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\..\shared\Monitor.Protocol\Monitor.Protocol.csproj" />
|
<ProjectReference Include="..\..\shared\MoShiro.Protocol\MoShiro.Protocol.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
namespace Monitor.Backend.Analysis;
|
namespace MoShiro.Backend.Analysis;
|
||||||
|
|
||||||
/// <summary>线路基础质量:RTT、丢包、抖动、连续丢包。</summary>
|
/// <summary>线路基础质量:RTT、丢包、抖动、连续丢包。</summary>
|
||||||
public sealed class BaseModel : ServiceModelBase
|
public sealed class BaseModel : ServiceModelBase
|
||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
namespace Monitor.Backend.Analysis;
|
namespace MoShiro.Backend.Analysis;
|
||||||
|
|
||||||
/// <summary>模型的输入:一个时间段内的导出值(例如 <c>rtt.p95</c>)。没有数据的指标不存在。</summary>
|
/// <summary>模型的输入:一个时间段内的导出值(例如 <c>rtt.p95</c>)。没有数据的指标不存在。</summary>
|
||||||
public sealed class MetricView
|
public sealed class MetricView
|
||||||
+2
-2
@@ -1,6 +1,6 @@
|
|||||||
using Monitor.Protocol;
|
using MoShiro.Protocol;
|
||||||
|
|
||||||
namespace Monitor.Backend.Analysis;
|
namespace MoShiro.Backend.Analysis;
|
||||||
|
|
||||||
public enum Dir
|
public enum Dir
|
||||||
{
|
{
|
||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
namespace Monitor.Backend.Analysis;
|
namespace MoShiro.Backend.Analysis;
|
||||||
|
|
||||||
/// <summary>算子工厂:<c>Ops.Rtt</c>、<c>Ops.Loss(Dir.Up)</c> 等,供 <c>MetricDef</c> 声明使用。</summary>
|
/// <summary>算子工厂:<c>Ops.Rtt</c>、<c>Ops.Loss(Dir.Up)</c> 等,供 <c>MetricDef</c> 声明使用。</summary>
|
||||||
public static class Ops
|
public static class Ops
|
||||||
+2
-2
@@ -1,6 +1,6 @@
|
|||||||
using Monitor.Protocol;
|
using MoShiro.Protocol;
|
||||||
|
|
||||||
namespace Monitor.Backend.Analysis;
|
namespace MoShiro.Backend.Analysis;
|
||||||
|
|
||||||
/// <summary>tcp_info 流的辅助:<c>side</c> 为 0 表示 Probe 一侧记录(Probe 时钟),否则为 Agent 一侧。</summary>
|
/// <summary>tcp_info 流的辅助:<c>side</c> 为 0 表示 Probe 一侧记录(Probe 时钟),否则为 Agent 一侧。</summary>
|
||||||
internal static class TcpInfoTime
|
internal static class TcpInfoTime
|
||||||
+6
-6
@@ -2,10 +2,10 @@ using Fleet.Core;
|
|||||||
using Microsoft.AspNetCore.Builder;
|
using Microsoft.AspNetCore.Builder;
|
||||||
using Microsoft.AspNetCore.Http;
|
using Microsoft.AspNetCore.Http;
|
||||||
using Microsoft.AspNetCore.Routing;
|
using Microsoft.AspNetCore.Routing;
|
||||||
using Monitor.Backend.Application;
|
using MoShiro.Backend.Application;
|
||||||
using Monitor.Protocol;
|
using MoShiro.Protocol;
|
||||||
|
|
||||||
namespace Monitor.Backend.Api;
|
namespace MoShiro.Backend.Api;
|
||||||
|
|
||||||
/// <summary>Agent 管理:列表、修改、审批、启停、接管、删除;加入令牌;Probe 身份。</summary>
|
/// <summary>Agent 管理:列表、修改、审批、启停、接管、删除;加入令牌;Probe 身份。</summary>
|
||||||
public static class AgentEndpoints
|
public static class AgentEndpoints
|
||||||
@@ -77,7 +77,7 @@ public static class AgentEndpoints
|
|||||||
AgentView? view = await agents.GetAsync(new NodeId(id), ct);
|
AgentView? view = await agents.GetAsync(new NodeId(id), ct);
|
||||||
if (view is null)
|
if (view is null)
|
||||||
{
|
{
|
||||||
return Problems.Create(http, MonitorErrors.AgentNotFound, "agent not found");
|
return Problems.Create(http, MoShiroErrors.AgentNotFound, "agent not found");
|
||||||
}
|
}
|
||||||
|
|
||||||
Concurrency.SetETag(http, view.Agent.RowVersion);
|
Concurrency.SetETag(http, view.Agent.RowVersion);
|
||||||
@@ -166,7 +166,7 @@ public static class AgentEndpoints
|
|||||||
private static async Task<IResult> GetProbeAsync(HttpContext http, ProbeService probes, string id, CancellationToken ct) =>
|
private static async Task<IResult> GetProbeAsync(HttpContext http, ProbeService probes, string id, CancellationToken ct) =>
|
||||||
await probes.GetAsync(id, ct) is { } view
|
await probes.GetAsync(id, ct) is { } view
|
||||||
? TypedResults.Json(Mappers.ToDto(view), ApiJsonContext.Default.ProbeDto)
|
? TypedResults.Json(Mappers.ToDto(view), ApiJsonContext.Default.ProbeDto)
|
||||||
: Problems.Create(http, MonitorErrors.ProbeNotFound, "probe not found");
|
: Problems.Create(http, MoShiroErrors.ProbeNotFound, "probe not found");
|
||||||
|
|
||||||
private static async Task<IResult> PatchProbeAsync(HttpContext http, ProbeService probes, PatchProbeRequest request, string id, CancellationToken ct)
|
private static async Task<IResult> PatchProbeAsync(HttpContext http, ProbeService probes, PatchProbeRequest request, string id, CancellationToken ct)
|
||||||
{
|
{
|
||||||
@@ -211,7 +211,7 @@ public static class AgentEndpoints
|
|||||||
return Problems.From(http, error);
|
return Problems.From(http, error);
|
||||||
}
|
}
|
||||||
|
|
||||||
return dropped ? TypedResults.NoContent() : Problems.Create(http, MonitorErrors.ProbeOffline, "this probe has no session with this agent");
|
return dropped ? TypedResults.NoContent() : Problems.Create(http, MoShiroErrors.ProbeOffline, "this probe has no session with this agent");
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>Probe 要连的面板地址:配置的公开地址,没有就用这次请求的地址。</summary>
|
/// <summary>Probe 要连的面板地址:配置的公开地址,没有就用这次请求的地址。</summary>
|
||||||
+4
-4
@@ -2,10 +2,10 @@ using Fleet.Core;
|
|||||||
using Microsoft.AspNetCore.Builder;
|
using Microsoft.AspNetCore.Builder;
|
||||||
using Microsoft.AspNetCore.Http;
|
using Microsoft.AspNetCore.Http;
|
||||||
using Microsoft.AspNetCore.Routing;
|
using Microsoft.AspNetCore.Routing;
|
||||||
using Monitor.Backend.Application;
|
using MoShiro.Backend.Application;
|
||||||
using Monitor.Protocol;
|
using MoShiro.Protocol;
|
||||||
|
|
||||||
namespace Monitor.Backend.Api;
|
namespace MoShiro.Backend.Api;
|
||||||
|
|
||||||
/// <summary>登录、退出、当前身份。登录接口限流,失败一律返回 401,不区分“用户不存在”和“密码错误”。</summary>
|
/// <summary>登录、退出、当前身份。登录接口限流,失败一律返回 401,不区分“用户不存在”和“密码错误”。</summary>
|
||||||
public static class AuthEndpoints
|
public static class AuthEndpoints
|
||||||
@@ -35,7 +35,7 @@ public static class AuthEndpoints
|
|||||||
string source = http.Connection.RemoteIpAddress?.ToString() ?? "unknown";
|
string source = http.Connection.RemoteIpAddress?.ToString() ?? "unknown";
|
||||||
if (!limiter.TryAcquire(source, out int retryAfter))
|
if (!limiter.TryAcquire(source, out int retryAfter))
|
||||||
{
|
{
|
||||||
return Problems.RetryAfter(http, new FleetError(MonitorErrors.TooManyRequests, "too many sign-in attempts"), retryAfter);
|
return Problems.RetryAfter(http, new FleetError(MoShiroErrors.TooManyRequests, "too many sign-in attempts"), retryAfter);
|
||||||
}
|
}
|
||||||
|
|
||||||
Result<LoginResult> result = await auth.LoginAsync(request.Username ?? "", request.Password ?? "", http.RequestAborted);
|
Result<LoginResult> result = await auth.LoginAsync(request.Username ?? "", request.Password ?? "", http.RequestAborted);
|
||||||
+4
-4
@@ -2,10 +2,10 @@ using Fleet.Core;
|
|||||||
using Microsoft.AspNetCore.Builder;
|
using Microsoft.AspNetCore.Builder;
|
||||||
using Microsoft.AspNetCore.Http;
|
using Microsoft.AspNetCore.Http;
|
||||||
using Microsoft.AspNetCore.Routing;
|
using Microsoft.AspNetCore.Routing;
|
||||||
using Monitor.Backend.Application;
|
using MoShiro.Backend.Application;
|
||||||
using Monitor.Protocol;
|
using MoShiro.Protocol;
|
||||||
|
|
||||||
namespace Monitor.Backend.Api;
|
namespace MoShiro.Backend.Api;
|
||||||
|
|
||||||
/// <summary>构建信息与更新:后端和每个节点的版本、tag、构建时间,CI 签名的发布清单,以及「更新这个节点」的请求。更新本身由节点宿主机上的更新器验签并执行。</summary>
|
/// <summary>构建信息与更新:后端和每个节点的版本、tag、构建时间,CI 签名的发布清单,以及「更新这个节点」的请求。更新本身由节点宿主机上的更新器验签并执行。</summary>
|
||||||
public static class BuildEndpoints
|
public static class BuildEndpoints
|
||||||
@@ -54,7 +54,7 @@ public static class BuildEndpoints
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static async Task<IResult> RequestUpdateAsync(HttpContext http, ReleaseService releases, string id, CancellationToken ct) =>
|
private static async Task<IResult> RequestUpdateAsync(HttpContext http, ReleaseService releases, string id, CancellationToken ct) =>
|
||||||
await releases.RequestUpdateAsync(new NodeId(id), ct) ? TypedResults.NoContent() : Problems.Create(http, MonitorErrors.AgentOffline, "the node is not connected");
|
await releases.RequestUpdateAsync(new NodeId(id), ct) ? TypedResults.NoContent() : Problems.Create(http, MoShiroErrors.AgentOffline, "the node is not connected");
|
||||||
|
|
||||||
private static ReleaseDto ToDto(ReleaseOffer offer) => new(offer.Component, offer.Seq, offer.Tag, offer.Version, offer.Digest);
|
private static ReleaseDto ToDto(ReleaseOffer offer) => new(offer.Component, offer.Seq, offer.Tag, offer.Version, offer.Digest);
|
||||||
|
|
||||||
+7
-7
@@ -3,10 +3,10 @@ using Fleet.Core;
|
|||||||
using Microsoft.AspNetCore.Builder;
|
using Microsoft.AspNetCore.Builder;
|
||||||
using Microsoft.AspNetCore.Http;
|
using Microsoft.AspNetCore.Http;
|
||||||
using Microsoft.AspNetCore.Routing;
|
using Microsoft.AspNetCore.Routing;
|
||||||
using Monitor.Backend.Application;
|
using MoShiro.Backend.Application;
|
||||||
using Monitor.Protocol;
|
using MoShiro.Protocol;
|
||||||
|
|
||||||
namespace Monitor.Backend.Api;
|
namespace MoShiro.Backend.Api;
|
||||||
|
|
||||||
/// <summary>原始数据检索与下载、事件、指标重算进度。</summary>
|
/// <summary>原始数据检索与下载、事件、指标重算进度。</summary>
|
||||||
public static class DataEndpoints
|
public static class DataEndpoints
|
||||||
@@ -18,8 +18,8 @@ public static class DataEndpoints
|
|||||||
group.MapGet("/raw/chunks", ListChunksAsync)
|
group.MapGet("/raw/chunks", ListChunksAsync)
|
||||||
.Api("raw_chunks_list", "Raw chunk metadata", Tag).Returns<PageDto<RawChunkMetaDto>>().ProducesProblems(400, 401, 403);
|
.Api("raw_chunks_list", "Raw chunk metadata", Tag).Returns<PageDto<RawChunkMetaDto>>().ProducesProblems(400, 401, 403);
|
||||||
group.MapGet("/raw/chunks/{chunk_id}", DownloadChunkAsync)
|
group.MapGet("/raw/chunks/{chunk_id}", DownloadChunkAsync)
|
||||||
.Api("raw_chunks_get", "Download a raw chunk (application/vnd.monitor.rawchunk)", Tag)
|
.Api("raw_chunks_get", "Download a raw chunk (application/vnd.moshiro.rawchunk)", Tag)
|
||||||
.Produces(StatusCodes.Status200OK, contentType: "application/vnd.monitor.rawchunk").ProducesProblems(401, 403, 404, 410);
|
.Produces(StatusCodes.Status200OK, contentType: "application/vnd.moshiro.rawchunk").ProducesProblems(401, 403, 404, 410);
|
||||||
group.MapGet("/events", ListEventsAsync)
|
group.MapGet("/events", ListEventsAsync)
|
||||||
.Api("events_list", "Agent events", Tag).Returns<PageDto<EventDto>>().ProducesProblems(400, 401, 403);
|
.Api("events_list", "Agent events", Tag).Returns<PageDto<EventDto>>().ProducesProblems(400, 401, 403);
|
||||||
group.MapGet("/analysis/recompute", RecomputeAsync)
|
group.MapGet("/analysis/recompute", RecomputeAsync)
|
||||||
@@ -57,7 +57,7 @@ public static class DataEndpoints
|
|||||||
{
|
{
|
||||||
if (!Guid.TryParse(chunk_id, out Guid id))
|
if (!Guid.TryParse(chunk_id, out Guid id))
|
||||||
{
|
{
|
||||||
return Problems.Create(http, MonitorErrors.ChunkNotFound, "chunk not found");
|
return Problems.Create(http, MoShiroErrors.ChunkNotFound, "chunk not found");
|
||||||
}
|
}
|
||||||
|
|
||||||
Result<(RawChunkMeta Meta, byte[] Bytes)> result = await raw.GetAsync(new ChunkId(id), http.RequestAborted);
|
Result<(RawChunkMeta Meta, byte[] Bytes)> result = await raw.GetAsync(new ChunkId(id), http.RequestAborted);
|
||||||
@@ -67,7 +67,7 @@ public static class DataEndpoints
|
|||||||
}
|
}
|
||||||
|
|
||||||
http.Response.Headers.CacheControl = "private, max-age=3600";
|
http.Response.Headers.CacheControl = "private, max-age=3600";
|
||||||
return TypedResults.File(found.Bytes, "application/vnd.monitor.rawchunk", chunk_id + ".mnrc");
|
return TypedResults.File(found.Bytes, "application/vnd.moshiro.rawchunk", chunk_id + ".msrc");
|
||||||
}
|
}
|
||||||
|
|
||||||
private static async Task<IResult> ListEventsAsync(
|
private static async Task<IResult> ListEventsAsync(
|
||||||
Loaded 100 of 309 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user