Show all connected Tailscale peers with connection methods
All checks were successful
Build and Release / build-and-release (push) Successful in 1m38s
All checks were successful
Build and Release / build-and-release (push) Successful in 1m38s
Replace single connection method display with individual sub-service rows for each online Tailscale peer. Each peer shows hostname and connection type (direct, relay, or idle) allowing monitoring of all connected devices and their connection quality. Query tailscale status --json to enumerate all online peers and display each as a separate sub-service under tailscaled.
This commit is contained in:
parent
ffecbc3166
commit
5b1e39cfca
6
Cargo.lock
generated
6
Cargo.lock
generated
@ -279,7 +279,7 @@ checksum = "a1d728cc89cf3aee9ff92b05e62b19ee65a02b5702cff7d5a377e32c6ae29d8d"
|
||||
|
||||
[[package]]
|
||||
name = "cm-dashboard"
|
||||
version = "0.1.261"
|
||||
version = "0.1.262"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"chrono",
|
||||
@ -301,7 +301,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "cm-dashboard-agent"
|
||||
version = "0.1.261"
|
||||
version = "0.1.262"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-trait",
|
||||
@ -325,7 +325,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "cm-dashboard-shared"
|
||||
version = "0.1.261"
|
||||
version = "0.1.262"
|
||||
dependencies = [
|
||||
"chrono",
|
||||
"serde",
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "cm-dashboard-agent"
|
||||
version = "0.1.262"
|
||||
version = "0.1.263"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
|
||||
@ -217,14 +217,15 @@ impl SystemdCollector {
|
||||
}
|
||||
|
||||
if service_name == "tailscaled" && status_info.active_state == "active" {
|
||||
// Add Tailscale connection method as sub-service
|
||||
if let Some(conn_method) = self.get_tailscale_connection_method() {
|
||||
// Add Tailscale peers with their connection methods as sub-services
|
||||
let peers = self.get_tailscale_peers();
|
||||
for (peer_name, conn_method) in peers {
|
||||
let metrics = Vec::new();
|
||||
sub_services.push(SubServiceData {
|
||||
name: format!("Connection: {}", conn_method),
|
||||
name: format!("{}: {}", peer_name, conn_method),
|
||||
service_status: Status::Info,
|
||||
metrics,
|
||||
service_type: "tailscale_connection".to_string(),
|
||||
service_type: "tailscale_peer".to_string(),
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -936,50 +937,63 @@ impl SystemdCollector {
|
||||
None
|
||||
}
|
||||
|
||||
/// Get Tailscale connection method (direct, relay, or proxy)
|
||||
fn get_tailscale_connection_method(&self) -> Option<String> {
|
||||
/// Get Tailscale connected peers with their connection methods
|
||||
/// Returns a list of (device_name, connection_method) tuples
|
||||
fn get_tailscale_peers(&self) -> Vec<(String, String)> {
|
||||
match Command::new("timeout")
|
||||
.args(["2", "tailscale", "status", "--json"])
|
||||
.output()
|
||||
{
|
||||
Ok(output) if output.status.success() => {
|
||||
let json_str = String::from_utf8_lossy(&output.stdout);
|
||||
let mut peers = Vec::new();
|
||||
|
||||
if let Ok(json_data) = serde_json::from_str::<serde_json::Value>(&json_str) {
|
||||
// Look for the self peer (current node) in the peer list
|
||||
if let Some(peers) = json_data["Peer"].as_object() {
|
||||
// Find the first active peer connection to determine connection method
|
||||
for (_peer_id, peer_data) in peers {
|
||||
if peer_data["Active"].as_bool().unwrap_or(false) {
|
||||
if let Some(peer_map) = json_data["Peer"].as_object() {
|
||||
// Iterate through all peers
|
||||
for (_peer_id, peer_data) in peer_map {
|
||||
// Only include active/online peers
|
||||
if !peer_data["Online"].as_bool().unwrap_or(false) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Get peer hostname or DNS name
|
||||
let peer_name = peer_data["HostName"]
|
||||
.as_str()
|
||||
.or_else(|| peer_data["DNSName"].as_str())
|
||||
.unwrap_or("unknown")
|
||||
.trim_end_matches('.')
|
||||
.to_string();
|
||||
|
||||
// Determine connection method
|
||||
let connection_method = if peer_data["Active"].as_bool().unwrap_or(false) {
|
||||
// Check if using relay
|
||||
let relay_node = peer_data["Relay"].as_str().unwrap_or("");
|
||||
if !relay_node.is_empty() {
|
||||
return Some("relay".to_string());
|
||||
}
|
||||
|
||||
// Check if using direct connection
|
||||
if let Some(endpoints) = peer_data["CurAddr"].as_str() {
|
||||
if !endpoints.is_empty() {
|
||||
return Some("direct".to_string());
|
||||
"relay"
|
||||
} else if let Some(cur_addr) = peer_data["CurAddr"].as_str() {
|
||||
// Check if using direct connection
|
||||
if !cur_addr.is_empty() {
|
||||
"direct"
|
||||
} else {
|
||||
"unknown"
|
||||
}
|
||||
} else {
|
||||
"unknown"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
"idle"
|
||||
};
|
||||
|
||||
// Check if using proxy from backend state
|
||||
if let Some(backend_state) = json_data["BackendState"].as_str() {
|
||||
if backend_state == "Running" {
|
||||
// If we're running but have no direct or relay, might be proxy
|
||||
// This is a fallback heuristic
|
||||
return Some("unknown".to_string());
|
||||
peers.push((peer_name, connection_method.to_string()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
peers
|
||||
}
|
||||
_ => None,
|
||||
_ => Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "cm-dashboard"
|
||||
version = "0.1.262"
|
||||
version = "0.1.263"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "cm-dashboard-shared"
|
||||
version = "0.1.262"
|
||||
version = "0.1.263"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user