Skip to content
Open
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
35 changes: 35 additions & 0 deletions crates/api-core/src/cfg/file.rs
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,11 @@ pub struct CarbideConfig {
/// instead.
pub networks: Option<HashMap<String, NetworkDefinition>>,

/// VPCs to create at startup. Use the
/// `CreateVpc` gRPC to create them later
/// instead.
pub vpcs: Option<HashMap<String, VpcDefinition>>,

/// IPMI tool implementation for DPU power control
/// (e.g., "prod" or "fake").
pub dpu_ipmi_tool_impl: Option<String>,
Expand Down Expand Up @@ -1557,6 +1562,8 @@ pub struct InitialObjectsConfig {
pub pools: Option<HashMap<String, ResourcePoolDef>>,
/// Network Segment definitions
pub networks: Option<HashMap<String, NetworkDefinition>>,
/// VPC definitions
pub vpcs: Option<HashMap<String, VpcDefinition>>,
}

/// TLS certificate and key configuration for securing
Expand Down Expand Up @@ -2124,6 +2131,7 @@ fn default_mqtt_broker_port() -> u16 {
}

pub use carbide_dpa_manager::config::{DpaConfig, MqttAuthConfig, MqttAuthMode};
use model::vpc::VpcDefinition;

/// DSX Exchange Event Bus configuration for publishing state change events via MQTT 3.1.1.
///
Expand Down Expand Up @@ -2323,6 +2331,7 @@ mod tests {
use std::sync::atomic::Ordering as AtomicOrdering;

use carbide_authn::config::CertComponent;
use carbide_network::virtualization::VpcVirtualizationType;
use carbide_site_explorer::config::SiteExplorerExploreMode;
use chrono::Datelike;
use figment::Figment;
Expand Down Expand Up @@ -3726,6 +3735,7 @@ firmware_url = "https://firmware.example.com/fw-b.bin"
let config: InitialObjectsConfig = Toml::from_path(f.as_path()).unwrap();
let pools = config.pools.as_ref().unwrap();
let networks = config.networks.as_ref().unwrap();
let vpcs = config.vpcs.as_ref().unwrap();

assert_eq!(
networks.get("admin").unwrap(),
Expand All @@ -3736,6 +3746,7 @@ firmware_url = "https://firmware.example.com/fw-b.bin"
mtu: 9000,
reserve_first: 5,
allocation_strategy: Default::default(),
vpc_name: None,
}
);

Expand All @@ -3748,6 +3759,30 @@ firmware_url = "https://firmware.example.com/fw-b.bin"
mtu: 1500,
reserve_first: 5,
allocation_strategy: Default::default(),
vpc_name: None,
}
);

assert_eq!(
networks.get("ZERO-DPU-HOST-01-SWP7").unwrap(),
&NetworkDefinition {
segment_type: NetworkDefinitionSegmentType::HostInband,
prefix: "10.217.18.192/30".parse().unwrap(),
gateway: "10.217.18.193".parse().unwrap(),
mtu: 1500,
reserve_first: 1,
allocation_strategy: Default::default(),
vpc_name: Some("zero-dpu-vpc".to_string()),
}
);

assert_eq!(
vpcs.get("zero-dpu-vpc").unwrap(),
&VpcDefinition {
organization_id: Some("2829bbe3-c169-4cd9-8b2a-19a8b1618a93".to_string()),
network_virtualization_type: VpcVirtualizationType::Flat,
routing_profile_type: None,
vni: None,
}
);

Expand Down
12 changes: 12 additions & 0 deletions crates/api-core/src/cfg/test_data/initial_objects.toml
Original file line number Diff line number Diff line change
Expand Up @@ -31,3 +31,15 @@ prefix = "172.99.0.0/26"
gateway = "172.99.0.1"
mtu = 1500
reserve_first = 5

[networks.ZERO-DPU-HOST-01-SWP7]
type = "hostinband"
prefix = "10.217.18.192/30"
gateway = "10.217.18.193"
mtu = 1500
reserve_first = 1
vpc_name = "zero-dpu-vpc"

[vpcs.zero-dpu-vpc]
organization_id = "2829bbe3-c169-4cd9-8b2a-19a8b1618a93"
network_virtualization_type = "flat"
87 changes: 86 additions & 1 deletion crates/api-core/src/db_init.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
use std::collections::HashMap;

use carbide_network::virtualization::VpcVirtualizationType;
use carbide_uuid::vpc::VpcId;
use db::dns::domain;
use db::network_segment::reconcile_network_defs;
use db::vpc::{self};
Expand All @@ -29,7 +30,9 @@ use model::machine::upgrade_policy::AgentUpgradePolicy;
use model::metadata::Metadata;
use model::network_prefix::NewNetworkPrefix;
use model::network_segment::{NetworkDefinition, NetworkSegmentType, NewNetworkSegment};
use model::vpc::{NewVpc, VpcStatus};
use model::resource_pool;
use model::resource_pool::ResourcePool;
use model::vpc::{NewVpc, VpcDefinition, VpcStatus, VpcVirtualizationTypeCapabilities};
use sqlx::{Pool, Postgres};

use crate::CarbideError;
Expand Down Expand Up @@ -96,12 +99,36 @@ pub async fn create_initial_networks(
tracing::debug!("Network segment {name} exists");
continue;
}

let mut ns = NewNetworkSegment::build_from(name, domain_id, def)?;
ns.can_stretch = Some(true);
ns.vpc_id = if let Some(vpc_name) = &def.vpc_name {
match db::vpc::find_by_name(&mut txn, vpc_name).await?.as_slice() {
[vpc] => {
vpc.network_virtualization_type
.ensure_supports_segment(&ns)?;
Some(vpc.id)
}
[] => {
return Err(CarbideError::InvalidArgument(format!(
"Network segment {name} references VPC {vpc_name}, but no VPC with that name exists"
)));
}
_ => {
return Err(CarbideError::InvalidArgument(format!(
"Network segment {name} references VPC {vpc_name}, but multiple VPCs with that name exist"
)));
}
}
} else {
None
};

// Capture before `save` moves `ns`. `insert_network_def` needs
// the id because `network_def.segment_id` is FK-bound to it.
let segment_id = ns.id;
// update_network_segments_svi_ip will take care of allocating svi ip.
tracing::info!("Creating network segment {name} from config: {ns:?}");
crate::handlers::network_segment::save(api, &mut txn, ns, true, false).await?;
// Snapshot the network definition in the same transaction as the network_segment row,
// so the two stay consistent across restarts.
Expand All @@ -115,6 +142,64 @@ pub async fn create_initial_networks(
Ok(())
}

pub async fn create_initial_vpcs(
db_pool: &Pool<Postgres>,
vpcs: &HashMap<String, VpcDefinition>,
vni_pool: &ResourcePool<i32>,
) -> Result<(), CarbideError> {
let mut txn = Transaction::begin(db_pool).await?;
for (name, def) in vpcs {
if db::vpc::find_by_name(&mut txn, name)
.await
.is_ok_and(|v| !v.is_empty())
{
tracing::debug!("VPC {name} exists");
continue;
}

let vpc_id = VpcId::new();
let tenant_organization_id = def
.organization_id
.clone()
.unwrap_or(uuid::Uuid::new_v4().into());

let vni = db::resource_pool::allocate(
vni_pool,
&mut txn,
resource_pool::OwnerType::Vpc,
vpc_id.to_string().as_ref(),
def.vni,
)
.await?;

let vpc = NewVpc {
id: vpc_id,
tenant_organization_id,
network_virtualization_type: def.network_virtualization_type,
metadata: Metadata {
name: name.to_owned(),
..Default::default()
},
network_security_group_id: None,
routing_profile_type: def.routing_profile_type.clone(),
vni: Some(vni),
};

// Validation
if def.routing_profile_type.is_some() {
def.network_virtualization_type
.ensure_supports_routing_profiles()
.map_err(CarbideError::from)?;
}

db::vpc::persist(vpc, VpcStatus { vni: Some(vni) }, &mut txn).await?;
tracing::info!("Created VPC {name}");
}

txn.commit().await?;
Ok(())
}

/// Create the static-assignments anchor segment if it doesn't exist.
/// This segment holds external static IP assignments that don't fall
/// within any managed network prefix. The 169.254.254.254/32 prefix is
Expand Down
Loading
Loading