mirror of
https://github.com/Quad4-Software/Reticulum-Go
synced 2026-08-29 23:48:44 -04:00
141 lines
4.1 KiB
Go
141 lines
4.1 KiB
Go
// SPDX-License-Identifier: Apache-2.0
|
|
// Copyright (c) 2024-2026 Quad4.io
|
|
|
|
package cli
|
|
|
|
import (
|
|
"encoding/hex"
|
|
"encoding/json"
|
|
"flag"
|
|
"fmt"
|
|
"time"
|
|
|
|
"quad4/reticulum-go/pkg/health"
|
|
"quad4/reticulum-go/pkg/rnsutil"
|
|
"quad4/reticulum-go/pkg/transport"
|
|
)
|
|
|
|
// NodeSnapshot is a path and link health dump for operators and porters.
|
|
type NodeSnapshot struct {
|
|
TS string `json:"ts"`
|
|
TransportUptime float64 `json:"transport_uptime"`
|
|
TransportID string `json:"transport_id,omitempty"`
|
|
ActiveLinks int `json:"active_links"`
|
|
NetmonFlap uint64 `json:"netmon_flap"`
|
|
Health health.Snapshot `json:"health"`
|
|
Interfaces []transport.InterfaceStat `json:"interfaces"`
|
|
Paths []snapshotPath `json:"paths"`
|
|
PathCount int `json:"path_count"`
|
|
PacketHashCount int `json:"packet_hash_count,omitempty"`
|
|
AnnounceCacheCount int `json:"announce_cache_count,omitempty"`
|
|
SeenAnnounceCount int `json:"seen_announce_count,omitempty"`
|
|
}
|
|
|
|
type snapshotPath struct {
|
|
Hash string `json:"hash"`
|
|
Hops uint8 `json:"hops"`
|
|
Via string `json:"via,omitempty"`
|
|
Interface string `json:"interface,omitempty"`
|
|
Timestamp float64 `json:"timestamp"`
|
|
Expires float64 `json:"expires"`
|
|
}
|
|
|
|
func RunSnapshot(args []string, opt ...Options) int {
|
|
stdout, stderr := cliIO(opt)
|
|
fs := flag.NewFlagSet("rgosnap", flag.ContinueOnError)
|
|
fs.SetOutput(stderr)
|
|
configDir := fs.String("config", "", "path to config directory")
|
|
maxHops := fs.Int("max-hops", 0, "omit paths with more hops than this (0 means no filter)")
|
|
timeout := fs.Duration("timeout", 10*time.Second, "RPC timeout")
|
|
quiet := fs.Bool("q", false, "quiet: suppress stderr hints")
|
|
bindFlagUsage(fs, "rgosnap - path table and health snapshot",
|
|
"Emits path table, link count, and health JSON via shared-instance RPC.",
|
|
[]helpLine{
|
|
{Cmd: "rgosnap [flags]"},
|
|
{Cmd: "reticulum-go snapshot [flags]"},
|
|
},
|
|
"rgosnap",
|
|
"rgosnap -max-hops 8",
|
|
)
|
|
if err := fs.Parse(args); err != nil {
|
|
return 2
|
|
}
|
|
|
|
cfg, err := rnsutil.LoadConfigDir(*configDir)
|
|
if err != nil {
|
|
diagErr(stderr, "config", err)
|
|
return 1
|
|
}
|
|
client, err := rnsutil.DialRPC(cfg, nil)
|
|
if err != nil {
|
|
fmt.Fprintf(stderr, "%s: %v\n", errMsg(stderr, "rpc"), err)
|
|
if !*quiet {
|
|
fmt.Fprintln(stderr, warnMsg(stderr, "hint: point -config at the daemon config dir"))
|
|
}
|
|
return 1
|
|
}
|
|
client.SetTimeout(*timeout)
|
|
|
|
stats, err := client.GetInterfaceStats()
|
|
if err != nil {
|
|
fmt.Fprintf(stderr, "%s: %v\n", errMsg(stderr, "interface stats"), err)
|
|
return 1
|
|
}
|
|
|
|
var mh *int
|
|
if *maxHops > 0 {
|
|
mh = maxHops
|
|
}
|
|
paths, err := client.GetPathTable(mh)
|
|
if err != nil {
|
|
diagErr(stderr, "path table", err)
|
|
return 1
|
|
}
|
|
|
|
linkCount, err := client.GetLinkCount()
|
|
if err != nil {
|
|
diagErr(stderr, "link count", err)
|
|
return 1
|
|
}
|
|
|
|
snap := NodeSnapshot{
|
|
TS: time.Now().UTC().Format(time.RFC3339Nano),
|
|
TransportUptime: stats.TransportUptime,
|
|
ActiveLinks: linkCount,
|
|
NetmonFlap: stats.NetmonFlap,
|
|
Health: stats.Health,
|
|
Interfaces: stats.Interfaces,
|
|
Paths: make([]snapshotPath, 0, len(paths)),
|
|
PathCount: len(paths),
|
|
PacketHashCount: stats.PacketHashCount,
|
|
AnnounceCacheCount: stats.AnnounceCacheCount,
|
|
SeenAnnounceCount: stats.SeenAnnounceCount,
|
|
}
|
|
if len(stats.TransportID) > 0 {
|
|
snap.TransportID = hex.EncodeToString(stats.TransportID)
|
|
}
|
|
if snap.ActiveLinks == 0 {
|
|
snap.ActiveLinks = stats.ActiveLinks
|
|
}
|
|
for _, p := range paths {
|
|
sp := snapshotPath{
|
|
Hash: hex.EncodeToString(p.Hash),
|
|
Hops: p.Hops,
|
|
Interface: p.Interface,
|
|
Timestamp: p.Timestamp,
|
|
Expires: p.Expires,
|
|
}
|
|
if len(p.Via) > 0 {
|
|
sp.Via = hex.EncodeToString(p.Via)
|
|
}
|
|
snap.Paths = append(snap.Paths, sp)
|
|
}
|
|
|
|
enc := json.NewEncoder(stdout)
|
|
enc.SetIndent("", " ")
|
|
if err := enc.Encode(snap); err != nil {
|
|
diagErr(stderr, "json", err)
|
|
return 1
|
|
}
|
|
return 0
|
|
}
|