use ipnetwork::{IpNetwork, Ipv4Network, Ipv6Network};
use serde::{Deserialize, Serialize};
use std::net::{IpAddr, Ipv6Addr};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct NetworkResource {
pub name: String,
pub bridge_name: String,
pub namespace_name: String,
pub mycelium_address: Option<Ipv6Addr>,
pub mycelium_subnet: Option<Ipv6Network>,
pub mycelium_gateway: Option<Ipv6Addr>,
pub bridge_ipv4: Option<Ipv4Network>,
pub next_vm_index: u8,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct VmNetworkConfig {
pub vm_name: String,
pub resource_name: String,
pub tap_device: String,
pub mycelium_ip: Option<Ipv6Addr>,
pub private_ip: Option<std::net::Ipv4Addr>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct MyceliumConfig {
pub seed: Vec<u8>,
pub peers: Vec<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct MyceliumInspection {
#[serde(rename = "publicKey")]
pub public_key: String,
pub address: Ipv6Addr,
}
impl MyceliumInspection {
pub fn ip(&self) -> Ipv6Addr {
self.address
}
pub fn subnet(&self) -> Result<Ipv6Network, super::error::NetworkError> {
let mut ip_bytes = self.address.octets();
for i in 8..16 {
ip_bytes[i] = 0;
}
let subnet_ip = Ipv6Addr::from(ip_bytes);
Ipv6Network::new(subnet_ip, 64)
.map_err(|e| super::error::NetworkError::Parse(e.to_string()))
}
pub fn gateway(&self) -> Result<Ipv6Network, super::error::NetworkError> {
let subnet = self.subnet()?;
let mut gw_bytes = subnet.network().octets();
gw_bytes[15] = 1;
let gw_ip = Ipv6Addr::from(gw_bytes);
Ipv6Network::new(gw_ip, 64).map_err(|e| super::error::NetworkError::Parse(e.to_string()))
}
}
#[derive(Debug, Clone)]
pub struct Route {
pub destination: Option<IpNetwork>,
pub gateway: IpAddr,
pub interface: Option<String>,
}
#[derive(Debug, Clone)]
pub struct VethPair {
pub local_name: String,
pub peer_name: String,
pub mtu: u32,
}
#[cfg(test)]
mod tests {
use super::*;
use std::str::FromStr;
#[test]
fn test_network_resource_creation() {
let resource = NetworkResource {
name: "test_network".to_string(),
bridge_name: "br-test_network".to_string(),
namespace_name: "ntest_network".to_string(),
mycelium_address: None,
mycelium_subnet: None,
mycelium_gateway: None,
bridge_ipv4: Some(Ipv4Network::from_str("192.168.1.0/24").unwrap()),
next_vm_index: 2,
};
assert_eq!(resource.name, "test_network");
assert_eq!(resource.bridge_name, "br-test_network");
}
#[test]
fn test_mycelium_config_creation() {
let config = MyceliumConfig {
seed: vec![1, 2, 3, 4, 5],
peers: vec!["peer1".to_string(), "peer2".to_string()],
};
assert_eq!(config.seed.len(), 5);
assert_eq!(config.peers.len(), 2);
}
#[test]
fn test_mycelium_inspection_ip() {
let inspection = MyceliumInspection {
public_key: "test_key".to_string(),
address: Ipv6Addr::from_str("2001:db8::1").unwrap(),
};
assert_eq!(inspection.ip(), Ipv6Addr::from_str("2001:db8::1").unwrap());
}
#[test]
fn test_mycelium_inspection_subnet() {
let inspection = MyceliumInspection {
public_key: "test_key".to_string(),
address: Ipv6Addr::from_str("2001:db8::1234:5678").unwrap(),
};
let subnet = inspection.subnet().unwrap();
assert_eq!(subnet.prefix(), 64);
assert_eq!(subnet.network().to_string(), "2001:db8::");
}
#[test]
fn test_mycelium_inspection_gateway() {
let inspection = MyceliumInspection {
public_key: "test_key".to_string(),
address: Ipv6Addr::from_str("2001:db8::1234:5678").unwrap(),
};
let gateway = inspection.gateway().unwrap();
assert_eq!(gateway.prefix(), 64);
assert_eq!(gateway.ip().to_string(), "2001:db8::1");
}
#[test]
fn test_route_with_destination() {
let route = Route {
destination: Some(IpNetwork::from_str("10.0.0.0/24").unwrap()),
gateway: IpAddr::from_str("10.0.0.1").unwrap(),
interface: Some("eth0".to_string()),
};
assert!(route.destination.is_some());
assert_eq!(route.interface, Some("eth0".to_string()));
}
#[test]
fn test_route_default() {
let route = Route {
destination: None,
gateway: IpAddr::from_str("192.168.1.1").unwrap(),
interface: None,
};
assert!(route.destination.is_none());
assert!(route.interface.is_none());
}
#[test]
fn test_veth_pair_creation() {
let veth = VethPair {
local_name: "veth0".to_string(),
peer_name: "veth1".to_string(),
mtu: 1500,
};
assert_eq!(veth.local_name, "veth0");
assert_eq!(veth.peer_name, "veth1");
assert_eq!(veth.mtu, 1500);
}
#[test]
fn test_network_resource_serialization() {
let resource = NetworkResource {
name: "test".to_string(),
bridge_name: "br-test".to_string(),
namespace_name: "ntest".to_string(),
mycelium_address: None,
mycelium_subnet: None,
mycelium_gateway: None,
bridge_ipv4: None,
next_vm_index: 2,
};
let json = serde_json::to_string(&resource).unwrap();
let deserialized: NetworkResource = serde_json::from_str(&json).unwrap();
assert_eq!(deserialized.name, "test");
}
#[test]
fn test_mycelium_inspection_deserialization() {
let json = r#"{"publicKey":"test_key","address":"2001:db8::1"}"#;
let inspection: MyceliumInspection = serde_json::from_str(json).unwrap();
assert_eq!(inspection.public_key, "test_key");
assert_eq!(inspection.address.to_string(), "2001:db8::1");
}
#[test]
fn test_vm_network_config() {
let config = VmNetworkConfig {
vm_name: "myvm".to_string(),
resource_name: "test".to_string(),
tap_device: "tap_myvm".to_string(),
mycelium_ip: Some(Ipv6Addr::from_str("2001:db8::2").unwrap()),
private_ip: Some(std::net::Ipv4Addr::from_str("192.168.1.2").unwrap()),
};
assert_eq!(config.vm_name, "myvm");
assert_eq!(config.tap_device, "tap_myvm");
}
}