Skip to content

Notify sled-agent of firewall rules #465

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 21 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
3cb2352
Add instance_id to NetworkInterface
teisenbe Nov 17, 2021
85190d5
Implement a basic network interface model
teisenbe Nov 29, 2021
9772513
Move subnet IP allocation to its own file
teisenbe Dec 1, 2021
bf0d74c
Merge branch 'main' into plumb-network-interface
teisenbe Dec 1, 2021
6f87c14
Add header to subnet_allocation.rs
teisenbe Dec 1, 2021
6c54559
Convert all names to IPs
teisenbe Nov 19, 2021
91a790d
Make target resolution go to interfaces instead of ips
teisenbe Nov 24, 2021
7a1b9a0
Most of the way to plumbed to sled agent
teisenbe Nov 24, 2021
9e55717
Add indices
teisenbe Nov 24, 2021
2820ec2
Add support for VPC targets/filters and fix bad merge
teisenbe Dec 2, 2021
2cbb977
Add basic tests for the allocation query generation
teisenbe Dec 4, 2021
74a5d8f
Merge remote-tracking branch 'origin/main' into plumb-network-interface
teisenbe Dec 6, 2021
347e66e
Make code match RFD174
teisenbe Dec 6, 2021
556c672
Implement a basic /vpcs/<vpc>/subnets/<subnet>/ips endpoint
teisenbe Dec 7, 2021
b323cd9
Verify that new instances get a network interface
teisenbe Dec 7, 2021
b04d5d9
Introduce a test for IP address allocation
teisenbe Dec 8, 2021
dc15e54
Respect address reservations from RFD21
teisenbe Dec 8, 2021
b5f4a89
Merge remote-tracking branch 'origin/main' into plumb-network-interface
teisenbe Dec 8, 2021
8aef5d8
Merge branch 'plumb-network-interface' into firewall-notify-sled
teisenbe Dec 8, 2021
09c409b
Clean up error messages, error in inetgw case
teisenbe Dec 8, 2021
9840569
Merge branch 'main' into firewall-notify-sled
teisenbe Dec 16, 2021
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
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.

5 changes: 5 additions & 0 deletions common/src/sql/dbinit.sql
Original file line number Diff line number Diff line change
Expand Up @@ -438,6 +438,11 @@ CREATE UNIQUE INDEX ON omicron.public.network_interface (
) WHERE
time_deleted IS NULL;

CREATE INDEX ON omicron.public.network_interface (
instance_id
) WHERE
time_deleted IS NULL;

/* Ensure we do not assign the same MAC twice within a VPC
* See RFD174's discussion on the scope of virtual MACs
*/
Expand Down
173 changes: 173 additions & 0 deletions nexus/src/db/datastore.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ use omicron_common::api::external::{
CreateResult, IdentityMetadataCreateParams,
};
use omicron_common::bail_unless;
use std::collections::HashMap;
use std::convert::TryFrom;
use std::sync::Arc;
use uuid::Uuid;
Expand Down Expand Up @@ -847,6 +848,149 @@ impl DataStore {
}
}

/// Identify all IPs in use by each instance
// TODO: how to name/where to put this
pub async fn resolve_instances_to_interfaces<
T: IntoIterator<Item = Name>,
>(
&self,
vpc: &Vpc,
instance_names: T,
) -> Result<HashMap<Name, Vec<NetworkInterface>>, Error> {
use db::schema::{instance, network_interface};
// TODO-performance: paginate the results of this query?
let ifaces = network_interface::table
.inner_join(
instance::table
.on(instance::id.eq(network_interface::instance_id)),
)
.select((instance::name, NetworkInterface::as_select()))
.filter(instance::project_id.eq(vpc.project_id))
.filter(instance::name.eq_any(instance_names))
.filter(network_interface::time_deleted.is_null())
.filter(instance::time_deleted.is_null())
.get_results_async(self.pool())
.await
.map_err(|e| {
public_error_from_diesel_pool(
e,
ResourceType::Instance,
LookupType::Other("Resolving to IPs".to_string()),
)
})?;

let mut result = HashMap::with_capacity(ifaces.len());
for (name, iface) in ifaces.into_iter() {
result.entry(name).or_insert(vec![]).push(iface)
}
Ok(result)
}

/// Identify all VNICs connected to each VpcSubnet
// TODO: how to name/where to put this
pub async fn resolve_subnets_to_interfaces<T: IntoIterator<Item = Name>>(
&self,
vpc: &Vpc,
subnet_names: T,
) -> Result<HashMap<Name, Vec<NetworkInterface>>, Error> {
use db::schema::{network_interface, vpc_subnet};
// TODO-performance: paginate the results of this query?
let subnets = network_interface::table
.inner_join(
vpc_subnet::table
.on(vpc_subnet::id.eq(network_interface::subnet_id)),
)
.select((vpc_subnet::name, NetworkInterface::as_select()))
.filter(vpc_subnet::vpc_id.eq(vpc.id()))
.filter(vpc_subnet::name.eq_any(subnet_names))
.filter(network_interface::time_deleted.is_null())
.filter(vpc_subnet::time_deleted.is_null())
.get_results_async(self.pool())
.await
.map_err(|e| {
public_error_from_diesel_pool(
e,
ResourceType::VpcSubnet,
LookupType::Other("Resolving to interfaces".to_string()),
)
})?;
let mut result = HashMap::with_capacity(subnets.len());
for (name, interface) in subnets.into_iter() {
let entry = result.entry(name).or_insert(vec![]);
entry.push(interface);
}
Ok(result)
}

/// Identify all VNICs connected to each Vpc
// TODO: how to name/where to put this
pub async fn resolve_vpcs_to_interfaces<T: IntoIterator<Item = Name>>(
&self,
project_id: &Uuid,
vpc_names: T,
) -> Result<HashMap<Name, Vec<NetworkInterface>>, Error> {
use db::schema::{network_interface, vpc};
// TODO-performance: paginate the results of this query?
let interfaces = network_interface::table
.inner_join(vpc::table.on(vpc::id.eq(network_interface::vpc_id)))
.select((vpc::name, NetworkInterface::as_select()))
.filter(vpc::project_id.eq(*project_id))
.filter(vpc::name.eq_any(vpc_names))
.filter(network_interface::time_deleted.is_null())
.filter(vpc::time_deleted.is_null())
.get_results_async(self.pool())
.await
.map_err(|e| {
public_error_from_diesel_pool(
e,
ResourceType::Vpc,
LookupType::Other("Resolving to interfaces".to_string()),
)
})?;
let mut result = HashMap::with_capacity(interfaces.len());
for (name, interface) in interfaces.into_iter() {
let entry = result.entry(name).or_insert(vec![]);
entry.push(interface);
}
Ok(result)
}

/// Identify all subnets in use by each VpcSubnet
// TODO: how to name/where to put this
pub async fn resolve_subnets_to_ips<T: IntoIterator<Item = Name>>(
&self,
vpc: &Vpc,
subnet_names: T,
) -> Result<HashMap<Name, Vec<ipnetwork::IpNetwork>>, Error> {
use db::schema::vpc_subnet;
// TODO-performance: paginate the results of this query?
let subnets = vpc_subnet::table
.select(VpcSubnet::as_select())
.filter(vpc_subnet::vpc_id.eq(vpc.id()))
.filter(vpc_subnet::name.eq_any(subnet_names))
.filter(vpc_subnet::time_deleted.is_null())
.get_results_async(self.pool())
.await
.map_err(|e| {
public_error_from_diesel_pool(
e,
ResourceType::VpcSubnet,
LookupType::Other("Resolving to IPs".to_string()),
)
})?;
let mut result = HashMap::with_capacity(subnets.len());
for subnet in subnets {
let entry = result.entry(subnet.name().clone()).or_insert(vec![]);
if let Some(block) = subnet.ipv4_block {
entry.push(ipnetwork::IpNetwork::V4(block.0 .0))
}
if let Some(block) = subnet.ipv6_block {
entry.push(ipnetwork::IpNetwork::V6(block.0 .0))
}
}
Ok(result)
}

/*
* Disks
*/
Expand Down Expand Up @@ -1616,6 +1760,35 @@ impl DataStore {
})
}

pub async fn vpc_resolve_to_sleds(
&self,
vpc_id: &Uuid,
) -> Result<Vec<Sled>, Error> {
use db::schema::{instance, network_interface, sled};

// Resolve each VNIC in the VPC to the Sled its on, so we know which
// Sleds to notify when firewall rules change.
network_interface::table
.inner_join(
instance::table
.on(instance::id.eq(network_interface::instance_id)),
)
.inner_join(sled::table.on(sled::id.eq(instance::active_server_id)))
.filter(network_interface::vpc_id.eq(*vpc_id))
.filter(network_interface::time_deleted.is_null())
.filter(instance::time_deleted.is_null())
.select(Sled::as_select())
.get_results_async(self.pool())
.await
.map_err(|e| {
public_error_from_diesel_pool(
e,
ResourceType::Vpc,
LookupType::Other("Resolving to sleds".to_string()),
)
})
}

pub async fn vpc_list_subnets(
&self,
vpc_id: &Uuid,
Expand Down
9 changes: 5 additions & 4 deletions nexus/src/db/model.rs
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@ macro_rules! impl_enum_type {
AsExpression,
FromSqlRow,
Eq,
Hash,
PartialEq,
Ord,
PartialOrd,
Expand Down Expand Up @@ -1434,7 +1435,7 @@ impl_enum_type!(
#[postgres(type_name = "vpc_firewall_rule_status", type_schema = "public")]
pub struct VpcFirewallRuleStatusEnum;

#[derive(Clone, Debug, AsExpression, FromSqlRow)]
#[derive(Clone, Copy, Debug, AsExpression, FromSqlRow)]
#[sql_type = "VpcFirewallRuleStatusEnum"]
pub struct VpcFirewallRuleStatus(pub external::VpcFirewallRuleStatus);

Expand All @@ -1449,7 +1450,7 @@ impl_enum_type!(
#[postgres(type_name = "vpc_firewall_rule_direction", type_schema = "public")]
pub struct VpcFirewallRuleDirectionEnum;

#[derive(Clone, Debug, AsExpression, FromSqlRow)]
#[derive(Clone, Copy, Debug, AsExpression, FromSqlRow)]
#[sql_type = "VpcFirewallRuleDirectionEnum"]
pub struct VpcFirewallRuleDirection(pub external::VpcFirewallRuleDirection);

Expand All @@ -1464,7 +1465,7 @@ impl_enum_type!(
#[postgres(type_name = "vpc_firewall_rule_action", type_schema = "public")]
pub struct VpcFirewallRuleActionEnum;

#[derive(Clone, Debug, AsExpression, FromSqlRow)]
#[derive(Clone, Copy, Debug, AsExpression, FromSqlRow)]
#[sql_type = "VpcFirewallRuleActionEnum"]
pub struct VpcFirewallRuleAction(pub external::VpcFirewallRuleAction);

Expand All @@ -1479,7 +1480,7 @@ impl_enum_type!(
#[postgres(type_name = "vpc_firewall_rule_protocol", type_schema = "public")]
pub struct VpcFirewallRuleProtocolEnum;

#[derive(Clone, Debug, AsExpression, FromSqlRow)]
#[derive(Clone, Copy, Debug, AsExpression, FromSqlRow)]
#[sql_type = "VpcFirewallRuleProtocolEnum"]
pub struct VpcFirewallRuleProtocol(pub external::VpcFirewallRuleProtocol);

Expand Down
Loading