mirror of
https://github.com/rustfs/rustfs.git
synced 2026-07-27 08:38:58 +00:00
214 lines
8.2 KiB
Rust
214 lines
8.2 KiB
Rust
// Copyright 2024 RustFS Team
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
use crate::storage::{ecstore_cluster, ecstore_layout::EndpointServerPools};
|
|
use rustfs_storage_api::{
|
|
CapabilitySnapshotError, CapabilityStatus, DiskCapabilities, MemorySamplingState, ObservabilitySnapshot,
|
|
ObservabilitySnapshotProvider, PlatformSupport, TopologyCapabilities, TopologySnapshot, TopologySnapshotProvider,
|
|
UserspaceProfilingCapability,
|
|
};
|
|
|
|
const NOT_WIRED_INTO_RUNTIME: &str = "not wired into runtime";
|
|
const STORAGE_MEDIA_NOT_REPORTED: &str = "storage media not reported by endpoints";
|
|
const FAILURE_DOMAIN_NOT_REPORTED: &str = "failure domain labels not reported by endpoints";
|
|
const NUMA_NOT_WIRED: &str = "NUMA topology not wired into runtime";
|
|
|
|
#[derive(Debug, Default, Clone, Copy)]
|
|
pub struct RustFsObservabilitySnapshotProvider;
|
|
|
|
#[async_trait::async_trait]
|
|
impl ObservabilitySnapshotProvider for RustFsObservabilitySnapshotProvider {
|
|
async fn observability_snapshot(&self) -> Result<ObservabilitySnapshot, CapabilitySnapshotError> {
|
|
Ok(runtime_observability_snapshot())
|
|
}
|
|
}
|
|
|
|
pub fn runtime_observability_snapshot() -> ObservabilitySnapshot {
|
|
ObservabilitySnapshot {
|
|
runtime_telemetry: runtime_telemetry_status(),
|
|
userspace_profiling: UserspaceProfilingCapability {
|
|
cpu: cpu_profiling_status(),
|
|
memory: memory_profiling_status(),
|
|
continuous_cpu: cpu_profiling_status(),
|
|
periodic_cpu: cpu_profiling_status(),
|
|
},
|
|
memory_sampling: MemorySamplingState {
|
|
process: CapabilityStatus::supported(),
|
|
system: CapabilityStatus::supported(),
|
|
cgroup: cgroup_memory_status(),
|
|
},
|
|
platform: PlatformSupport {
|
|
target_triple: option_env!("TARGET").map(str::to_owned),
|
|
os: Some(std::env::consts::OS.to_owned()),
|
|
arch: Some(std::env::consts::ARCH.to_owned()),
|
|
allocator: CapabilityStatus::supported()
|
|
.with_reason(format!("backend={}", crate::allocator_reclaim::allocator_backend())),
|
|
ebpf: CapabilityStatus::unknown().with_reason(NOT_WIRED_INTO_RUNTIME),
|
|
numa: CapabilityStatus::unsupported().with_reason(NUMA_NOT_WIRED),
|
|
},
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, Clone)]
|
|
pub struct EndpointTopologySnapshotProvider {
|
|
endpoint_pools: EndpointServerPools,
|
|
}
|
|
|
|
impl EndpointTopologySnapshotProvider {
|
|
pub fn new(endpoint_pools: EndpointServerPools) -> Self {
|
|
Self { endpoint_pools }
|
|
}
|
|
}
|
|
|
|
#[async_trait::async_trait]
|
|
impl TopologySnapshotProvider for EndpointTopologySnapshotProvider {
|
|
async fn topology_snapshot(&self) -> Result<TopologySnapshot, CapabilitySnapshotError> {
|
|
Ok(topology_snapshot_from_endpoint_pools(&self.endpoint_pools))
|
|
}
|
|
}
|
|
|
|
pub fn topology_snapshot_from_endpoint_pools(endpoint_pools: &EndpointServerPools) -> TopologySnapshot {
|
|
ecstore_cluster::topology_snapshot_from_endpoint_pools_with_capabilities(
|
|
endpoint_pools,
|
|
TopologyCapabilities {
|
|
profiling: cpu_profiling_status(),
|
|
numa: CapabilityStatus::unsupported().with_reason(NUMA_NOT_WIRED),
|
|
failure_domain_labels: CapabilityStatus::unknown().with_reason(FAILURE_DOMAIN_NOT_REPORTED),
|
|
media_labels: CapabilityStatus::unknown().with_reason(STORAGE_MEDIA_NOT_REPORTED),
|
|
},
|
|
DiskCapabilities {
|
|
media_type: CapabilityStatus::unknown().with_reason(STORAGE_MEDIA_NOT_REPORTED),
|
|
failure_domain: CapabilityStatus::unknown().with_reason(FAILURE_DOMAIN_NOT_REPORTED),
|
|
numa: CapabilityStatus::unsupported().with_reason(NUMA_NOT_WIRED),
|
|
profiling: cpu_profiling_status(),
|
|
},
|
|
)
|
|
}
|
|
|
|
fn runtime_telemetry_status() -> CapabilityStatus {
|
|
if rustfs_obs::dial9::is_enabled() {
|
|
CapabilityStatus::supported()
|
|
} else {
|
|
CapabilityStatus::disabled()
|
|
}
|
|
}
|
|
|
|
fn cpu_profiling_status() -> CapabilityStatus {
|
|
if cfg!(any(target_os = "linux", target_os = "macos")) {
|
|
CapabilityStatus::supported()
|
|
} else {
|
|
CapabilityStatus::unsupported().with_reason("userspace CPU profiling supports linux and macos targets")
|
|
}
|
|
}
|
|
|
|
fn memory_profiling_status() -> CapabilityStatus {
|
|
if cfg!(all(target_os = "linux", target_env = "gnu", target_arch = "x86_64")) {
|
|
CapabilityStatus::supported().with_reason("jemalloc memory profiling target")
|
|
} else {
|
|
CapabilityStatus::unsupported().with_reason("memory profiling supports linux gnu x86_64 targets")
|
|
}
|
|
}
|
|
|
|
fn cgroup_memory_status() -> CapabilityStatus {
|
|
if cfg!(target_os = "linux") {
|
|
CapabilityStatus::supported()
|
|
} else {
|
|
CapabilityStatus::unsupported().with_reason("cgroup memory sampling supports linux targets")
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
use crate::storage::{
|
|
ecstore_disk::endpoint::Endpoint,
|
|
ecstore_layout::{Endpoints, PoolEndpoints},
|
|
};
|
|
use rustfs_storage_api::{CapabilityState, ObservabilitySnapshotProvider, TopologySnapshotProvider};
|
|
|
|
#[tokio::test]
|
|
async fn observability_provider_returns_platform_snapshot() {
|
|
let provider = RustFsObservabilitySnapshotProvider;
|
|
let snapshot = provider.observability_snapshot().await.expect("observability snapshot");
|
|
|
|
assert_eq!(snapshot.platform.os.as_deref(), Some(std::env::consts::OS));
|
|
assert_eq!(snapshot.platform.arch.as_deref(), Some(std::env::consts::ARCH));
|
|
assert!(matches!(
|
|
snapshot.runtime_telemetry.state,
|
|
CapabilityState::Supported | CapabilityState::Disabled
|
|
));
|
|
assert!(
|
|
snapshot
|
|
.platform
|
|
.allocator
|
|
.reason
|
|
.as_deref()
|
|
.unwrap_or_default()
|
|
.contains("backend=")
|
|
);
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn topology_provider_maps_endpoint_pools_to_sets_and_disks() {
|
|
let endpoint_pools = sample_endpoint_pools();
|
|
let provider = EndpointTopologySnapshotProvider::new(endpoint_pools);
|
|
let snapshot = provider.topology_snapshot().await.expect("topology snapshot");
|
|
|
|
assert_eq!(snapshot.pools.len(), 1);
|
|
assert_eq!(snapshot.pools[0].sets.len(), 2);
|
|
assert_eq!(snapshot.pools[0].sets[0].disks.len(), 2);
|
|
assert_eq!(snapshot.pools[0].sets[1].disks.len(), 2);
|
|
assert_eq!(snapshot.pools[0].sets[0].disks[1].disk_index, 1);
|
|
assert_eq!(
|
|
snapshot.pools[0].sets[0].disks[0]
|
|
.labels
|
|
.additional
|
|
.get("endpoint_type")
|
|
.map(String::as_str),
|
|
Some("path")
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn topology_snapshot_does_not_expose_local_paths() {
|
|
let endpoint_pools = sample_endpoint_pools();
|
|
let snapshot = topology_snapshot_from_endpoint_pools(&endpoint_pools);
|
|
let encoded = serde_json::to_string(&snapshot).expect("serialize topology snapshot");
|
|
|
|
assert!(!encoded.contains("/tmp/rustfs-runtime-capability"));
|
|
}
|
|
|
|
fn sample_endpoint_pools() -> EndpointServerPools {
|
|
let endpoints = (0..4)
|
|
.map(|index| {
|
|
let mut endpoint =
|
|
Endpoint::try_from(format!("/tmp/rustfs-runtime-capability-{index}").as_str()).expect("local endpoint");
|
|
endpoint.set_pool_index(0);
|
|
endpoint.set_set_index(index / 2);
|
|
endpoint.set_disk_index(index % 2);
|
|
endpoint
|
|
})
|
|
.collect::<Vec<_>>();
|
|
|
|
EndpointServerPools::from(vec![PoolEndpoints {
|
|
legacy: false,
|
|
set_count: 2,
|
|
drives_per_set: 2,
|
|
endpoints: Endpoints::from(endpoints),
|
|
cmd_line: "/tmp/rustfs-runtime-capability-{0...3}".to_owned(),
|
|
platform: "OS: test | Arch: test".to_owned(),
|
|
}])
|
|
}
|
|
}
|