use std::collections::{BTreeSet, HashMap};
use itertools::Itertools;
use serde::{Deserialize, Serialize};
use serde_json::json;
use crate::{
config::{ConfigExpr, ConfigModifier, NetworkConfig},
event::EventQueue,
network::Network,
ospf::OspfImpl,
types::{AsId, NetworkDeviceRef, NetworkError, Prefix, PrefixMap, RouterId},
};
const JSON_FIELD_NAME_NETWORK: &str = "net";
const JSON_FIELD_NAME_CONFIG: &str = "config_nodes_routes";
type ExportRoutes<P> = (RouterId, P, Vec<AsId>, Option<u32>, BTreeSet<u32>);
impl<P, Q, Ospf> Network<P, Q, Ospf>
where
P: Prefix,
Q: EventQueue<P> + Serialize,
Ospf: OspfImpl,
{
pub fn as_json_str(&self) -> String {
serde_json::to_string(&json!({
JSON_FIELD_NAME_NETWORK: serde_json::to_value(self).unwrap(),
JSON_FIELD_NAME_CONFIG: self.as_config_json_str(),
}))
.unwrap()
}
pub fn as_json_str_compact(&self) -> String {
serde_json::to_string(&json!({
JSON_FIELD_NAME_CONFIG: self.as_config_json_str()
}))
.unwrap()
}
fn as_config_json_str(&self) -> serde_json::Value {
let config = Vec::from_iter(self.get_config().unwrap().iter().cloned());
let mut nodes: Vec<(RouterId, String, Option<AsId>)> = self
.devices()
.map(|r| match r {
NetworkDeviceRef::InternalRouter(r) => (r.router_id(), r.name().to_string(), None),
NetworkDeviceRef::ExternalRouter(r) => {
(r.router_id(), r.name().to_string(), Some(r.as_id()))
}
})
.collect();
nodes.sort_by_key(|(r, _, _)| *r);
let links: Vec<(RouterId, RouterId)> = self
.ospf
.edges()
.map(|e| (e.src(), e.dst()))
.map(|(a, b)| if a < b { (a, b) } else { (b, a) })
.unique()
.collect();
let routes: Vec<ExportRoutes<P>> = self
.external_routers()
.flat_map(|r| {
let id = r.router_id();
r.get_advertised_routes().values().map(move |route| {
(
id,
route.prefix,
route.as_path.clone(),
route.med,
route.community.clone(),
)
})
})
.collect();
serde_json::to_value(&(config, nodes, links, routes)).unwrap()
}
}
impl<P, Q, Ospf> Network<P, Q, Ospf>
where
P: Prefix,
Q: EventQueue<P>,
Ospf: OspfImpl,
for<'a> Q: Deserialize<'a>,
{
pub fn from_json_str<F>(s: &str, default_queue: F) -> Result<Self, NetworkError>
where
F: FnOnce() -> Q,
{
let content: serde_json::Value = serde_json::from_str(s)?;
if let Some(net) = content
.get(JSON_FIELD_NAME_NETWORK)
.and_then(|v| serde_json::from_value(v.clone()).ok())
{
Ok(net)
} else {
match content
.get(JSON_FIELD_NAME_CONFIG)
.and_then(|v| v.as_array())
{
Some(v) if v.len() == 4 => Self::from_config_nodes_routes(
v[0].clone(),
v[1].clone(),
v[2].clone(),
v[3].clone(),
default_queue,
),
_ => Err(serde_json::from_str::<ConfigNodeRoutes>(s).unwrap_err())?,
}
}
}
}
impl<P, Q, Ospf> Network<P, Q, Ospf>
where
P: Prefix,
Q: EventQueue<P>,
Ospf: OspfImpl,
{
fn from_config_nodes_routes<F>(
config: serde_json::Value,
nodes: serde_json::Value,
links: serde_json::Value,
routes: serde_json::Value,
default_queue: F,
) -> Result<Self, NetworkError>
where
F: FnOnce() -> Q,
{
let config: Vec<ConfigExpr<P>> = serde_json::from_value(config)?;
let nodes: Vec<(RouterId, String, Option<AsId>)> = serde_json::from_value(nodes)?;
let links: Vec<(RouterId, RouterId)> = serde_json::from_value(links)?;
let routes: Vec<ExportRoutes<P>> = serde_json::from_value(routes)?;
let mut nodes_lut: HashMap<RouterId, RouterId> = HashMap::new();
let mut net = Network::new(default_queue());
for (id, name, as_id) in nodes.into_iter() {
let new_id = if let Some(as_id) = as_id {
net.add_external_router(name, as_id)
} else {
net.add_router(name)
};
nodes_lut.insert(id, new_id);
}
let node = |id: RouterId| {
nodes_lut
.get(&id)
.copied()
.ok_or(NetworkError::DeviceNotFound(id))
};
let links = links
.into_iter()
.map(|(a, b)| Ok::<_, NetworkError>((node(a)?, node(b)?)))
.collect::<Result<Vec<_>, _>>()?;
net.add_links_from(links)?;
for expr in config.iter() {
let expr = match expr.clone() {
ConfigExpr::IgpLinkWeight {
source,
target,
weight,
} => ConfigExpr::IgpLinkWeight {
source: node(source)?,
target: node(target)?,
weight,
},
ConfigExpr::OspfArea {
source,
target,
area,
} => ConfigExpr::OspfArea {
source: node(source)?,
target: node(target)?,
area,
},
ConfigExpr::BgpSession {
source,
target,
session_type,
} => ConfigExpr::BgpSession {
source: node(source)?,
target: node(target)?,
session_type,
},
ConfigExpr::BgpRouteMap {
router,
neighbor,
direction,
map,
} => ConfigExpr::BgpRouteMap {
router: node(router)?,
neighbor: node(neighbor)?,
direction,
map,
},
ConfigExpr::StaticRoute {
router,
prefix,
target,
} => ConfigExpr::StaticRoute {
router: node(router)?,
prefix,
target,
},
ConfigExpr::LoadBalancing { router } => ConfigExpr::LoadBalancing {
router: node(router)?,
},
};
net.apply_modifier(&ConfigModifier::Insert(expr))?;
}
for (src, prefix, as_path, med, community) in routes.into_iter() {
net.advertise_external_route(src, prefix, as_path, med, community)?;
}
Ok(net)
}
}
#[derive(Debug, Deserialize)]
struct ConfigNodeRoutes {
#[allow(dead_code)]
config_nodes_routes: (serde_json::Value, serde_json::Value, serde_json::Value),
}