Skip to content

Commit 20b7413

Browse files
committed
feat(mgmt): impl Display for VPC & Peerings
Signed-off-by: Fredi Raspall <[email protected]>
1 parent 078534e commit 20b7413

File tree

4 files changed

+331
-8
lines changed

4 files changed

+331
-8
lines changed

mgmt/src/rpc/overlay/display.rs

+191
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,191 @@
1+
// SPDX-License-Identifier: Apache-2.0
2+
// Copyright Open Network Fabric Authors
3+
4+
use crate::rpc::overlay::vpc::Vpc;
5+
use routing::pretty_utils::Heading;
6+
use std::fmt::Display;
7+
8+
use crate::rpc::overlay::VpcManifest;
9+
use crate::rpc::overlay::vpc::Peering;
10+
use crate::rpc::overlay::vpc::VpcTable;
11+
use crate::rpc::overlay::vpcpeering::{VpcExpose, VpcPeering, VpcPeeringTable};
12+
13+
const SEP: &str = " ";
14+
15+
impl Display for VpcExpose {
16+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
17+
let mut carriage = false;
18+
if !self.ips.is_empty() {
19+
write!(f, "{SEP} prefixes:")?;
20+
self.ips.iter().for_each(|x| {
21+
let _ = write!(f, " {x}");
22+
});
23+
}
24+
if !self.nots.is_empty() {
25+
write!(f, ", except")?;
26+
self.nots.iter().for_each(|x| {
27+
let _ = write!(f, " {x}");
28+
});
29+
}
30+
31+
writeln!(f)?;
32+
33+
if !self.as_range.is_empty() {
34+
write!(f, "{SEP} as:")?;
35+
self.as_range.iter().for_each(|x| {
36+
let _ = write!(f, " {x}");
37+
});
38+
carriage = true;
39+
}
40+
41+
if !self.not_as.is_empty() {
42+
write!(f, ", excluding")?;
43+
self.not_as.iter().for_each(|x| {
44+
let _ = write!(f, " {x}");
45+
});
46+
carriage = true;
47+
}
48+
if carriage { writeln!(f) } else { Ok(()) }
49+
}
50+
}
51+
52+
// Vpc manifest is common to VpcPeering and Peering
53+
fn fmt_manifest(
54+
f: &mut std::fmt::Formatter<'_>,
55+
is_local: bool,
56+
57+
manifest: &VpcManifest,
58+
) -> std::fmt::Result {
59+
if is_local {
60+
writeln!(f, " local:")?;
61+
} else {
62+
writeln!(f, " remote, {}:", manifest.name)?;
63+
}
64+
65+
for e in &manifest.exposes {
66+
e.fmt(f)?;
67+
}
68+
Ok(())
69+
}
70+
71+
impl Display for Peering {
72+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
73+
writeln!(f, " ■ {}:", self.name)?;
74+
fmt_manifest(f, true, &self.local)?;
75+
writeln!(f)?;
76+
fmt_manifest(f, false, &self.remote)?;
77+
writeln!(f)
78+
}
79+
}
80+
81+
/* ========= VPCs =========*/
82+
83+
macro_rules! VPC_TBL_FMT {
84+
() => {
85+
" {:<18} {:<8} {:<9} {:<18} {:<18}"
86+
};
87+
}
88+
fn fmt_vpc_table_heading(f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
89+
writeln!(
90+
f,
91+
"{}",
92+
format_args!(
93+
VPC_TBL_FMT!(),
94+
"VPC", "VNI", "peers", "remote", "peering name"
95+
)
96+
)
97+
}
98+
99+
// Auxiliary type to implement detailed VPC display
100+
pub struct VpcDetailed<'a>(pub &'a Vpc);
101+
impl Display for VpcDetailed<'_> {
102+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
103+
let vpc = self.0;
104+
Heading(format!("vpc: {}", vpc.name)).fmt(f)?;
105+
writeln!(f, " name: {}", vpc.name)?;
106+
writeln!(f, " vni : {}", vpc.vni)?;
107+
writeln!(f, " peerings: {}", vpc.peerings.len())?;
108+
Heading(format!("Peerings of {}", vpc.name)).fmt(f)?;
109+
for peering in &vpc.peerings {
110+
peering.fmt(f)?;
111+
}
112+
Ok(())
113+
}
114+
}
115+
116+
impl Display for Vpc {
117+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
118+
// VPC that has no peerings
119+
if self.peerings.is_empty() {
120+
writeln!(
121+
f,
122+
"{}",
123+
format_args!(VPC_TBL_FMT!(), &self.name, self.vni, "", "", "")
124+
)?;
125+
} else {
126+
// VPC that has peerings
127+
for (num, peering) in self.peerings.iter().enumerate() {
128+
let (name, vni, num_peers) = if num == 0 {
129+
(
130+
self.name.as_str(),
131+
self.vni.to_string(),
132+
self.peerings.len().to_string(),
133+
)
134+
} else {
135+
("", "".to_string(), "".to_string())
136+
};
137+
writeln!(
138+
f,
139+
"{}",
140+
format_args!(
141+
VPC_TBL_FMT!(),
142+
name, vni, num_peers, peering.remote.name, peering.name
143+
)
144+
)?;
145+
}
146+
}
147+
Ok(())
148+
}
149+
}
150+
impl Display for VpcTable {
151+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
152+
Heading(format!("VPCs ({})", self.len())).fmt(f)?;
153+
fmt_vpc_table_heading(f)?;
154+
for vpc in self.values() {
155+
vpc.fmt(f)?;
156+
}
157+
Ok(())
158+
}
159+
}
160+
161+
/* ===== VPC peerings =====*/
162+
163+
fn fmt_peering_manifest(
164+
f: &mut std::fmt::Formatter<'_>,
165+
manifest: &VpcManifest,
166+
) -> std::fmt::Result {
167+
writeln!(f, " {}:", manifest.name)?;
168+
for e in &manifest.exposes {
169+
e.fmt(f)?;
170+
}
171+
Ok(())
172+
}
173+
174+
impl Display for VpcPeering {
175+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
176+
writeln!(f, " ■ {}:", self.name)?;
177+
fmt_peering_manifest(f, &self.left)?;
178+
writeln!(f)?;
179+
fmt_peering_manifest(f, &self.right)?;
180+
writeln!(f)
181+
}
182+
}
183+
impl Display for VpcPeeringTable {
184+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
185+
Heading(format!("VPC Peering Table ({})", self.len())).fmt(f)?;
186+
for peering in self.values() {
187+
peering.fmt(f)?;
188+
}
189+
Ok(())
190+
}
191+
}

mgmt/src/rpc/overlay/mod.rs

+1
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
//! Dataplane configuration model: overlay configuration
55
6+
pub mod display;
67
pub mod tests;
78
pub mod vpc;
89
pub mod vpcpeering;

mgmt/src/rpc/overlay/tests.rs

+136-8
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
pub mod test {
99
use crate::rpc::ApiError;
1010
use crate::rpc::overlay::Overlay;
11+
use crate::rpc::overlay::display::VpcDetailed;
1112
use crate::rpc::overlay::vpc::{Vpc, VpcTable};
1213
use crate::rpc::overlay::vpcpeering::VpcExpose;
1314
use crate::rpc::overlay::vpcpeering::VpcManifest;
@@ -17,25 +18,25 @@ pub mod test {
1718

1819
/* Build sample manifests for a peering */
1920
fn build_manifest_vpc1() -> VpcManifest {
20-
let mut vpc1 = VpcManifest::new("VPC-1");
21+
let mut m1 = VpcManifest::new("VPC-1");
2122
let expose = VpcExpose::empty()
2223
.ip(Prefix::from(("10.0.0.0", 25)))
2324
.ip(Prefix::from(("10.0.2.128", 25)))
2425
.not(Prefix::from(("10.0.1.13", 32)))
2526
.not(Prefix::from(("10.0.2.130", 32)))
2627
.as_range(Prefix::from(("100.64.1.0", 24)))
2728
.not_as(Prefix::from(("100.64.1.13", 32)));
28-
vpc1.add_expose(expose).expect("Should succeed");
29-
vpc1
29+
m1.add_expose(expose).expect("Should succeed");
30+
m1
3031
}
3132
fn build_manifest_vpc2() -> VpcManifest {
32-
let mut vpc1 = VpcManifest::new("VPC-2");
33+
let mut m2 = VpcManifest::new("VPC-2");
3334
let expose = VpcExpose::empty()
3435
.ip(Prefix::from(("10.0.0.0", 24)))
3536
.as_range(Prefix::from(("100.64.2.0", 24)));
3637

37-
vpc1.add_expose(expose).expect("Should succeed");
38-
vpc1
38+
m2.add_expose(expose).expect("Should succeed");
39+
m2
3940
}
4041

4142
/* build sample peering between VPC-1 and VPC-2 */
@@ -44,7 +45,7 @@ pub mod test {
4445
let m1 = build_manifest_vpc1();
4546
let m2 = build_manifest_vpc2();
4647
// build vpc peering with the manifests
47-
VpcPeering::new("VPC-1-to-VPC-2", m1, m2)
48+
VpcPeering::new("VPC-1--VPC-2", m1, m2)
4849
}
4950

5051
#[test]
@@ -113,10 +114,12 @@ pub mod test {
113114
let mut peering_table = VpcPeeringTable::new();
114115
peering_table.add(peering).expect("Should succeed");
115116

117+
println!("{peering_table}");
118+
116119
/* build overlay object and validate it */
117120
let overlay = Overlay::new(vpc_table, peering_table);
118121
assert_eq!(overlay.validate(), Ok(()));
119-
println!("{overlay:#?}");
122+
//println!("{overlay:#?}");
120123
}
121124

122125
#[test]
@@ -154,4 +157,129 @@ pub mod test {
154157
assert!(x.contains(&"Peering-4".to_owned()));
155158
assert!(!x.contains(&"Peering-3".to_owned()), "not there");
156159
}
160+
161+
#[test]
162+
fn test_vpc_collect_peerings() {
163+
fn man_vpc1_with_vpc2() -> VpcManifest {
164+
let mut m1 = VpcManifest::new("VPC-1");
165+
let expose = VpcExpose::empty()
166+
.ip(Prefix::from(("192.168.50.0", 24)))
167+
.not(Prefix::from(("192.168.50.13", 32)));
168+
m1.add_expose(expose).expect("Should succeed");
169+
170+
let expose = VpcExpose::empty()
171+
.ip(Prefix::from(("192.168.111.0", 24)))
172+
.not(Prefix::from(("192.168.111.2", 32)))
173+
.not(Prefix::from(("192.168.111.254", 32)))
174+
.as_range(Prefix::from(("100.64.200.0", 24)))
175+
.not_as(Prefix::from(("100.64.200.13", 32)));
176+
m1.add_expose(expose).expect("Should succeed");
177+
m1
178+
}
179+
fn man_vpc1_with_vpc3() -> VpcManifest {
180+
let mut m1 = VpcManifest::new("VPC-1");
181+
let expose = VpcExpose::empty().ip(Prefix::from(("192.168.60.0", 24)));
182+
m1.add_expose(expose).expect("Should succeed");
183+
m1
184+
}
185+
fn man_vpc1_with_vpc4() -> VpcManifest {
186+
let mut m1 = VpcManifest::new("VPC-1");
187+
let expose = VpcExpose::empty().ip(Prefix::from(("192.168.60.0", 24)));
188+
m1.add_expose(expose).expect("Should succeed");
189+
m1
190+
}
191+
fn man_vpc2() -> VpcManifest {
192+
let mut m1 = VpcManifest::new("VPC-2");
193+
let expose = VpcExpose::empty().ip(Prefix::from(("192.168.80.0", 24)));
194+
m1.add_expose(expose).expect("Should succeed");
195+
m1
196+
}
197+
fn man_vpc2_with_vpc3() -> VpcManifest {
198+
let mut m1 = VpcManifest::new("VPC-2");
199+
let expose = VpcExpose::empty().ip(Prefix::from(("192.168.80.0", 24)));
200+
m1.add_expose(expose).expect("Should succeed");
201+
m1
202+
}
203+
fn man_vpc3() -> VpcManifest {
204+
let mut m1 = VpcManifest::new("VPC-3");
205+
let expose = VpcExpose::empty().ip(Prefix::from(("192.168.128.0", 27)));
206+
m1.add_expose(expose).expect("Should succeed");
207+
m1
208+
}
209+
fn man_vpc4() -> VpcManifest {
210+
let mut m1 = VpcManifest::new("VPC-4");
211+
let expose = VpcExpose::empty()
212+
.ip(Prefix::from(("192.168.201.1", 32)))
213+
.ip(Prefix::from(("192.168.202.2", 32)))
214+
.ip(Prefix::from(("192.168.203.3", 32)));
215+
m1.add_expose(expose).expect("Should succeed");
216+
217+
let expose = VpcExpose::empty()
218+
.ip(Prefix::from(("192.168.204.4", 32)))
219+
.as_range(Prefix::from(("100.64.204.4", 32)));
220+
m1.add_expose(expose).expect("Should succeed");
221+
222+
let expose = VpcExpose::empty()
223+
.ip(Prefix::from(("192.168.210.0", 29)))
224+
.not(Prefix::from(("192.168.210.1", 32)));
225+
m1.add_expose(expose).expect("Should succeed");
226+
227+
m1
228+
}
229+
230+
/* build VPC table with 3 vpcs */
231+
let mut vpc_table = VpcTable::new();
232+
let _ = vpc_table.add(Vpc::new("VPC-1", 3000).expect("Should succeed"));
233+
let _ = vpc_table.add(Vpc::new("VPC-2", 4000).expect("Should succeed"));
234+
let _ = vpc_table.add(Vpc::new("VPC-3", 2000).expect("Should succeed"));
235+
let _ = vpc_table.add(Vpc::new("VPC-4", 6000).expect("Should succeed"));
236+
237+
/* build peering table with 3 peerings */
238+
let mut peering_table = VpcPeeringTable::new();
239+
peering_table
240+
.add(VpcPeering::new(
241+
"VPC-1--VPC-2",
242+
man_vpc1_with_vpc2(),
243+
man_vpc2(),
244+
))
245+
.expect("Should succeed");
246+
247+
peering_table
248+
.add(VpcPeering::new(
249+
"VPC-1--VPC-3",
250+
man_vpc1_with_vpc3(),
251+
man_vpc3(),
252+
))
253+
.expect("Should succeed");
254+
255+
peering_table
256+
.add(VpcPeering::new(
257+
"VPC-1--VPC-4",
258+
man_vpc1_with_vpc4(),
259+
man_vpc4(),
260+
))
261+
.expect("Should succeed");
262+
263+
peering_table
264+
.add(VpcPeering::new(
265+
"VPC-2--VPC-3",
266+
man_vpc2_with_vpc3(),
267+
man_vpc3(),
268+
))
269+
.expect("Should succeed");
270+
271+
/* display peering table */
272+
println!("{peering_table}");
273+
274+
/* collect the peerings for each VPC */
275+
vpc_table.collect_peerings(&peering_table);
276+
277+
/* display VPC table */
278+
println!("{vpc_table}");
279+
280+
/* get vpc VPC1 */
281+
if let Some(vpc) = vpc_table.get_vpc("VPC-1") {
282+
println!("{}", VpcDetailed(vpc));
283+
}
284+
}
157285
}

0 commit comments

Comments
 (0)