176 lines
5.3 KiB
Rust
176 lines
5.3 KiB
Rust
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<Self, SubnetError> {
|
|
// 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<u8> {
|
|
let mut lt_name: Vec<u8> = 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<Subnet> {
|
|
/// Create a new lease associated with the subnet.
|
|
pub fn new_lease(
|
|
&self,
|
|
mac_addr: &MacAddr,
|
|
inst_name: &str,
|
|
) -> Result<Entity<Lease>, Box<dyn std::error::Error>> {
|
|
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<StorIter<Lease>, sled::Error> {
|
|
let lease_tree = self.db.open_tree(self.lease_tree())?;
|
|
Ok(StorIter::new(self.db.clone(), lease_tree))
|
|
}
|
|
}
|