feature(krata): implement network reservation list (#366)

This commit is contained in:
Alex Zenla
2024-08-26 15:05:57 -04:00
committed by GitHub
parent 79e27256e6
commit 19f35ef20a
16 changed files with 381 additions and 77 deletions

View File

@ -1,6 +1,7 @@
pub mod device;
pub mod host;
pub mod image;
pub mod network;
pub mod zone;
use crate::cli::device::DeviceCommand;
@ -14,6 +15,7 @@ use krata::{
events::EventStream,
v1::control::{control_service_client::ControlServiceClient, ResolveZoneIdRequest},
};
use network::NetworkCommand;
use tonic::{transport::Channel, Request};
#[derive(Parser)]
@ -36,6 +38,7 @@ pub struct ControlCommand {
pub enum ControlCommands {
Zone(ZoneCommand),
Image(ImageCommand),
Network(NetworkCommand),
Device(DeviceCommand),
Host(HostCommand),
}
@ -57,6 +60,8 @@ impl ControlCommands {
match self {
ControlCommands::Zone(zone) => zone.run(client, events).await,
ControlCommands::Network(network) => network.run(client, events).await,
ControlCommands::Image(image) => image.run(client, events).await,
ControlCommands::Device(device) => device.run(client, events).await,

View File

@ -0,0 +1,43 @@
use anyhow::Result;
use clap::{Parser, Subcommand};
use reservation::NetworkReservationCommand;
use tonic::transport::Channel;
use krata::events::EventStream;
use krata::v1::control::control_service_client::ControlServiceClient;
pub mod reservation;
#[derive(Parser)]
#[command(about = "Manage the network on the isolation engine")]
pub struct NetworkCommand {
#[command(subcommand)]
subcommand: NetworkCommands,
}
impl NetworkCommand {
pub async fn run(
self,
client: ControlServiceClient<Channel>,
events: EventStream,
) -> Result<()> {
self.subcommand.run(client, events).await
}
}
#[derive(Subcommand)]
pub enum NetworkCommands {
Reservation(NetworkReservationCommand),
}
impl NetworkCommands {
pub async fn run(
self,
client: ControlServiceClient<Channel>,
events: EventStream,
) -> Result<()> {
match self {
NetworkCommands::Reservation(reservation) => reservation.run(client, events).await,
}
}
}

View File

@ -0,0 +1,125 @@
use anyhow::Result;
use clap::{Parser, ValueEnum};
use comfy_table::{presets::UTF8_FULL_CONDENSED, Cell, Table};
use krata::{
events::EventStream,
v1::{
common::NetworkReservation,
control::{control_service_client::ControlServiceClient, ListNetworkReservationsRequest},
},
};
use serde_json::Value;
use tonic::transport::Channel;
use crate::format::{kv2line, proto2dynamic, proto2kv};
#[derive(ValueEnum, Clone, Debug, PartialEq, Eq)]
enum NetworkReservationListFormat {
Table,
Json,
JsonPretty,
Jsonl,
Yaml,
KeyValue,
Simple,
}
#[derive(Parser)]
#[command(about = "List network reservation information")]
pub struct NetworkReservationListCommand {
#[arg(short, long, default_value = "table", help = "Output format")]
format: NetworkReservationListFormat,
}
impl NetworkReservationListCommand {
pub async fn run(
self,
mut client: ControlServiceClient<Channel>,
_events: EventStream,
) -> Result<()> {
let reply = client
.list_network_reservations(ListNetworkReservationsRequest {})
.await?
.into_inner();
let mut reservations = reply.reservations;
reservations.sort_by(|a, b| a.uuid.cmp(&b.uuid));
match self.format {
NetworkReservationListFormat::Table => {
self.print_reservations_table(reservations)?;
}
NetworkReservationListFormat::Simple => {
for reservation in reservations {
println!(
"{}\t{}\t{}\t{}",
reservation.uuid, reservation.ipv4, reservation.ipv6, reservation.mac
);
}
}
NetworkReservationListFormat::Json
| NetworkReservationListFormat::JsonPretty
| NetworkReservationListFormat::Yaml => {
let mut values = Vec::new();
for device in reservations {
let message = proto2dynamic(device)?;
values.push(serde_json::to_value(message)?);
}
let value = Value::Array(values);
let encoded = if self.format == NetworkReservationListFormat::JsonPretty {
serde_json::to_string_pretty(&value)?
} else if self.format == NetworkReservationListFormat::Yaml {
serde_yaml::to_string(&value)?
} else {
serde_json::to_string(&value)?
};
println!("{}", encoded.trim());
}
NetworkReservationListFormat::Jsonl => {
for device in reservations {
let message = proto2dynamic(device)?;
println!("{}", serde_json::to_string(&message)?);
}
}
NetworkReservationListFormat::KeyValue => {
self.print_key_value(reservations)?;
}
}
Ok(())
}
fn print_reservations_table(&self, reservations: Vec<NetworkReservation>) -> Result<()> {
let mut table = Table::new();
table.load_preset(UTF8_FULL_CONDENSED);
table.set_content_arrangement(comfy_table::ContentArrangement::Dynamic);
table.set_header(vec!["uuid", "ipv4", "ipv6", "mac"]);
for reservation in reservations {
table.add_row(vec![
Cell::new(reservation.uuid),
Cell::new(reservation.ipv4),
Cell::new(reservation.ipv6),
Cell::new(reservation.mac),
]);
}
if table.is_empty() {
println!("no network reservations found");
} else {
println!("{}", table);
}
Ok(())
}
fn print_key_value(&self, reservations: Vec<NetworkReservation>) -> Result<()> {
for reservation in reservations {
let kvs = proto2kv(reservation)?;
println!("{}", kv2line(kvs));
}
Ok(())
}
}

View File

@ -0,0 +1,43 @@
use anyhow::Result;
use clap::{Parser, Subcommand};
use list::NetworkReservationListCommand;
use tonic::transport::Channel;
use krata::events::EventStream;
use krata::v1::control::control_service_client::ControlServiceClient;
pub mod list;
#[derive(Parser)]
#[command(about = "Manage network reservations")]
pub struct NetworkReservationCommand {
#[command(subcommand)]
subcommand: NetworkReservationCommands,
}
impl NetworkReservationCommand {
pub async fn run(
self,
client: ControlServiceClient<Channel>,
events: EventStream,
) -> Result<()> {
self.subcommand.run(client, events).await
}
}
#[derive(Subcommand)]
pub enum NetworkReservationCommands {
List(NetworkReservationListCommand),
}
impl NetworkReservationCommands {
pub async fn run(
self,
client: ControlServiceClient<Channel>,
events: EventStream,
) -> Result<()> {
match self {
NetworkReservationCommands::List(list) => list.run(client, events).await,
}
}
}