use super::{Entity, StorIter}; use nzr_api::model::SubnetData; use nzr_api::net::cidr::CidrV4; use nzr_api::net::mac::MacAddr; use serde::{Deserialize, Serialize}; use serde_with::skip_serializing_none; use std::fmt; use std::ops::Deref; use super::Storable; #[skip_serializing_none] #[derive(Clone, Serialize, Deserialize)] pub struct Subnet { pub model: SubnetData, } impl Deref for Subnet { type Target = SubnetData; fn deref(&self) -> &Self::Target { &self.model } } impl From<&Subnet> for SubnetData { fn from(value: &Subnet) -> Self { value.model.clone() } } impl From<&SubnetData> for Subnet { fn from(value: &SubnetData) -> Self { Self { model: value.clone(), } } } #[derive(Clone, Serialize, Deserialize)] pub struct Lease { pub subnet: String, pub ipv4_addr: CidrV4, pub mac_addr: MacAddr, pub inst_name: String, } #[derive(Debug)] pub enum SubnetError { DbError(sled::Error), SubnetExists, BadNetwork(nzr_api::net::cidr::Error), BadData, BadStartHost, BadEndHost, BadRange, HostOutsideRange, BadHost(nzr_api::net::cidr::Error), CantDelete(sled::Error), SubnetFull, BadDomainName, } impl fmt::Display for SubnetError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { Self::DbError(er) => write!(f, "Database error: {}", er), Self::SubnetExists => write!(f, "Subnet already exists"), Self::BadNetwork(er) => write!(f, "Error deserializing network from database: {}", er), Self::BadData => write!(f, "Malformed data in database"), Self::BadStartHost => write!(f, "Starting host is not in provided subnet"), Self::BadEndHost => write!(f, "Ending host is not in provided subnet"), Self::BadRange => write!(f, "Ending host is before starting host"), Self::HostOutsideRange => write!(f, "Available host is outside defined host range"), Self::BadHost(er) => write!( f, "Host is within range but couldn't be converted to IP: {}", er ), Self::CantDelete(de) => write!(f, "Error when trying to delete: {}", de), Self::SubnetFull => write!(f, "No addresses are left to assign in subnet"), Self::BadDomainName => { write!(f, "Invalid domain name. Must be in the format xx.yy.tld") } } } } impl std::error::Error for SubnetError {} impl Storable for Subnet { fn tree_name() -> Option<&'static [u8]> { Some(b"nets") } fn on_delete(&self, db: &sled::Db) -> Result<(), super::StorableError> { db.drop_tree(self.lease_tree()) .map_err(|e| super::StorableError::new(super::ErrType::DbError, e))?; Ok(()) } } impl Subnet { pub fn from_model(data: &nzr_api::model::SubnetData) -> Result { // validate start and end addresses if data.end_host < data.start_host { Err(SubnetError::BadRange) } else if !data.network.contains(&data.start_host) { Err(SubnetError::BadStartHost) } else if !data.network.contains(&data.end_host) { Err(SubnetError::BadEndHost) } else { let subnet = Subnet { model: data.clone(), }; Ok(subnet) } } /// Gets the lease tree from sled. pub fn lease_tree(&self) -> Vec { let mut lt_name: Vec = vec![b'L']; lt_name.extend_from_slice(&self.model.network.octets()); lt_name } } impl Storable for Lease { fn tree_name() -> Option<&'static [u8]> { None } } impl Entity { /// Create a new lease associated with the subnet. pub fn new_lease( &self, mac_addr: &MacAddr, inst_name: &str, ) -> Result, Box> { let tree = self.db.open_tree(self.lease_tree())?; let max_lease = match tree.last()? { Some(lease) => { // XXX: this is overkill, but a lazy hack for now u32::from_be_bytes(lease.0[..4].try_into().unwrap()) & !u32::from(self.model.network.netmask()) } None => self.model.start_bytes(), }; let new_ip = self .model .network .make_ip(max_lease + 1) .map_err(SubnetError::BadHost)?; let lease_data = Lease { subnet: String::from_utf8_lossy(&self.key).to_string(), ipv4_addr: CidrV4::new(new_ip, self.model.network.cidr()), mac_addr: mac_addr.clone(), inst_name: inst_name.to_owned(), }; let lease_tree = self .db .open_tree(self.lease_tree()) .map_err(SubnetError::DbError)?; let octets = lease_data.ipv4_addr.addr.octets(); let ent = Entity::transient(lease_data, octets, lease_tree, self.db.clone()); ent.update()?; Ok(ent) } /// Get an iterator over all leases in the subnet. pub fn leases(&self) -> Result, sled::Error> { let lease_tree = self.db.open_tree(self.lease_tree())?; Ok(StorIter::new(self.db.clone(), lease_tree)) } }