use serde::{Deserialize, Serialize};
use std::collections::HashMap;
#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct HardwareReport {
pub summary: SystemSummary,
pub hostname: String,
pub fqdn: String,
pub os_ip: Vec<InterfaceIPs>,
pub bmc_ip: Option<String>,
pub bmc_mac: Option<String>,
pub hardware: HardwareInfo,
pub network: NetworkInfo,
}
#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct SystemSummary {
pub system_info: SystemInfo,
pub total_memory: String,
pub memory_config: String,
pub total_storage: String,
pub total_storage_tb: f64,
pub filesystems: Vec<String>,
pub bios: BiosInfo,
pub chassis: ChassisInfo,
pub motherboard: MotherboardInfo,
pub total_gpus: usize,
pub total_nics: usize,
pub numa_topology: HashMap<String, NumaNode>,
pub cpu_topology: CpuTopology,
pub cpu_summary: String,
}
#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct SystemInfo {
pub uuid: String,
pub serial: String,
pub product_name: String,
pub product_manufacturer: String,
}
#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct BiosInfo {
pub vendor: String,
pub version: String,
pub release_date: String,
pub firmware_version: String,
}
#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct ChassisInfo {
pub manufacturer: String,
pub type_: String,
pub serial: String,
}
#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct MotherboardInfo {
pub manufacturer: String,
pub product_name: String,
pub version: String,
pub serial: String,
pub features: String,
pub location: String,
pub type_: String,
}
#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct CpuTopology {
pub total_cores: u32,
pub total_threads: u32,
pub sockets: u32,
pub cores_per_socket: u32,
pub threads_per_core: u32,
pub numa_nodes: u32,
pub cpu_model: String,
}
#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct HardwareInfo {
pub cpu: CpuInfo,
pub memory: MemoryInfo,
pub storage: StorageInfo,
pub gpus: GpuInfo,
}
#[derive(Debug, Serialize, Deserialize, Clone, Default)]
pub struct CpuInfo {
pub model: String,
pub cores: u32,
pub threads: u32,
pub sockets: u32,
pub speed: String,
#[serde(default)]
pub vendor: String,
#[serde(default)]
pub architecture: String,
#[serde(default)]
pub frequency_mhz: u32,
#[serde(default)]
pub frequency_min_mhz: Option<u32>,
#[serde(default)]
pub frequency_max_mhz: Option<u32>,
#[serde(default)]
pub cache_l1d_kb: Option<u32>,
#[serde(default)]
pub cache_l1i_kb: Option<u32>,
#[serde(default)]
pub cache_l2_kb: Option<u32>,
#[serde(default)]
pub cache_l3_kb: Option<u32>,
#[serde(default)]
pub flags: Vec<String>,
#[serde(default)]
pub microarchitecture: Option<String>,
#[serde(default)]
pub caches: Vec<CpuCacheInfo>,
#[serde(default)]
pub detection_methods: Vec<String>,
}
impl CpuInfo {
pub fn set_speed_string(&mut self) {
if self.frequency_mhz > 0 {
if self.frequency_mhz >= 1000 {
self.speed = format!("{:.2} GHz", self.frequency_mhz as f64 / 1000.0);
} else {
self.speed = format!("{} MHz", self.frequency_mhz);
}
}
}
pub fn calculate_totals(&mut self) {
}
}
#[derive(Debug, Serialize, Deserialize, Clone, Default)]
pub struct CpuCacheInfo {
pub level: u8,
pub cache_type: String,
pub size_kb: u32,
pub ways_of_associativity: Option<u32>,
pub line_size_bytes: Option<u32>,
pub sets: Option<u32>,
pub shared: Option<bool>,
}
#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct MemoryInfo {
pub total: String,
pub type_: String,
pub speed: String,
pub modules: Vec<MemoryModule>,
}
#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct MemoryModule {
pub size: String,
pub type_: String,
pub speed: String,
pub location: String,
pub manufacturer: String,
pub serial: String,
}
#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct StorageInfo {
pub devices: Vec<StorageDevice>,
}
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq, Default)]
pub enum StorageType {
Nvme,
Ssd,
Hdd,
Emmc,
Virtual,
#[default]
Unknown,
}
impl StorageType {
pub fn from_device(name: &str, is_rotational: bool) -> Self {
if name.starts_with("nvme") {
StorageType::Nvme
} else if name.starts_with("mmcblk") {
StorageType::Emmc
} else if name.starts_with("loop") || name.starts_with("ram") || name.starts_with("dm-") {
StorageType::Virtual
} else if is_rotational {
StorageType::Hdd
} else {
StorageType::Ssd
}
}
pub fn display_name(&self) -> &'static str {
match self {
StorageType::Nvme => "NVMe SSD",
StorageType::Ssd => "SSD",
StorageType::Hdd => "HDD",
StorageType::Emmc => "eMMC",
StorageType::Virtual => "Virtual",
StorageType::Unknown => "Unknown",
}
}
}
#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct StorageDevice {
pub name: String,
#[serde(default)]
pub device_path: String,
#[serde(default)]
pub device_type: StorageType,
pub type_: String,
pub size: String,
#[serde(default)]
pub size_bytes: u64,
#[serde(default)]
pub size_gb: f64,
pub model: String,
#[serde(default)]
pub serial_number: Option<String>,
#[serde(default)]
pub firmware_version: Option<String>,
#[serde(default)]
pub wwn: Option<String>,
#[serde(default)]
pub interface: String,
#[serde(default)]
pub is_rotational: bool,
#[serde(default)]
pub detection_method: String,
}
impl Default for StorageDevice {
fn default() -> Self {
Self {
name: String::new(),
device_path: String::new(),
device_type: StorageType::Unknown,
type_: String::new(),
size: String::new(),
size_bytes: 0,
size_gb: 0.0,
model: String::new(),
serial_number: None,
firmware_version: None,
wwn: None,
interface: String::new(),
is_rotational: false,
detection_method: String::new(),
}
}
}
impl StorageDevice {
pub fn calculate_size_fields(&mut self) {
if self.size_bytes > 0 {
self.size_gb = self.size_bytes as f64 / (1024.0 * 1024.0 * 1024.0);
if self.size_gb >= 1000.0 {
self.size = format!("{:.2} TB", self.size_gb / 1024.0);
} else {
self.size = format!("{:.2} GB", self.size_gb);
}
}
}
pub fn set_device_path(&mut self) {
if self.device_path.is_empty() && !self.name.is_empty() {
self.device_path = format!("/dev/{}", self.name);
}
}
}
#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct GpuInfo {
pub devices: Vec<GpuDevice>,
}
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq, Default)]
pub enum GpuVendor {
Nvidia,
Amd,
Intel,
Apple,
#[default]
Unknown,
}
impl GpuVendor {
pub fn from_pci_vendor(vendor_id: &str) -> Self {
match vendor_id.to_lowercase().as_str() {
"10de" => GpuVendor::Nvidia,
"1002" => GpuVendor::Amd,
"8086" => GpuVendor::Intel,
_ => GpuVendor::Unknown,
}
}
pub fn name(&self) -> &'static str {
match self {
GpuVendor::Nvidia => "NVIDIA",
GpuVendor::Amd => "AMD",
GpuVendor::Intel => "Intel",
GpuVendor::Apple => "Apple",
GpuVendor::Unknown => "Unknown",
}
}
}
#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct GpuDevice {
pub index: u32,
pub name: String,
pub uuid: String,
pub memory: String,
#[serde(default)]
pub memory_total_mb: u64,
#[serde(default)]
pub memory_free_mb: Option<u64>,
pub pci_id: String,
#[serde(default)]
pub pci_bus_id: Option<String>,
pub vendor: String,
#[serde(default)]
pub vendor_enum: GpuVendor,
pub numa_node: Option<i32>,
#[serde(default)]
pub driver_version: Option<String>,
#[serde(default)]
pub compute_capability: Option<String>,
#[serde(default)]
pub detection_method: String,
}
impl Default for GpuDevice {
fn default() -> Self {
Self {
index: 0,
name: String::new(),
uuid: String::new(),
memory: String::new(),
memory_total_mb: 0,
memory_free_mb: None,
pci_id: String::new(),
pci_bus_id: None,
vendor: String::new(),
vendor_enum: GpuVendor::Unknown,
numa_node: None,
driver_version: None,
compute_capability: None,
detection_method: String::new(),
}
}
}
impl GpuDevice {
pub fn set_memory_string(&mut self) {
if self.memory_total_mb > 0 {
if self.memory_total_mb >= 1024 {
self.memory = format!("{:.1} GB", self.memory_total_mb as f64 / 1024.0);
} else {
self.memory = format!("{} MB", self.memory_total_mb);
}
}
}
}
#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct NetworkInfo {
pub interfaces: Vec<NetworkInterface>,
pub infiniband: Option<InfinibandInfo>,
}
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq, Default)]
pub enum NetworkInterfaceType {
Ethernet,
Wireless,
Loopback,
Bridge,
Vlan,
Bond,
Veth,
TunTap,
Infiniband,
Macvlan,
#[default]
Unknown,
}
#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct NetworkInterface {
pub name: String,
pub mac: String,
pub ip: String,
pub prefix: String,
pub speed: Option<String>,
#[serde(default)]
pub speed_mbps: Option<u32>,
pub type_: String,
#[serde(default)]
pub interface_type: NetworkInterfaceType,
pub vendor: String,
pub model: String,
pub pci_id: String,
pub numa_node: Option<i32>,
#[serde(default)]
pub driver: Option<String>,
#[serde(default)]
pub driver_version: Option<String>,
#[serde(default = "default_mtu")]
pub mtu: u32,
#[serde(default)]
pub is_up: bool,
#[serde(default)]
pub is_virtual: bool,
#[serde(default)]
pub carrier: Option<bool>,
}
fn default_mtu() -> u32 {
1500
}
impl Default for NetworkInterface {
fn default() -> Self {
Self {
name: String::new(),
mac: String::new(),
ip: String::new(),
prefix: String::new(),
speed: None,
speed_mbps: None,
type_: String::new(),
interface_type: NetworkInterfaceType::Unknown,
vendor: String::new(),
model: String::new(),
pci_id: String::new(),
numa_node: None,
driver: None,
driver_version: None,
mtu: 1500,
is_up: false,
is_virtual: false,
carrier: None,
}
}
}
#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct InfinibandInfo {
pub interfaces: Vec<IbInterface>,
}
#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct IbInterface {
pub name: String,
pub port: u32,
pub state: String,
pub rate: String,
}
#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct NumaNode {
pub id: i32,
pub cpus: Vec<u32>,
pub memory: String,
pub devices: Vec<NumaDevice>,
pub distances: HashMap<String, u32>,
}
#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct NumaDevice {
pub type_: String,
pub pci_id: String,
pub name: String,
}
#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct InterfaceIPs {
pub interface: String,
pub ip_addresses: Vec<String>,
}
#[derive(Debug, Clone)]
pub struct ReportConfig {
pub include_sensitive: bool,
pub skip_sudo: bool,
pub command_timeout: u64,
pub verbose: bool,
}
impl Default for ReportConfig {
fn default() -> Self {
Self {
include_sensitive: false,
skip_sudo: false,
command_timeout: 30,
verbose: false,
}
}
}
#[derive(Debug, Clone)]
pub struct PublishConfig {
pub endpoint: String,
pub auth_token: Option<String>,
pub skip_tls_verify: bool,
pub labels: HashMap<String, String>,
pub system_identifier: Option<String>,
}