Skip to content

Commit

Permalink
list external ips with parents
Browse files Browse the repository at this point in the history
  • Loading branch information
rcgoodfellow committed Sep 26, 2023
1 parent 6c58491 commit 1c22bda
Show file tree
Hide file tree
Showing 4 changed files with 166 additions and 0 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

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

1 change: 1 addition & 0 deletions dev-tools/omdb/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ tabled.workspace = true
textwrap.workspace = true
tokio = { workspace = true, features = [ "full" ] }
uuid.workspace = true
ipnetwork.workspace = true

[dev-dependencies]
expectorate.workspace = true
Expand Down
150 changes: 150 additions & 0 deletions dev-tools/omdb/src/bin/omdb/db.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@
//! would be the only consumer -- and in that case it's okay to query the
//! database directly.
// NOTE: eminates from Tabled macros
#![allow(clippy::useless_vec)]

use crate::Omdb;
use anyhow::anyhow;
use anyhow::bail;
Expand All @@ -30,6 +33,7 @@ use nexus_db_model::DnsName;
use nexus_db_model::DnsVersion;
use nexus_db_model::DnsZone;
use nexus_db_model::Instance;
use nexus_db_model::Project;
use nexus_db_model::Sled;
use nexus_db_queries::context::OpContext;
use nexus_db_queries::db;
Expand Down Expand Up @@ -80,6 +84,7 @@ enum DbCommands {
Services(ServicesArgs),
/// Print information about sleds
Sleds,
Network(NetworkArgs),
}

#[derive(Debug, Args)]
Expand Down Expand Up @@ -156,6 +161,21 @@ enum ServicesCommands {
ListBySled,
}

#[derive(Debug, Args)]
struct NetworkArgs {
#[command(subcommand)]
command: NetworkCommands,

#[clap(long)]
verbose: bool,
}

#[derive(Debug, Subcommand)]
enum NetworkCommands {
/// List external IPs
ListEips,
}

impl DbArgs {
/// Run a `omdb db` subcommand.
pub(crate) async fn run_cmd(
Expand Down Expand Up @@ -246,6 +266,14 @@ impl DbArgs {
DbCommands::Sleds => {
cmd_db_sleds(&opctx, &datastore, self.fetch_limit).await
}

DbCommands::Network(NetworkArgs {
command: NetworkCommands::ListEips,
verbose,
}) => {
cmd_db_eips(&opctx, &datastore, self.fetch_limit, *verbose)
.await
}
}
}
}
Expand Down Expand Up @@ -881,6 +909,128 @@ async fn cmd_db_dns_names(
Ok(())
}

async fn cmd_db_eips(
opctx: &OpContext,
datastore: &DataStore,
limit: NonZeroU32,
verbose: bool,
) -> Result<(), anyhow::Error> {
let ips = datastore
.lookup_external_ips(&opctx)
.await
.context("listing external ips")?;

check_limit(&ips, limit, || String::from("listing external ips"));

struct PortRange {
first: u16,
last: u16,
}

impl Display for PortRange {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}/{}", self.first, self.last)
}
}

#[derive(Tabled)]
enum Owner {
Instance { project: String, name: String },
Service { kind: String },
None,
}

impl Display for Owner {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Instance { project, name } => {
write!(f, "Instance {project}/{name}")
}
Self::Service { kind } => write!(f, "Service {kind}"),
Self::None => write!(f, "None"),
}
}
}

#[derive(Tabled)]
struct IpRow {
ip: ipnetwork::IpNetwork,
ports: PortRange,
kind: String,
owner: Owner,
}

let mut rows = Vec::new();

for ip in &ips {
if verbose {
println!("{ip:#?}");
continue;
}

let owner = if let Some(owner_id) = ip.parent_id {
if ip.is_service {
let service = LookupPath::new(opctx, datastore)
.service_id(owner_id)
.fetch()
.await?;
Owner::Service { kind: format!("{:?}", service.1.kind) }
} else {
use db::schema::instance::dsl as instance_dsl;
let instance = instance_dsl::instance
.filter(instance_dsl::id.eq(owner_id))
.limit(1)
.select(Instance::as_select())
.load_async(datastore.pool_for_tests().await?)
.await
.context("loading requested instance")?
.pop()
.context("loading requested instance")?;

use db::schema::project::dsl as project_dsl;
let project = project_dsl::project
.filter(project_dsl::id.eq(instance.project_id))
.limit(1)
.select(Project::as_select())
.load_async(datastore.pool_for_tests().await?)
.await
.context("loading requested project")?
.pop()
.context("loading requested instance")?;

Owner::Instance {
project: project.name().to_string(),
name: instance.name().to_string(),
}
}
} else {
Owner::None
};

let row = IpRow {
ip: ip.ip,
ports: PortRange {
first: ip.first_port.into(),
last: ip.last_port.into(),
},
kind: format!("{:?}", ip.kind),
owner,
};
rows.push(row);
}

if !verbose {
rows.sort_by(|a, b| a.ip.cmp(&b.ip));
let table = tabled::Table::new(rows)
.with(tabled::settings::Style::empty())
.to_string();

println!("{}", table);
}

Ok(())
}

fn print_name(
prefix: &str,
name: &str,
Expand Down
14 changes: 14 additions & 0 deletions nexus/db-queries/src/db/datastore/external_ip.rs
Original file line number Diff line number Diff line change
Expand Up @@ -289,4 +289,18 @@ impl DataStore {
.await
.map_err(|e| public_error_from_diesel_pool(e, ErrorHandler::Server))
}

/// Fetch all external IP addresses of any kind for the provided instance
pub async fn lookup_external_ips(
&self,
opctx: &OpContext,
) -> LookupResult<Vec<ExternalIp>> {
use db::schema::external_ip::dsl;
dsl::external_ip
.filter(dsl::time_deleted.is_null())
.select(ExternalIp::as_select())
.get_results_async(self.pool_authorized(opctx).await?)
.await
.map_err(|e| public_error_from_diesel_pool(e, ErrorHandler::Server))
}
}

0 comments on commit 1c22bda

Please sign in to comment.