Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .envrc
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
export DIRENV_LOG_FORMAT=""
use flake
22 changes: 21 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,27 @@ echo "Build complete! Run with: sudo ./target/release/hardware_report" && \
sudo ./target/release/hardware_report
```

### Option 3: Pre-built Releases (Recommended for Quick Setup)
### Option 3: Direct Flake Installation (Nix Users)

**Install directly from GitHub without cloning:**
```bash
# Install to user profile
nix profile install github:sfcompute/hardware_report

# Run directly without installing
nix run github:sfcompute/hardware_report

# Use in a nix shell
nix shell github:sfcompute/hardware_report
```

**Benefits:**
- No need to clone the repository
- Always uses the latest committed version
- Automatic dependency management
- Clean integration with existing Nix workflows

### Option 4: Pre-built Releases (Recommended for Quick Setup)

Instead of building from source, you can download pre-built binaries and Debian packages from our [GitHub Releases](https://github.com/sfcompute/hardware_report/releases) page.

Expand Down
24 changes: 20 additions & 4 deletions flake.nix
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
{
description = "Hardware Report - A tool for generating hardware information reports";

nixConfig = {
extra-substituters = [ "https://cache.nixos.org/" ];
extra-trusted-public-keys = [ "cache.nixos.org-1:6NCHdD59X431o0gWypbMrAURkbJ16ZPMQFGspcDShjY=" ];
};

inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
Expand Down Expand Up @@ -37,11 +42,14 @@

# Runtime dependencies that the binary needs
runtimeDeps = with pkgs; [
numactl
# Cross-platform tools
ipmitool
pciutils # for lspci
] ++ lib.optionals stdenv.isLinux [
# Linux-only tools
numactl
ethtool
util-linux # for lscpu
pciutils # for lspci
dmidecode # for system/BIOS/memory information
];

Expand Down Expand Up @@ -255,8 +263,16 @@ EOF
echo "Run 'cargo build' to build the project"
echo "Run 'cargo run' to run the project"
echo ""
echo "Runtime dependencies are available in PATH:"
echo "- numactl, ipmitool, ethtool, lscpu, lspci, dmidecode"
echo "Runtime dependencies available in PATH:"
echo "- ipmitool, pciutils (lspci)"
${if pkgs.stdenv.isLinux then ''
echo "- numactl, ethtool, util-linux (lscpu), dmidecode"
'' else ''
echo "- (Linux-only tools like numactl, ethtool not available on Darwin)"
''}
echo ""
echo "Platform: ${pkgs.stdenv.hostPlatform.system}"
echo "Rust toolchain: ${rustToolchain.version}"
'';
};

Expand Down
57 changes: 56 additions & 1 deletion src/bin/hardware_report.rs
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,30 @@ struct Opt {
/// No summary output to console
#[structopt(long)]
noout: bool,

/// Enable NetBox integration
#[structopt(long)]
netbox: bool,

/// NetBox URL (required with --netbox)
#[structopt(long, env = "NETBOX_URL")]
netbox_url: Option<String>,

/// NetBox API token (required with --netbox)
#[structopt(long, env = "NETBOX_TOKEN")]
netbox_token: Option<String>,

/// NetBox site name (default: "Digital Ocean")
#[structopt(long, default_value = "Digital Ocean")]
netbox_site: String,

/// NetBox device role (default: "production")
#[structopt(long, default_value = "production")]
netbox_role: String,

/// Dry run mode for NetBox (preview changes without applying)
#[structopt(long)]
netbox_dry_run: bool,
}

fn parse_label(s: &str) -> Result<(String, String), String> {
Expand Down Expand Up @@ -279,7 +303,7 @@ async fn main() -> Result<(), Box<dyn Error>> {
if opt.post {
let labels: HashMap<String, String> = opt.labels.into_iter().collect();
post_data(
server_info,
&server_info,
labels,
&opt.endpoint,
opt.auth_token.as_deref(),
Expand All @@ -290,5 +314,36 @@ async fn main() -> Result<(), Box<dyn Error>> {
println!("\nSuccessfully posted data to remote server");
}

// Handle NetBox integration if enabled
if opt.netbox {
if opt.netbox_url.is_none() {
eprintln!("Error: --netbox-url is required when using --netbox");
std::process::exit(1);
}
if opt.netbox_token.is_none() {
eprintln!("Error: --netbox-token is required when using --netbox");
std::process::exit(1);
}

use hardware_report::netbox::sync_to_netbox;

sync_to_netbox(
&server_info,
opt.netbox_url.as_ref().unwrap(),
opt.netbox_token.as_ref().unwrap(),
Some(&opt.netbox_site),
Some(&opt.netbox_role),
opt.skip_tls_verify,
opt.netbox_dry_run,
)
.await?;

if opt.netbox_dry_run {
println!("\nNetBox dry run completed - no changes were made");
} else {
println!("\nSuccessfully synchronized data to NetBox");
}
}

Ok(())
}
49 changes: 25 additions & 24 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,13 +35,16 @@ use std::collections::{HashMap, HashSet};
use std::error::Error;
use std::process::Command;

pub mod posting;
pub mod netbox;

lazy_static! {
static ref STORAGE_SIZE_RE: Regex = Regex::new(r"(\d+(?:\.\d+)?)(B|K|M|G|T)").unwrap();
static ref NETWORK_SPEED_RE: Regex = Regex::new(r"Speed:\s+(\S+)").unwrap();
}

/// CPU topology information
#[derive(Debug, Serialize, Deserialize)]
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CpuTopology {
pub total_cores: u32,
pub total_threads: u32,
Expand All @@ -53,7 +56,7 @@ pub struct CpuTopology {
}

/// Motherboard information
#[derive(Debug, Serialize, Deserialize)]
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct MotherboardInfo {
pub manufacturer: String,
pub product_name: String,
Expand All @@ -64,7 +67,7 @@ pub struct MotherboardInfo {
pub type_: String,
}

#[derive(Debug, Serialize, Deserialize)]
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SystemInfo {
pub uuid: String,
pub serial: String,
Expand All @@ -73,7 +76,7 @@ pub struct SystemInfo {
}

/// Summary of key system components
#[derive(Debug, Serialize, Deserialize)]
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SystemSummary {
/// System information
pub system_info: SystemInfo,
Expand Down Expand Up @@ -106,7 +109,7 @@ pub struct SystemSummary {
}

/// BIOS information
#[derive(Debug, Serialize, Deserialize)]
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct BiosInfo {
pub vendor: String,
pub version: String,
Expand All @@ -115,15 +118,15 @@ pub struct BiosInfo {
}

/// Chassis information
#[derive(Debug, Serialize, Deserialize)]
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ChassisInfo {
pub manufacturer: String,
pub type_: String,
pub serial: String,
}

/// Represents the overall server information
#[derive(Debug, Serialize, Deserialize)]
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ServerInfo {
/// System summary
pub summary: SystemSummary,
Expand All @@ -138,7 +141,7 @@ pub struct ServerInfo {
}

/// Contains detailed hardware information
#[derive(Debug, Serialize, Deserialize)]
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct HardwareInfo {
/// CPU information.
pub cpu: CpuInfo,
Expand All @@ -151,7 +154,7 @@ pub struct HardwareInfo {
}

/// Represents CPU information.
#[derive(Debug, Serialize, Deserialize)]
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CpuInfo {
/// CPU model name.
pub model: String,
Expand All @@ -166,7 +169,7 @@ pub struct CpuInfo {
}

/// Represents memory information.
#[derive(Debug, Serialize, Deserialize)]
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct MemoryInfo {
/// Total memory size.
pub total: String,
Expand All @@ -179,7 +182,7 @@ pub struct MemoryInfo {
}

/// Represents a memory module.
#[derive(Debug, Serialize, Deserialize)]
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct MemoryModule {
/// Size of the memory module.
pub size: String,
Expand All @@ -196,14 +199,14 @@ pub struct MemoryModule {
}

/// Represents storage information.
#[derive(Debug, Serialize, Deserialize)]
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct StorageInfo {
/// List of storage devices.
pub devices: Vec<StorageDevice>,
}

/// Represents a storage device.
#[derive(Debug, Serialize, Deserialize)]
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct StorageDevice {
/// Device name.
pub name: String,
Expand All @@ -216,14 +219,14 @@ pub struct StorageDevice {
}

/// Represents GPU information.
#[derive(Debug, Serialize, Deserialize)]
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct GpuInfo {
/// List of GPU devices.
pub devices: Vec<GpuDevice>,
}

/// Represents a GPU device.
#[derive(Debug, Serialize, Deserialize)]
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct GpuDevice {
/// GPU index
pub index: u32,
Expand All @@ -242,7 +245,7 @@ pub struct GpuDevice {
}

/// Represents a NUMA node
#[derive(Debug, Serialize, Deserialize)]
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct NumaNode {
/// Node ID
pub id: i32,
Expand All @@ -257,7 +260,7 @@ pub struct NumaNode {
}

/// Represents a device attached to a NUMA node
#[derive(Debug, Serialize, Deserialize)]
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct NumaDevice {
/// Device type (GPU, NIC, etc.)
pub type_: String,
Expand All @@ -268,7 +271,7 @@ pub struct NumaDevice {
}

/// Represents network information.
#[derive(Debug, Serialize, Deserialize)]
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct NetworkInfo {
/// List of network interfaces.
pub interfaces: Vec<NetworkInterface>,
Expand All @@ -277,7 +280,7 @@ pub struct NetworkInfo {
}

/// Represents a network interface.
#[derive(Debug, Serialize, Deserialize)]
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct NetworkInterface {
/// Interface name.
pub name: String,
Expand All @@ -296,14 +299,14 @@ pub struct NetworkInterface {
}

/// Represents Infiniband information.
#[derive(Debug, Serialize, Deserialize)]
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct InfinibandInfo {
/// List of Infiniband interfaces.
pub interfaces: Vec<IbInterface>,
}

/// Represents an Infiniband interface.
#[derive(Debug, Serialize, Deserialize)]
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct IbInterface {
/// Interface name.
pub name: String,
Expand All @@ -320,9 +323,7 @@ pub struct NumaInfo {
pub nodes: Vec<NumaNode>,
}

pub mod posting;

#[derive(Debug, Serialize, Deserialize)]
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct InterfaceIPs {
pub interface: String,
pub ip_addresses: Vec<String>,
Expand Down
Loading