Skip to content

Commit

Permalink
Plumb 'PathInPool' structure
Browse files Browse the repository at this point in the history
  • Loading branch information
smklein committed Jul 1, 2024
1 parent 6869d92 commit 4292158
Show file tree
Hide file tree
Showing 8 changed files with 154 additions and 117 deletions.
181 changes: 93 additions & 88 deletions illumos-utils/src/running_zone.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ use crate::link::{Link, VnicAllocator};
use crate::opte::{Port, PortTicket};
use crate::svc::wait_for_service;
use crate::zone::{AddressRequest, IPADM, ZONE_PREFIX};
use crate::zpool::PathInPool;
use camino::{Utf8Path, Utf8PathBuf};
use camino_tempfile::Utf8TempDir;
use ipnetwork::IpNetwork;
Expand Down Expand Up @@ -806,85 +807,85 @@ impl RunningZone {
/// returned.
/// - Other errors may be returned attempting to look up and accessing an
/// address on the zone.
pub async fn get(
log: &Logger,
vnic_allocator: &VnicAllocator<Etherstub>,
zone_prefix: &str,
addrtype: AddressRequest,
) -> Result<Self, GetZoneError> {
let zone_info = Zones::get()
.await
.map_err(|err| GetZoneError::GetZones {
prefix: zone_prefix.to_string(),
err,
})?
.into_iter()
.find(|zone_info| zone_info.name().starts_with(&zone_prefix))
.ok_or_else(|| GetZoneError::NotFound {
prefix: zone_prefix.to_string(),
})?;

if zone_info.state() != zone::State::Running {
return Err(GetZoneError::NotRunning {
name: zone_info.name().to_string(),
state: zone_info.state(),
});
}

let zone_name = zone_info.name();
let vnic_name =
Zones::get_control_interface(zone_name).map_err(|err| {
GetZoneError::ControlInterface {
name: zone_name.to_string(),
err,
}
})?;
let addrobj = AddrObject::new_control(&vnic_name).map_err(|err| {
GetZoneError::AddrObject { name: zone_name.to_string(), err }
})?;
Zones::ensure_address(Some(zone_name), &addrobj, addrtype).map_err(
|err| GetZoneError::EnsureAddress {
name: zone_name.to_string(),
err,
},
)?;

let control_vnic = vnic_allocator
.wrap_existing(vnic_name)
.expect("Failed to wrap valid control VNIC");

// The bootstrap address for a running zone never changes,
// so there's no need to call `Zones::ensure_address`.
// Currently, only the switch zone has a bootstrap interface.
let bootstrap_vnic = Zones::get_bootstrap_interface(zone_name)
.map_err(|err| GetZoneError::BootstrapInterface {
name: zone_name.to_string(),
err,
})?
.map(|name| {
vnic_allocator
.wrap_existing(name)
.expect("Failed to wrap valid bootstrap VNIC")
});

Ok(Self {
id: zone_info.id().map(|x| {
x.try_into().expect("zoneid_t is expected to be an i32")
}),
inner: InstalledZone {
log: log.new(o!("zone" => zone_name.to_string())),
zonepath: zone_info.path().to_path_buf().try_into()?,
name: zone_name.to_string(),
control_vnic,
// TODO(https://github.com/oxidecomputer/omicron/issues/725)
//
// Re-initialize guest_vnic state by inspecting the zone.
opte_ports: vec![],
links: vec![],
bootstrap_vnic,
},
})
}
// pub async fn get(
// log: &Logger,
// vnic_allocator: &VnicAllocator<Etherstub>,
// zone_prefix: &str,
// addrtype: AddressRequest,
// ) -> Result<Self, GetZoneError> {
// let zone_info = Zones::get()
// .await
// .map_err(|err| GetZoneError::GetZones {
// prefix: zone_prefix.to_string(),
// err,
// })?
// .into_iter()
// .find(|zone_info| zone_info.name().starts_with(&zone_prefix))
// .ok_or_else(|| GetZoneError::NotFound {
// prefix: zone_prefix.to_string(),
// })?;
//
// if zone_info.state() != zone::State::Running {
// return Err(GetZoneError::NotRunning {
// name: zone_info.name().to_string(),
// state: zone_info.state(),
// });
// }
//
// let zone_name = zone_info.name();
// let vnic_name =
// Zones::get_control_interface(zone_name).map_err(|err| {
// GetZoneError::ControlInterface {
// name: zone_name.to_string(),
// err,
// }
// })?;
// let addrobj = AddrObject::new_control(&vnic_name).map_err(|err| {
// GetZoneError::AddrObject { name: zone_name.to_string(), err }
// })?;
// Zones::ensure_address(Some(zone_name), &addrobj, addrtype).map_err(
// |err| GetZoneError::EnsureAddress {
// name: zone_name.to_string(),
// err,
// },
// )?;
//
// let control_vnic = vnic_allocator
// .wrap_existing(vnic_name)
// .expect("Failed to wrap valid control VNIC");
//
// // The bootstrap address for a running zone never changes,
// // so there's no need to call `Zones::ensure_address`.
// // Currently, only the switch zone has a bootstrap interface.
// let bootstrap_vnic = Zones::get_bootstrap_interface(zone_name)
// .map_err(|err| GetZoneError::BootstrapInterface {
// name: zone_name.to_string(),
// err,
// })?
// .map(|name| {
// vnic_allocator
// .wrap_existing(name)
// .expect("Failed to wrap valid bootstrap VNIC")
// });
//
// Ok(Self {
// id: zone_info.id().map(|x| {
// x.try_into().expect("zoneid_t is expected to be an i32")
// }),
// inner: InstalledZone {
// log: log.new(o!("zone" => zone_name.to_string())),
// zonepath: zone_info.path().to_path_buf().try_into()?,
// name: zone_name.to_string(),
// control_vnic,
// // TODO(https://github.com/oxidecomputer/omicron/issues/725)
// //
// // Re-initialize guest_vnic state by inspecting the zone.
// opte_ports: vec![],
// links: vec![],
// bootstrap_vnic,
// },
// })
// }

/// Return references to the OPTE ports for this zone.
pub fn opte_ports(&self) -> impl Iterator<Item = &Port> {
Expand Down Expand Up @@ -1081,7 +1082,7 @@ pub struct InstalledZone {
log: Logger,

// Filesystem path of the zone
zonepath: Utf8PathBuf,
zonepath: PathInPool,

// Name of the Zone.
name: String,
Expand Down Expand Up @@ -1131,7 +1132,7 @@ impl InstalledZone {

/// Returns the filesystem path to the zonepath
pub fn zonepath(&self) -> &Utf8Path {
&self.zonepath
&self.zonepath.path
}

pub fn site_profile_xml_path(&self) -> Utf8PathBuf {
Expand All @@ -1147,7 +1148,7 @@ impl InstalledZone {

/// Returns the filesystem path to the zone's root in the GZ.
pub fn root(&self) -> Utf8PathBuf {
self.zonepath.join(Self::ROOT_FS_PATH)
self.zonepath.path.join(Self::ROOT_FS_PATH)
}
}

Expand Down Expand Up @@ -1198,7 +1199,7 @@ pub struct ZoneBuilder<'a> {
/// Allocates the NIC used for control plane communication.
underlay_vnic_allocator: Option<&'a VnicAllocator<Etherstub>>,
/// Filesystem path at which the installed zone will reside.
zone_root_path: Option<&'a Utf8Path>,
zone_root_path: Option<PathInPool>,
/// The directories that will be searched for the image tarball for the
/// provided zone type ([`Self::with_zone_type`]).
zone_image_paths: Option<&'a [Utf8PathBuf]>,
Expand Down Expand Up @@ -1251,7 +1252,7 @@ impl<'a> ZoneBuilder<'a> {
}

/// Filesystem path at which the installed zone will reside.
pub fn with_zone_root_path(mut self, root_path: &'a Utf8Path) -> Self {
pub fn with_zone_root_path(mut self, root_path: PathInPool) -> Self {
self.zone_root_path = Some(root_path);
self
}
Expand Down Expand Up @@ -1345,8 +1346,11 @@ impl<'a> ZoneBuilder<'a> {
self.zone_type?,
self.unique_name,
);
let zonepath = temp_dir
.join(self.zone_root_path?.strip_prefix("/").unwrap())
let mut zonepath = self.zone_root_path?;
zonepath.path = temp_dir
.join(
zonepath.path.strip_prefix("/").unwrap()
)
.join(&full_zone_name);
let iz = InstalledZone {
log: self.log?,
Expand Down Expand Up @@ -1376,7 +1380,7 @@ impl<'a> ZoneBuilder<'a> {
let Self {
log: Some(log),
underlay_vnic_allocator: Some(underlay_vnic_allocator),
zone_root_path: Some(zone_root_path),
zone_root_path: Some(mut zone_root_path),
zone_image_paths: Some(zone_image_paths),
zone_type: Some(zone_type),
unique_name,
Expand Down Expand Up @@ -1440,6 +1444,7 @@ impl<'a> ZoneBuilder<'a> {
net_device_names.sort();
net_device_names.dedup();

zone_root_path.path = zone_root_path.path.join(&full_zone_name);
Zones::install_omicron_zone(
&log,
&zone_root_path,
Expand All @@ -1460,7 +1465,7 @@ impl<'a> ZoneBuilder<'a> {

Ok(InstalledZone {
log: log.new(o!("zone" => full_zone_name.clone())),
zonepath: zone_root_path.join(&full_zone_name),
zonepath: zone_root_path,
name: full_zone_name,
control_vnic,
bootstrap_vnic,
Expand Down
6 changes: 3 additions & 3 deletions illumos-utils/src/zone.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ use std::net::{IpAddr, Ipv6Addr};

use crate::addrobj::AddrObject;
use crate::dladm::{EtherstubVnic, VNIC_PREFIX_BOOTSTRAP, VNIC_PREFIX_CONTROL};
use crate::zpool::PathInPool;
use crate::{execute, PFEXEC};
use omicron_common::address::SLED_PREFIX;

Expand Down Expand Up @@ -282,7 +283,7 @@ impl Zones {
#[allow(clippy::too_many_arguments)]
pub async fn install_omicron_zone(
log: &Logger,
zone_root_path: &Utf8Path,
zone_root_path: &PathInPool,
zone_name: &str,
zone_image: &Utf8Path,
datasets: &[zone::Dataset],
Expand Down Expand Up @@ -319,10 +320,9 @@ impl Zones {
true,
zone::CreationOptions::Blank,
);
let path = zone_root_path.join(zone_name);
cfg.get_global()
.set_brand("omicron1")
.set_path(&path)
.set_path(&zone_root_path.path)
.set_autoboot(false)
.set_ip_type(zone::IpType::Exclusive);
if !limit_priv.is_empty() {
Expand Down
15 changes: 14 additions & 1 deletion illumos-utils/src/zpool.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
//! Utilities for managing Zpools.
use crate::{execute, ExecutionError, PFEXEC};
use camino::Utf8Path;
use camino::{Utf8Path, Utf8PathBuf};
use std::str::FromStr;

pub use omicron_common::zpool_name::ZpoolName;
Expand Down Expand Up @@ -181,6 +181,19 @@ impl FromStr for ZpoolInfo {
/// Wraps commands for interacting with ZFS pools.
pub struct Zpool {}

/// A path which exists within a pool.
///
/// By storing these types together, it's possible to answer
/// whether or not a path exists on a particular device.
// Technically we could re-derive the pool name from the path,
// but that involves some string parsing, and honestly I'd just
// Rather Not.
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct PathInPool {
pub pool: Option<ZpoolName>,
pub path: Utf8PathBuf,
}

#[cfg_attr(any(test, feature = "testing"), mockall::automock, allow(dead_code))]
impl Zpool {
pub fn create(
Expand Down
14 changes: 8 additions & 6 deletions sled-agent/src/instance.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ use omicron_common::api::internal::shared::{
use omicron_common::backoff;
use omicron_uuid_kinds::{GenericUuid, InstanceUuid, PropolisUuid};
use propolis_client::Client as PropolisClient;
use rand::prelude::SliceRandom;
use rand::prelude::IteratorRandom;
use rand::SeedableRng;
use sled_storage::dataset::ZONE_DATASET;
use sled_storage::manager::StorageHandle;
Expand Down Expand Up @@ -1343,20 +1343,22 @@ impl InstanceRunner {
// configured VNICs.
let zname = propolis_zone_name(self.propolis_id());
let mut rng = rand::rngs::StdRng::from_entropy();
let root = self
let latest_disks = self
.storage
.get_latest_disks()
.await
.all_u2_mountpoints(ZONE_DATASET)
.all_u2_mountpoints(ZONE_DATASET);

let root = latest_disks
.into_iter()
.choose(&mut rng)
.ok_or_else(|| Error::U2NotFound)?
.clone();
.ok_or_else(|| Error::U2NotFound)?;
let installed_zone = self
.zone_builder_factory
.builder()
.with_log(self.log.clone())
.with_underlay_vnic_allocator(&self.vnic_allocator)
.with_zone_root_path(&root)
.with_zone_root_path(root)
.with_zone_image_paths(&["/opt/oxide".into()])
.with_zone_type("propolis-server")
.with_unique_name(self.propolis_id().into_untyped_uuid())
Expand Down
13 changes: 7 additions & 6 deletions sled-agent/src/probe_manager.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ use omicron_common::api::external::{
VpcFirewallRuleStatus,
};
use omicron_common::api::internal::shared::NetworkInterface;
use rand::prelude::SliceRandom;
use rand::prelude::IteratorRandom;
use rand::SeedableRng;
use sled_storage::dataset::ZONE_DATASET;
use sled_storage::manager::StorageHandle;
Expand Down Expand Up @@ -226,14 +226,15 @@ impl ProbeManagerInner {
/// boots the probe zone.
async fn add_probe(self: &Arc<Self>, probe: &ProbeState) -> Result<()> {
let mut rng = rand::rngs::StdRng::from_entropy();
let root = self
let current_disks = self
.storage
.get_latest_disks()
.await
.all_u2_mountpoints(ZONE_DATASET)
.all_u2_mountpoints(ZONE_DATASET);
let zone_root_path = current_disks
.into_iter()
.choose(&mut rng)
.ok_or_else(|| anyhow!("u2 not found"))?
.clone();
.ok_or_else(|| anyhow!("u2 not found"))?;

let nic = probe
.interface
Expand Down Expand Up @@ -268,7 +269,7 @@ impl ProbeManagerInner {
.builder()
.with_log(self.log.clone())
.with_underlay_vnic_allocator(&self.vnic_allocator)
.with_zone_root_path(&root)
.with_zone_root_path(zone_root_path)
.with_zone_image_paths(&["/opt/oxide".into()])
.with_zone_type("probe")
.with_unique_name(probe.id)
Expand Down
Loading

0 comments on commit 4292158

Please sign in to comment.