Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,8 @@ toml = "0.8.12"
toml_edit = "0.22.12"
url = "2.5.0"
uuid = { version = "1.8.0", features = ["serde", "v4"] }
colored = "2.1.0"
humantime = "2"

# Config for 'cargo dist'
[workspace.metadata.dist]
Expand Down
2 changes: 2 additions & 0 deletions cli/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@ thouart = { workspace = true }
tokio = { workspace = true }
url = { workspace = true }
uuid = { workspace = true }
colored = { workspace = true }
humantime = { workspace = true }

[dev-dependencies]
assert_cmd = { workspace = true }
Expand Down
27 changes: 27 additions & 0 deletions cli/docs/cli.json
Original file line number Diff line number Diff line change
Expand Up @@ -1966,6 +1966,33 @@
}
]
},
{
"name": "net",
"subcommands": [
{
"name": "bgp",
"subcommands": [
{
"name": "status",
"about": "Get the status of switch ports."
}
]
},
{
"name": "port",
"subcommands": [
{
"name": "config",
"about": "Get the configuration of switch ports."
},
{
"name": "status",
"about": "Get the status of switch ports."
}
]
}
]
},
{
"name": "ping",
"about": "Ping API",
Expand Down
70 changes: 70 additions & 0 deletions cli/src/cmd_net/bgp_status.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at https://mozilla.org/MPL/2.0/.

use crate::RunnableCmd;
use anyhow::Result;
use async_trait::async_trait;
use clap::Parser;
use colored::*;
use oxide::context::Context;
use oxide::types::{BgpPeerStatus, SwitchLocation};
use oxide::ClientSystemNetworkingExt;
use std::io::Write;
use tabwriter::TabWriter;

/// Get the status of switch ports.
#[derive(Parser, Debug, Clone)]
#[command(verbatim_doc_comment)]
#[command(name = "net bgp status")]
pub struct CmdBgpStatus {}

#[async_trait]
impl RunnableCmd for CmdBgpStatus {
async fn run(&self, ctx: &Context) -> Result<()> {
let c = ctx.client()?;

let status = c.networking_bgp_status().send().await?.into_inner();

let (sw0, sw1) = status
.iter()
.partition(|x| x.switch == SwitchLocation::Switch0);

println!("{}", "switch0".dimmed());
println!("{}", "=======".dimmed());
show_status(&sw0)?;
println!();

println!("{}", "switch1".dimmed());
println!("{}", "=======".dimmed());
show_status(&sw1)?;

Ok(())
}
}

fn show_status(st: &Vec<&BgpPeerStatus>) -> Result<()> {
let mut tw = TabWriter::new(std::io::stdout()).ansi(true);
writeln!(
&mut tw,
"{}\t{}\t{}\t{}\t{}",
"Peer Address".dimmed(),
"Local ASN".dimmed(),
"Remote ASN".dimmed(),
"Session State".dimmed(),
"State Duration".dimmed(),
)?;
for s in st {
writeln!(
tw,
"{}\t{}\t{}\t{:?}\t{}",
s.addr,
s.local_asn,
s.remote_asn,
s.state,
humantime::Duration::from(std::time::Duration::from_millis(s.state_duration_millis)),
)?;
}
tw.flush()?;
Ok(())
}
15 changes: 15 additions & 0 deletions cli/src/cmd_net/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at https://mozilla.org/MPL/2.0/.

use crate::cli_builder::NewCli;

mod bgp_status;
mod port_config;
mod port_status;

pub fn make_cli(cli: NewCli<'static>) -> NewCli<'static> {
cli.add_custom::<port_status::CmdPortStatus>("net port status")
.add_custom::<port_config::CmdPortConfig>("net port config")
.add_custom::<bgp_status::CmdBgpStatus>("net bgp status")
}
170 changes: 170 additions & 0 deletions cli/src/cmd_net/port_config.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at https://mozilla.org/MPL/2.0/.

use std::io::Write;

use crate::RunnableCmd;
use anyhow::Result;
use async_trait::async_trait;
use clap::Parser;
use colored::*;
use oxide::context::Context;
use oxide::{ClientSystemHardwareExt, ClientSystemNetworkingExt};
use tabwriter::TabWriter;

/// Get the configuration of switch ports.
#[derive(Parser, Debug, Clone)]
#[command(verbatim_doc_comment)]
#[command(name = "net port config")]
pub struct CmdPortConfig {}

#[async_trait]
impl RunnableCmd for CmdPortConfig {
async fn run(&self, ctx: &Context) -> Result<()> {
let c = ctx.client()?;

let ports = c
.networking_switch_port_list()
.limit(u32::MAX)
.send()
.await?
.into_inner()
.items;

let mut tw = TabWriter::new(std::io::stdout()).ansi(true);

// TODO bad API, having to pull all address lots to work backwards from
// address reference to address lot block is terribad
let addr_lots = c
.networking_address_lot_list()
.limit(u32::MAX)
.send()
.await?
.into_inner()
.items;

let mut addr_lot_blocks = Vec::new();
for a in addr_lots.iter() {
let blocks = c
.networking_address_lot_block_list()
.address_lot(a.id)
.limit(u32::MAX)
.send()
.await?
.into_inner()
.items;

for b in blocks.iter() {
addr_lot_blocks.push((a.clone(), b.clone()));
}
}

for p in &ports {
if let Some(id) = p.port_settings_id {
let config = c
.networking_switch_port_settings_view()
.port(id)
.send()
.await?
.into_inner();

println!(
"{}{}{}",
p.switch_location.to_string().blue(),
"/".dimmed(),
p.port_name.blue(),
);

println!(
"{}",
"=".repeat(p.port_name.len() + p.switch_location.to_string().len() + 1)
.dimmed()
);

writeln!(
&mut tw,
"{}\t{}\t{}",
"Autoneg".dimmed(),
"Fec".dimmed(),
"Speed".dimmed(),
)?;

for l in &config.links {
writeln!(&mut tw, "{:?}\t{:?}\t{:?}", l.autoneg, l.fec, l.speed,)?;
}
tw.flush()?;
println!("");

writeln!(&mut tw, "{}\t{}", "Address".dimmed(), "Lot".dimmed())?;
for a in &config.addresses {
let addr = match &a.address {
oxide::types::IpNet::V4(a) => a.to_string(),
oxide::types::IpNet::V6(a) => a.to_string(),
};

let alb = addr_lot_blocks
.iter()
.find(|x| x.1.id == a.address_lot_block_id)
.unwrap();

writeln!(&mut tw, "{}\t{}", addr, *alb.0.name)?;
}
tw.flush()?;
println!("");

writeln!(
&mut tw,
"{}\t{}\t{}\t{}\t{}\t{}\t{}\t{}\t{}\t{}\t{}\t{}\t{}\t{}\t{}\t{}",
"BGP Peer".dimmed(),
"Export".dimmed(),
"Import".dimmed(),
"Communities".dimmed(),
"Connect Retry".dimmed(),
"Delay Open".dimmed(),
"Enforce First AS".dimmed(),
"Hold Time".dimmed(),
"Idle Hold Time".dimmed(),
"Keepalive".dimmed(),
"Local Pref".dimmed(),
"Md5 Auth".dimmed(),
"Min TTL".dimmed(),
"MED".dimmed(),
"Remote ASN".dimmed(),
"VLAN".dimmed(),
)?;
for p in &config.bgp_peers {
writeln!(
&mut tw,
"{}\t{:?}\t{:?}\t{:?}\t{}\t{}\t{}\t{}\t{}\t{}\t{:?}\t{:?}\t{:?}\t{:?}\t{:?}\t{:?}",
p.addr,
p.allowed_export,
p.allowed_import,
p.communities,
p.connect_retry,
p.delay_open,
p.enforce_first_as,
p.hold_time,
p.idle_hold_time,
p.keepalive,
p.local_pref,
p.md5_auth_key,
p.min_ttl,
p.multi_exit_discriminator,
p.remote_asn,
p.vlan_id,
)?;
}
tw.flush()?;
println!("");

// Uncomment to see full payload
//println!("");
//println!("{:#?}", config);
//println!("");
}
}

Ok(())
}
}
Loading