Skip to content
Merged
Show file tree
Hide file tree
Changes from 21 commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
a0d50a6
[sled-agent] Launch nexus on an IPv4 address. Routing still broken.
smklein Jun 30, 2022
a1ec4cd
Update the dbwipe commands for parity with dbinit
smklein Jun 30, 2022
1046a45
Merge branch 'main' into update-wipe-too
smklein Jun 30, 2022
f43c66a
Merge branch 'main' into nexus-on-v4
smklein Jun 30, 2022
a32cde7
now we're cooking with ipv4 on the underlay
smklein Jun 30, 2022
cb9e00a
Merge branch 'update-wipe-too' into nexus-on-v4
smklein Jun 30, 2022
91bf43e
create pub/private ipv4s for nexus, start setting up routes
smklein Jun 30, 2022
809140d
route from GZ to non-gz explicitly added
smklein Jun 30, 2022
26afcdb
error handling
smklein Jun 30, 2022
1516eac
Merge branch 'main' into update-wipe-too
smklein Jun 30, 2022
06ca2fc
Do a silly thing
smklein Jun 30, 2022
1d0b4c2
Create the DB too
smklein Jun 30, 2022
9f19034
Merge branch 'update-wipe-too' into nexus-on-v4
smklein Jun 30, 2022
2f76eaf
warnings
smklein Jun 30, 2022
4611e5d
224 -> 226
smklein Jun 30, 2022
3250691
Update port too
smklein Jun 30, 2022
6b94175
Merge branch 'main' into nexus-on-v4
smklein Jul 1, 2022
c0c91a1
Merge branch 'main' into nexus-on-v4
smklein Jul 1, 2022
d240f1c
Merge branch 'main' into nexus-on-v4
smklein Jul 6, 2022
d7baa27
Allocate VNIC for Nexus, default route to config-based gateway
smklein Jul 6, 2022
f5fbdd9
Clippy, dead code
smklein Jul 6, 2022
a08d958
Merge branch 'main' into nexus-on-v4
smklein Jul 6, 2022
e548459
review feedback
smklein Jul 7, 2022
c622ac6
Merge branch 'main' into nexus-on-v4
smklein Jul 7, 2022
46404fe
no more ipv4 forwarding
smklein Jul 7, 2022
3826dbb
update docs
smklein Jul 7, 2022
e118c2a
fmt
smklein Jul 7, 2022
68263c6
No default gateway, update docs
smklein Jul 7, 2022
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
4 changes: 2 additions & 2 deletions sled-agent/src/bootstrap/agent.rs
Original file line number Diff line number Diff line change
Expand Up @@ -150,15 +150,15 @@ impl Agent {
err,
})?;

let etherstub = Dladm::create_etherstub().map_err(|e| {
let etherstub = Dladm::ensure_etherstub().map_err(|e| {
BootstrapError::SledError(format!(
"Can't access etherstub device: {}",
e
))
})?;

let etherstub_vnic =
Dladm::create_etherstub_vnic(&etherstub).map_err(|e| {
Dladm::ensure_etherstub_vnic(&etherstub).map_err(|e| {
BootstrapError::SledError(format!(
"Can't access etherstub VNIC device: {}",
e
Expand Down
6 changes: 5 additions & 1 deletion sled-agent/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use crate::illumos::dladm::{self, Dladm, PhysicalLink};
use crate::illumos::zpool::ZpoolName;
use dropshot::ConfigLogging;
use serde::Deserialize;
use std::net::SocketAddr;
use std::net::{Ipv4Addr, SocketAddr};
use std::path::{Path, PathBuf};
use uuid::Uuid;

Expand All @@ -27,6 +27,10 @@ pub struct Config {
/// Optional list of zpools to be used as "discovered disks".
pub zpools: Option<Vec<ZpoolName>>,

/// Address of the Internet gateway, which is particularly
/// relevant for external-facing services (such as Nexus).
pub gateway_address: Option<Ipv4Addr>,

/// The data link on which we infer the bootstrap address.
///
/// If unsupplied, we default to the first physical device.
Expand Down
4 changes: 2 additions & 2 deletions sled-agent/src/illumos/dladm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ pub struct Dladm {}
#[cfg_attr(test, mockall::automock, allow(dead_code))]
impl Dladm {
/// Creates an etherstub, or returns one which already exists.
pub fn create_etherstub() -> Result<Etherstub, ExecutionError> {
pub fn ensure_etherstub() -> Result<Etherstub, ExecutionError> {
if let Ok(stub) = Self::get_etherstub() {
return Ok(stub);
}
Expand All @@ -136,7 +136,7 @@ impl Dladm {
///
/// This VNIC is not tracked like [`crate::illumos::vnic::Vnic`], because
/// it is expected to exist for the lifetime of the sled.
pub fn create_etherstub_vnic(
pub fn ensure_etherstub_vnic(
source: &Etherstub,
) -> Result<EtherstubVnic, CreateVnicError> {
if let Ok(vnic) = Self::get_etherstub_vnic() {
Expand Down
68 changes: 65 additions & 3 deletions sled-agent/src/illumos/running_zone.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,14 @@
//! Utilities to manage running zones.

use crate::illumos::addrobj::AddrObject;
use crate::illumos::dladm::Etherstub;
use crate::illumos::svc::wait_for_service;
use crate::illumos::vnic::{Vnic, VnicAllocator};
use crate::illumos::zone::{AddressRequest, ZONE_PREFIX};
use crate::opte::OptePort;
use ipnetwork::IpNetwork;
use slog::Logger;
use std::net::Ipv6Addr;
use std::net::{Ipv4Addr, Ipv6Addr};
use std::path::PathBuf;

#[cfg(test)]
Expand Down Expand Up @@ -153,14 +154,22 @@ impl RunningZone {
&self,
addrtype: AddressRequest,
) -> Result<IpNetwork, EnsureAddressError> {
info!(self.inner.log, "Adding address: {:?}", addrtype);
let name = match addrtype {
AddressRequest::Dhcp => "omicron",
AddressRequest::Static(net) => match net.ip() {
std::net::IpAddr::V4(_) => "omicron4",
std::net::IpAddr::V6(_) => "omicron6",
},
};
self.ensure_address_with_name(addrtype, name).await
}

pub async fn ensure_address_with_name(
&self,
addrtype: AddressRequest,
name: &str,
) -> Result<IpNetwork, EnsureAddressError> {
info!(self.inner.log, "Adding address: {:?}", addrtype);
let addrobj = AddrObject::new(self.inner.control_vnic.name(), name)
.map_err(|err| EnsureAddressError::AddrObject {
request: addrtype,
Expand All @@ -172,6 +181,31 @@ impl RunningZone {
Ok(network)
}

// TODO: Remove once Nexus uses OPTE - external addresses should generally
// be served via OPTE.
pub async fn ensure_external_address_with_name(
&self,
addrtype: AddressRequest,
name: &str,
) -> Result<IpNetwork, EnsureAddressError> {
info!(self.inner.log, "Adding address: {:?}", addrtype);
let addrobj = AddrObject::new(
self.inner.physical_vnic
.as_ref()
.expect("Cannot allocate external address on zone without physical VNIC")
.name(),
name
)
.map_err(|err| EnsureAddressError::AddrObject {
request: addrtype,
zone: self.inner.name.clone(),
err,
})?;
let network =
Zones::ensure_address(Some(&self.inner.name), &addrobj, addrtype)?;
Ok(network)
}

pub async fn add_default_route(
&self,
gateway: Ipv6Addr,
Expand All @@ -187,6 +221,19 @@ impl RunningZone {
Ok(())
}

pub async fn add_default_route4(
&self,
gateway: Ipv4Addr,
) -> Result<(), RunCommandError> {
self.run_cmd(&[
"/usr/sbin/route",
"add",
"default",
&gateway.to_string(),
])?;
Ok(())
}

/// Looks up a running zone based on the `zone_prefix`, if one already exists.
///
/// - If the zone was found, is running, and has a network interface, it is
Expand Down Expand Up @@ -249,6 +296,7 @@ impl RunningZone {
//
// Re-initialize guest_vnic state by inspecting the zone.
opte_ports: vec![],
physical_vnic: None,
},
})
}
Expand Down Expand Up @@ -301,6 +349,10 @@ pub struct InstalledZone {

// OPTE devices for the guest network interfaces
opte_ports: Vec<OptePort>,

// Physical VNIC possibly provisioned to the zone.
// TODO: Remove once Nexus traffic is transmitted over OPTE.
physical_vnic: Option<Vnic>,
}

impl InstalledZone {
Expand All @@ -325,14 +377,16 @@ impl InstalledZone {
zone_name
}

#[allow(clippy::too_many_arguments)]
pub async fn install(
log: &Logger,
vnic_allocator: &VnicAllocator,
vnic_allocator: &VnicAllocator<Etherstub>,
service_name: &str,
unique_name: Option<&str>,
datasets: &[zone::Dataset],
devices: &[zone::Device],
opte_ports: Vec<OptePort>,
physical_vnic: Option<Vnic>,
) -> Result<InstalledZone, InstallZoneError> {
let control_vnic = vnic_allocator.new_control(None).map_err(|err| {
InstallZoneError::CreateVnic {
Expand All @@ -345,10 +399,17 @@ impl InstalledZone {
let zone_image_path =
PathBuf::from(&format!("/opt/oxide/{}.tar.gz", service_name));

let phys_iter = if let Some(ref vnic) = physical_vnic {
vec![vnic.name().to_string()].into_iter()
} else {
vec![].into_iter()
};

let net_device_names: Vec<String> = opte_ports
.iter()
.map(|port| port.vnic().name().to_string())
.chain(std::iter::once(control_vnic.name().to_string()))
.chain(phys_iter)
.collect();

Zones::install_omicron_zone(
Expand All @@ -370,6 +431,7 @@ impl InstalledZone {
name: zone_name,
control_vnic,
opte_ports,
physical_vnic,
})
}
}
13 changes: 7 additions & 6 deletions sled-agent/src/illumos/vnic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
//! API for controlling a single instance.

use crate::illumos::dladm::{
CreateVnicError, DeleteVnicError, Etherstub, VNIC_PREFIX,
CreateVnicError, DeleteVnicError, VnicSource, VNIC_PREFIX,
VNIC_PREFIX_CONTROL, VNIC_PREFIX_GUEST,
};
use omicron_common::api::external::MacAddr;
Expand All @@ -23,13 +23,13 @@ use crate::illumos::dladm::MockDladm as Dladm;
/// May be used to allocate runtime-unique IDs for objects
/// which have naming constraints - such as VNICs.
#[derive(Clone, Debug)]
pub struct VnicAllocator {
pub struct VnicAllocator<DL: VnicSource + 'static> {
value: Arc<AtomicU64>,
scope: String,
data_link: Etherstub,
data_link: DL,
}

impl VnicAllocator {
impl<DL: VnicSource + Clone> VnicAllocator<DL> {
/// Creates a new Vnic name allocator with a particular scope.
///
/// The intent with varying scopes is to create non-overlapping
Expand All @@ -41,11 +41,11 @@ impl VnicAllocator {
///
/// VnicAllocator::new("Storage") produces
/// - oxControlStorage[NNN]
pub fn new<S: AsRef<str>>(scope: S, etherstub: Etherstub) -> Self {
pub fn new<S: AsRef<str>>(scope: S, data_link: DL) -> Self {
Self {
value: Arc::new(AtomicU64::new(0)),
scope: scope.as_ref().to_string(),
data_link: etherstub,
data_link,
}
}

Expand Down Expand Up @@ -167,6 +167,7 @@ impl Drop for Vnic {
#[cfg(test)]
mod test {
use super::*;
use crate::illumos::dladm::Etherstub;

#[test]
fn test_allocate() {
Expand Down
8 changes: 4 additions & 4 deletions sled-agent/src/illumos/zone.rs
Original file line number Diff line number Diff line change
Expand Up @@ -101,8 +101,8 @@ pub struct EnsureAddressError {
#[derive(thiserror::Error, Debug)]
#[error("Failed to create address {address} with name {name} in the GZ on {link:?}: {err}. Note to developers: {extra_note}")]
pub struct EnsureGzAddressError {
address: Ipv6Addr,
link: EtherstubVnic,
address: IpAddr,
link: String,
name: String,
#[source]
err: anyhow::Error,
Expand Down Expand Up @@ -580,8 +580,8 @@ impl Zones {
Ok(())
}(link.clone(), address, name)
.map_err(|err| EnsureGzAddressError {
address,
link,
address: IpAddr::V6(address),
link: link.0.clone(),
name: name.to_string(),
err,
extra_note:
Expand Down
9 changes: 6 additions & 3 deletions sled-agent/src/instance.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
//! API for controlling a single instance.

use crate::common::instance::{Action as InstanceAction, InstanceStates};
use crate::illumos::dladm::Etherstub;
use crate::illumos::running_zone::{
InstalledZone, RunCommandError, RunningZone,
};
Expand Down Expand Up @@ -204,7 +205,7 @@ struct InstanceInner {
propolis_ip: IpAddr,

// NIC-related properties
vnic_allocator: VnicAllocator,
vnic_allocator: VnicAllocator<Etherstub>,

// OPTE port related properties
underlay_addr: Ipv6Addr,
Expand Down Expand Up @@ -400,7 +401,7 @@ mockall::mock! {
pub fn new(
log: Logger,
id: Uuid,
vnic_allocator: VnicAllocator,
vnic_allocator: VnicAllocator<Etherstub>,
underlay_addr: Ipv6Addr,
port_allocator: OptePortAllocator,
initial: InstanceHardware,
Expand Down Expand Up @@ -446,7 +447,7 @@ impl Instance {
pub fn new(
log: Logger,
id: Uuid,
vnic_allocator: VnicAllocator,
vnic_allocator: VnicAllocator<Etherstub>,
underlay_addr: Ipv6Addr,
port_allocator: OptePortAllocator,
initial: InstanceHardware,
Expand Down Expand Up @@ -534,6 +535,8 @@ impl Instance {
zone::Device { name: "/dev/viona".to_string() },
],
opte_ports,
// physical_vnic=
None,
)
.await?;

Expand Down
2 changes: 1 addition & 1 deletion sled-agent/src/instance_manager.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ struct InstanceManagerInternal {
/// A mapping from a Sled Agent "Instance ID" to ("Propolis ID", [Instance]).
instances: Mutex<BTreeMap<Uuid, (Uuid, Instance)>>,

vnic_allocator: VnicAllocator,
vnic_allocator: VnicAllocator<Etherstub>,
underlay_addr: Ipv6Addr,
port_allocator: OptePortAllocator,
}
Expand Down
2 changes: 1 addition & 1 deletion sled-agent/src/params.rs
Original file line number Diff line number Diff line change
Expand Up @@ -303,7 +303,7 @@ impl From<DatasetEnsureBody> for sled_agent_client::types::DatasetEnsureBody {
)]
#[serde(tag = "type", rename_all = "snake_case")]
pub enum ServiceType {
Nexus { internal_address: SocketAddrV6, external_address: SocketAddrV6 },
Nexus { internal_address: SocketAddrV6, external_address: SocketAddr },
InternalDns { server_address: SocketAddrV6, dns_address: SocketAddrV6 },
Oximeter,
}
Expand Down
Loading