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 27, 2023
1 parent 9a1f6bc commit 2ddc0a9
Show file tree
Hide file tree
Showing 5 changed files with 168 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 @@ -82,6 +86,8 @@ enum DbCommands {
Sleds,
/// Print information about customer instances
Instances,
/// Print information about the network
Network(NetworkArgs),
}

#[derive(Debug, Args)]
Expand Down Expand Up @@ -158,6 +164,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 @@ -251,6 +272,13 @@ impl DbArgs {
DbCommands::Instances => {
cmd_db_instances(&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 @@ -933,6 +961,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
2 changes: 2 additions & 0 deletions dev-tools/omdb/tests/usage_errors.out
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ Commands:
services Print information about control plane services
sleds Print information about sleds
instances Print information about customer instances
network Print information about the network
help Print this message or the help of the given subcommand(s)

Options:
Expand All @@ -112,6 +113,7 @@ Commands:
services Print information about control plane services
sleds Print information about sleds
instances Print information about customer instances
network Print information about the network
help Print this message or the help of the given subcommand(s)

Options:
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 2ddc0a9

Please sign in to comment.