diff --git a/common/Cargo.toml b/common/Cargo.toml index db0bdc58fcc..89193d111d7 100644 --- a/common/Cargo.toml +++ b/common/Cargo.toml @@ -14,6 +14,7 @@ futures = "0.3.18" http = "0.2.5" hyper = "0.14" ipnetwork = "0.18" +opteadm = { path = "../../opte/opteadm" } macaddr = { version = "1.0.1", features = [ "serde_std" ] } reqwest = { version = "0.11", default-features = false, features = ["rustls-tls"] } ring = "0.16" diff --git a/common/src/api/external/error.rs b/common/src/api/external/error.rs index 0de127bfcaa..011607c9203 100644 --- a/common/src/api/external/error.rs +++ b/common/src/api/external/error.rs @@ -266,6 +266,12 @@ impl From for crate::api::external::Error { } } +impl From for Error { + fn from(err: opteadm::Error) -> Self { + Error::InternalError { internal_message: format!("{}", err) } + } +} + /** * Like [`assert!`], except that instead of panicking, this function returns an * `Err(Error::InternalError)` with an appropriate message if the given diff --git a/common/src/api/external/mod.rs b/common/src/api/external/mod.rs index 03d4a9644d9..338125a65f0 100644 --- a/common/src/api/external/mod.rs +++ b/common/src/api/external/mod.rs @@ -1759,14 +1759,23 @@ impl JsonSchema for L4PortRange { /// hardware devices on a network. // NOTE: We're using the `macaddr` crate for the internal representation. But as with the `ipnet`, // this crate does not implement `JsonSchema`. -#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)] +#[derive(Clone, Copy, Debug, DeserializeFromStr, PartialEq, SerializeDisplay)] pub struct MacAddr(pub macaddr::MacAddr6); +impl FromStr for MacAddr { + type Err = macaddr::ParseError; + + fn from_str(s: &str) -> Result { + s.parse().map(|addr| MacAddr(addr)) + } +} + impl TryFrom for MacAddr { - type Error = macaddr::ParseError; + type Error = ::Err; fn try_from(s: String) -> Result { - s.parse().map(|addr| MacAddr(addr)) + // s.parse().map(|addr| MacAddr(addr)) + MacAddr::from_str(s.as_ref()) } } diff --git a/nexus/src/nexus.rs b/nexus/src/nexus.rs index 79d8d6479cf..98c60291275 100644 --- a/nexus/src/nexus.rs +++ b/nexus/src/nexus.rs @@ -19,6 +19,7 @@ use crate::saga_interface::SagaContext; use crate::sagas; use anyhow::Context; use async_trait::async_trait; +use chrono::Utc; use futures::future::ready; use futures::StreamExt; use hex; @@ -1095,6 +1096,24 @@ impl Nexus { let runtime: nexus::InstanceRuntimeState = instance.runtime().clone().into(); + let rpz_nic = sled_agent_client::types::NetworkInterface { + identity: sled_agent_client::types::IdentityMetadata { + id: Uuid::new_v4(), + name: sled_agent_client::types::Name("rpz-nic".to_string()), + description: "test nic".to_string(), + time_created: Utc::now(), + time_modified: Utc::now(), + }, + vpc_id: Uuid::new_v4(), + subnet_id: Uuid::new_v4(), + mac: sled_agent_client::types::MacAddr( + format!("02:08:20:BE:A0:F2") + ), + ip: std::net::IpAddr::V4( + std::net::Ipv4Addr::new(10, 0, 0, 213) + ).to_string(), + }; + // TODO: Populate this with an appropriate NIC. // See also: sic_create_instance_record in sagas.rs for a similar // construction. @@ -1102,7 +1121,7 @@ impl Nexus { runtime: sled_agent_client::types::InstanceRuntimeState::from( runtime, ), - nics: vec![], + nics: vec![rpz_nic], }; let new_runtime = sa diff --git a/nexus/src/sagas.rs b/nexus/src/sagas.rs index 6888cf82792..74682b1883f 100644 --- a/nexus/src/sagas.rs +++ b/nexus/src/sagas.rs @@ -18,8 +18,11 @@ use crate::external_api::params; use crate::saga_interface::SagaContext; use chrono::Utc; use lazy_static::lazy_static; +use omicron_common::api::external; use omicron_common::api::external::Generation; +use omicron_common::api::external::IdentityMetadata; use omicron_common::api::external::InstanceState; +use omicron_common::api::external::NetworkInterface; use omicron_common::api::internal::nexus::InstanceRuntimeState; use omicron_common::api::internal::sled_agent::InstanceHardware; use serde::Deserialize; @@ -166,11 +169,27 @@ async fn sic_create_instance_record( .await .map_err(ActionError::action_failed)?; + let rpz_nic = NetworkInterface { + identity: IdentityMetadata { + id: Uuid::new_v4(), + name: "rpz-nic".parse().unwrap(), + description: "test nic".to_string(), + time_created: Utc::now(), + time_modified: Utc::now(), + }, + vpc_id: Uuid::new_v4(), + subnet_id: Uuid::new_v4(), + mac: external::MacAddr( + macaddr::MacAddr6::from([0x02, 0x08, 0x20, 0xBE, 0xA0, 0xF2]) + ), + ip: std::net::IpAddr::V4(std::net::Ipv4Addr::new(10, 0, 0, 213)), + }; + // TODO: Populate this with an appropriate NIC. // See also: instance_set_runtime in nexus.rs for a similar construction. Ok(InstanceHardware { runtime: instance.runtime().clone().into(), - nics: vec![], + nics: vec![rpz_nic], }) } diff --git a/sled-agent/Cargo.toml b/sled-agent/Cargo.toml index e04303a689c..906383e320a 100644 --- a/sled-agent/Cargo.toml +++ b/sled-agent/Cargo.toml @@ -16,6 +16,8 @@ futures = "0.3.18" ipnetwork = "0.18" nexus-client = { path = "../nexus-client" } omicron-common = { path = "../common" } +opte-core = { path = "../../opte/opte-core" } +opteadm = { path = "../../opte/opteadm" } p256 = "0.9.0" percent-encoding = "2.1.0" progenitor = { git = "https://github.com/oxidecomputer/progenitor" } diff --git a/sled-agent/src/illumos/dladm.rs b/sled-agent/src/illumos/dladm.rs index 62db17600c9..f755e956aea 100644 --- a/sled-agent/src/illumos/dladm.rs +++ b/sled-agent/src/illumos/dladm.rs @@ -24,6 +24,11 @@ pub enum Error { #[error("Failed to parse output: {0}")] Parse(#[from] std::string::FromUtf8Error), + + // TODO: This isn't the right place to put this, but I just want + // to get integration going. + #[error("OPTE error: {0}")] + OpteErr(#[from] opteadm::Error), } /// The name of a physical datalink. diff --git a/sled-agent/src/instance.rs b/sled-agent/src/instance.rs index 26a54f67c76..ff3d70fa75d 100644 --- a/sled-agent/src/instance.rs +++ b/sled-agent/src/instance.rs @@ -406,6 +406,7 @@ impl Instance { &inner.nic_id_allocator, &physical_dl, Some(nic.mac), + nic.ip, inner.vlan, ) .map_err(|e| e.into()) diff --git a/sled-agent/src/vnic.rs b/sled-agent/src/vnic.rs index 764812ddb1a..1c2b6e4a364 100644 --- a/sled-agent/src/vnic.rs +++ b/sled-agent/src/vnic.rs @@ -9,6 +9,7 @@ use crate::illumos::dladm::{ PhysicalLink, VNIC_PREFIX_CONTROL, VNIC_PREFIX_GUEST, }; use omicron_common::api::external::MacAddr; +use std::net::IpAddr; use std::sync::{ atomic::{AtomicU64, Ordering}, Arc, @@ -74,10 +75,29 @@ impl Vnic { allocator: &IdAllocator, physical_dl: &PhysicalLink, mac: Option, + ip: IpAddr, vlan: Option, ) -> Result { let name = guest_vnic_name(allocator.next()); Dladm::create_vnic(physical_dl, &name, mac, vlan)?; + let ip4 = match ip { + IpAddr::V4(v) => v, + + _ => { + todo!("OPTE supports IPv4 guests only at the moment"); + } + }; + let ip_cfg = opte_core::ioctl::IpConfig { + // NOTE: OPTE has it's own Ipv4Addr, thus the into(). + private_ip: ip4.into(), + snat: None, + }; + let req = opte_core::ioctl::AddPortReq { + link_name: name.clone(), + ip_cfg, + }; + let hdl = opteadm::OpteAdm::open()?; + hdl.add_port(&req)?; Ok(Vnic { name, deleted: false }) } @@ -99,6 +119,8 @@ impl Vnic { Ok(()) } else { self.deleted = true; + let hdl = opteadm::OpteAdm::open()?; + hdl.delete_port(&self.name)?; Dladm::delete_vnic(&self.name) } }