use core::sync::atomic::AtomicU32;
use std::net::IpAddr;
use std::{collections::HashMap, path::PathBuf, sync::atomic::Ordering};
use std::sync::RwLock;
use inetnum::asn::Asn;
use routecore::bmp::message::RibType;
#[derive(Debug, Default)]
pub struct Register {
serial: AtomicU32,
info: RwLock<HashMap<IngressId, IngressInfo>>,
}
pub type IngressId = u32;
macro_rules! update_field {
($old:ident, $new:ident, $field:ident) => {
if $new.$field.is_some() {
$old.$field = $new.$field;
}
}
}
impl Register {
pub(crate) fn new() -> Self {
Self {
serial: 1.into(),
info: RwLock::new(HashMap::new()),
}
}
pub fn overview(&self) -> String {
let lock = self.info.read().unwrap();
let mut res = String::new();
for (id, info) in lock.iter() {
res.push_str(&format!(
"{id:02} {}\t\t{}\n",
info.remote_asn.map(|a|a.to_string()).unwrap_or("".to_string()),
info.remote_addr.map(|a|a.to_string()).unwrap_or("".to_string()),
));
}
res
}
pub(crate) fn register(&self) -> IngressId {
self.serial.fetch_add(1, Ordering::Relaxed)
}
pub(crate) fn update_info(
&self,
id: IngressId,
new_info: IngressInfo,
) -> Option<IngressInfo> {
let mut lock = self.info.write().unwrap();
log::debug!("update_info for {id} with {new_info:?}");
if let Some(mut old) = lock.remove(&id) {
update_field!(old, new_info, unit_name);
update_field!(old, new_info, parent_ingress);
update_field!(old, new_info, remote_addr);
update_field!(old, new_info, remote_asn);
update_field!(old, new_info, rib_type);
update_field!(old, new_info, filename);
update_field!(old, new_info, name);
update_field!(old, new_info, desc);
lock.insert(id, old) } else {
lock.insert(id, new_info) }
}
pub fn get(&self, id: IngressId) -> Option<IngressInfo> {
self.info.read().unwrap().get(&id).cloned()
}
pub fn ids_for_parent(&self, parent: IngressId) -> Vec<IngressId> {
let mut res = Vec::new();
for (id, info) in self.info.read().unwrap().iter() {
if info.parent_ingress == Some(parent) {
res.push(*id);
}
}
res
}
pub fn find_existing_peer(
&self,
query: &IngressInfo
) -> Option<(IngressId, IngressInfo)> {
let lock = self.info.read().unwrap();
for (id, info) in lock.iter() {
if info.parent_ingress.is_some()
&& info.remote_addr.is_some()
&& info.remote_asn.is_some()
&& info.parent_ingress == query.parent_ingress
&& info.remote_asn == query.remote_asn
&& info.remote_addr == query.remote_addr
&& info.rib_type == query.rib_type
{
return Some((*id, info.clone()))
}
}
None
}
pub fn find_existing_bmp_router(
&self,
query: &IngressInfo
) -> Option<(IngressId, IngressInfo)> {
let lock = self.info.read().unwrap();
log::debug!("query: {query:?}");
for (id, info) in lock.iter() {
if info.parent_ingress.is_some()
&& info.remote_addr.is_some()
&& info.parent_ingress == query.parent_ingress
&& info.remote_addr == query.remote_addr
{
return Some((*id, info.clone()))
}
}
log::debug!("no match in find_existing_bmp_router");
None
}
}
#[derive(Clone, Debug, Default)]
#[serde_with::skip_serializing_none]
#[derive(serde::Serialize)]
pub struct IngressInfo {
pub unit_name: Option<String>,
pub parent_ingress: Option<IngressId>,
pub remote_addr: Option<IpAddr>,
pub remote_asn: Option<Asn>,
pub rib_type: Option<RibType>,
pub filename: Option<PathBuf>,
pub name: Option<String>,
pub desc: Option<String>,
}
impl IngressInfo {
pub fn new() -> Self {
Self::default()
}
pub fn with_unit_name(self, unit_name: impl Into<String>) -> Self {
Self { unit_name: Some(unit_name.into()), ..self }
}
pub fn with_parent(self, parent: IngressId) -> Self {
Self { parent_ingress: Some(parent), ..self }
}
pub fn with_remote_addr(self, addr: IpAddr) -> Self {
Self { remote_addr: Some(addr), ..self }
}
pub fn with_remote_asn(self, asn: Asn) -> Self {
Self { remote_asn: Some(asn), ..self }
}
pub fn with_rib_type(self, rib_type: RibType) -> Self {
Self { rib_type: Some(rib_type), ..self }
}
pub fn with_filename(self, path: PathBuf) -> Self {
Self { filename: Some(path), ..self }
}
pub fn with_name(self, name: impl Into<String>) -> Self {
Self { name: Some(name.into()), ..self }
}
pub fn with_desc(self, desc: impl Into<String>) -> Self {
Self { desc: Some(desc.into()), ..self }
}
}
#[cfg(test)]
mod tests {
}