Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
e5d5317
Adds basic types to represent VPCs, subnets, and NICs.
bnaecker Jul 14, 2021
8e3dc47
Splits IP v4 and v6 subnet types
bnaecker Jul 19, 2021
63b51d4
Use STRING in MAC addr DB type for uniformity, and add TODO about NIC…
bnaecker Jul 20, 2021
cef9ecc
Rename VNIC -> NetworkInterface
bnaecker Jul 21, 2021
7a4021b
Add serialization derives to NetworkInterface
bnaecker Jul 21, 2021
32e2c76
Split internal/external, though everything is external rn
smklein Jul 21, 2021
46e581b
Pull some disk stuff out of external
smklein Jul 21, 2021
26c187c
Split project
smklein Jul 21, 2021
0d941fb
Split up some instances
smklein Jul 21, 2021
37aedcc
Sagas, diskattachments, racks/sleds, bootstrap agent objects
smklein Jul 22, 2021
2e0a0df
Oximeter structs
smklein Jul 22, 2021
51f8624
fmt
smklein Jul 22, 2021
680e797
Merge branch 'main' into api-separation
smklein Jul 26, 2021
788e75b
Clippy
smklein Jul 26, 2021
22d12f8
put project_id on VPC model, remove TODO on subnet and NIC
david-crespo Jul 27, 2021
d898303
Merge branch 'main' into api-separation
smklein Jul 28, 2021
1c77776
Merge branch 'nexus/basic-networking-types' into api-separation
smklein Jul 28, 2021
07a1b7b
[api][instance] Relocate instance hardware info to InstanceRuntimeState
smklein Jul 20, 2021
ac9ebb0
Fix tests, leave view object unchanged
smklein Jul 21, 2021
7519b63
Merge
smklein Jul 28, 2021
573c8a7
Merge branch 'api-separation' into instance-hw
smklein Jul 28, 2021
6ed0a48
Start plumbing nics through sled agent -> Propolis
smklein Jul 29, 2021
0bab62b
Theoretically, guest nics should be allocated when requested
smklein Jul 30, 2021
54149b8
Macs maybe?
smklein Jul 30, 2021
5e67a83
Conjuring a NIC in Nexus, throwing it around
smklein Aug 4, 2021
2eca04c
pass tests pls thank u
smklein Aug 4, 2021
dafa19d
Merge branch 'main' into api-separation
smklein Aug 4, 2021
9cb2400
Merge branch 'api-separation' into instance-hw
smklein Aug 4, 2021
5a99282
Merge branch 'instance-hw' into pass-nics-and-use-em
smklein Aug 4, 2021
bd52794
Merge w/Diesel branch
smklein Sep 1, 2021
2aac0ad
Merge branch 'instance-hw' into pass-nics-and-use-em
smklein Sep 1, 2021
a254093
Merge branch 'main' into pass-nics-and-use-em
smklein Sep 1, 2021
0059667
Workaround for libpq
smklein Sep 1, 2021
bc54efb
Update readme
smklein Sep 1, 2021
7c5c848
Review feedback
smklein Sep 1, 2021
d881b90
rm feedback
smklein Sep 1, 2021
2174dd5
Merge branch 'pg-hack' into pass-nics-and-use-em
smklein Sep 1, 2021
0d95eed
Merge branch 'main' into pass-nics-and-use-em
smklein Sep 1, 2021
430f4db
Avoid actually sending fake NICs from Nexus
smklein Sep 1, 2021
e9c106f
Merge branch 'main' into pass-nics-and-use-em
smklein Sep 7, 2021
6bcc2ff
Plumb VLAN tagging through API
smklein Sep 7, 2021
772c4da
uprev propolis version
smklein Sep 7, 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
496 changes: 242 additions & 254 deletions Cargo.lock

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion omicron-common/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ futures = "0.3.15"
http = "0.2.0"
hyper = "0.14"
libc = "0.2.98"
propolis-server = { git = "https://github.com/oxidecomputer/propolis", rev = "b6da043d" }
propolis-server = { git = "https://github.com/oxidecomputer/propolis", rev = "bc0661e" }
postgres-protocol = "0.6.1"
rayon = "1.5"
reqwest = { version = "0.11", default-features = false, features = ["rustls-tls"] }
Expand Down
12 changes: 8 additions & 4 deletions omicron-common/src/api/internal/sled_agent.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
//! APIs exposed by Sled Agent.

use crate::api::internal;
use crate::api::{external, internal};
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use std::fmt::{Debug, Display, Formatter, Result as FormatResult};
Expand Down Expand Up @@ -39,15 +39,19 @@ impl DiskStateRequested {
}
}

/// Runtime state of an instance.
pub type InstanceRuntimeState = internal::nexus::InstanceRuntimeState;
/// Describes the instance hardware.
#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)]
pub struct InstanceHardware {
pub runtime: internal::nexus::InstanceRuntimeState,
pub nics: Vec<external::NetworkInterface>,
}

/// Sent to a sled agent to establish the runtime state of an Instance
#[derive(Serialize, Deserialize, JsonSchema)]
pub struct InstanceEnsureBody {
/// Last runtime state of the Instance known to Nexus (used if the agent
/// has never seen this Instance before).
pub initial_runtime: InstanceRuntimeState,
pub initial: InstanceHardware,
/// requested runtime state of the Instance
pub target: InstanceRuntimeStateRequested,
}
Expand Down
19 changes: 9 additions & 10 deletions omicron-common/src/sled_agent_client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,14 @@
* generated by the server.
*/

use crate::api;
use crate::api::external::Error;
use crate::api::internal::nexus::DiskRuntimeState;
use crate::api::internal::nexus::InstanceRuntimeState;
use crate::api::internal::sled_agent::DiskEnsureBody;
use crate::api::internal::sled_agent::DiskStateRequested;
use crate::api::internal::sled_agent::InstanceEnsureBody;
use crate::api::internal::sled_agent::InstanceHardware;
use crate::api::internal::sled_agent::InstanceRuntimeStateRequested;
use crate::http_client::HttpClient;
use async_trait::async_trait;
use http::Method;
Expand Down Expand Up @@ -53,24 +55,21 @@ impl Client {
pub async fn instance_ensure(
self: &Arc<Self>,
instance_id: Uuid,
initial_runtime: api::internal::sled_agent::InstanceRuntimeState,
target: api::internal::sled_agent::InstanceRuntimeStateRequested,
) -> Result<api::internal::sled_agent::InstanceRuntimeState, Error> {
initial: InstanceHardware,
target: InstanceRuntimeStateRequested,
) -> Result<InstanceRuntimeState, Error> {
let path = format!("/instances/{}", instance_id);
let body = Body::from(
serde_json::to_string(&InstanceEnsureBody {
initial_runtime,
target,
})
.unwrap(),
serde_json::to_string(&InstanceEnsureBody { initial, target })
.unwrap(),
);
let mut response =
self.client.request(Method::PUT, path.as_str(), body).await?;
/* TODO-robustness handle 300-level? */
assert!(response.status().is_success());
let value = self
.client
.read_json::<api::internal::sled_agent::InstanceRuntimeState>(
.read_json::<InstanceRuntimeState>(
&self.client.error_message_base(&Method::PUT, path.as_str()),
&mut response,
)
Expand Down
1 change: 1 addition & 0 deletions omicron-nexus/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ hyper = "0.14"
ipnetwork = "0.18"
lazy_static = "1.4.0"
libc = "0.2.98"
macaddr = { version = "1.0.1", features = [ "serde_std" ]}
newtype_derive = "0.1.6"
serde_json = "1.0"
serde_with = "1.9.4"
Expand Down
2 changes: 1 addition & 1 deletion omicron-nexus/src/db/model.rs
Original file line number Diff line number Diff line change
Expand Up @@ -382,7 +382,7 @@ impl From<internal::nexus::InstanceRuntimeState> for InstanceRuntimeState {
/// Conversion to the internal API type.
impl Into<internal::nexus::InstanceRuntimeState> for InstanceRuntimeState {
fn into(self) -> internal::nexus::InstanceRuntimeState {
internal::sled_agent::InstanceRuntimeState {
internal::nexus::InstanceRuntimeState {
run_state: *self.state.state(),
sled_uuid: self.sled_uuid,
ncpus: self.ncpus,
Expand Down
12 changes: 11 additions & 1 deletion omicron-nexus/src/nexus.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ use omicron_common::api::internal::nexus::DiskRuntimeState;
use omicron_common::api::internal::nexus::OximeterInfo;
use omicron_common::api::internal::nexus::ProducerEndpoint;
use omicron_common::api::internal::sled_agent::DiskStateRequested;
use omicron_common::api::internal::sled_agent::InstanceHardware;
use omicron_common::api::internal::sled_agent::InstanceRuntimeStateRequested;
use omicron_common::api::internal::sled_agent::InstanceStateRequested;
use omicron_common::bail_unless;
Expand Down Expand Up @@ -762,8 +763,17 @@ impl Nexus {
* not the newest one, that's fine. That might just mean the sled agent
* beat us to it.
*/

// TODO: Populate this with an appropriate NIC.
// See also: sic_create_instance_record in sagas.rs for a similar
// construction.
let instance_hardware = InstanceHardware {
runtime: instance.runtime().into(),
nics: vec![],
};

let new_runtime = sa
.instance_ensure(instance.id, instance.runtime().into(), requested)
.instance_ensure(instance.id, instance_hardware, requested)
.await?;

self.db_datastore
Expand Down
10 changes: 7 additions & 3 deletions omicron-nexus/src/sagas.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ use omicron_common::api::external::Generation;
use omicron_common::api::external::InstanceCreateParams;
use omicron_common::api::external::InstanceState;
use omicron_common::api::internal::nexus::InstanceRuntimeState;
use omicron_common::api::internal::sled_agent::InstanceHardware;
use omicron_common::api::internal::sled_agent::InstanceRuntimeStateRequested;
use omicron_common::api::internal::sled_agent::InstanceStateRequested;
use serde::Deserialize;
Expand Down Expand Up @@ -125,7 +126,7 @@ async fn sic_alloc_server(

async fn sic_create_instance_record(
sagactx: ActionContext<SagaInstanceCreate>,
) -> Result<InstanceRuntimeState, ActionError> {
) -> Result<InstanceHardware, ActionError> {
let osagactx = sagactx.user_data();
let params = sagactx.saga_params();
let sled_uuid = sagactx.lookup::<Uuid>("server_id");
Expand All @@ -151,7 +152,10 @@ async fn sic_create_instance_record(
)
.await
.map_err(ActionError::action_failed)?;
Ok(instance.runtime().into())

// TODO: Populate this with an appropriate NIC.
// See also: instance_set_runtime in nexus.rs for a similar construction.
Ok(InstanceHardware { runtime: instance.runtime().into(), nics: vec![] })
}

async fn sic_instance_ensure(
Expand All @@ -167,7 +171,7 @@ async fn sic_instance_ensure(
let instance_id = sagactx.lookup::<Uuid>("instance_id")?;
let sled_uuid = sagactx.lookup::<Uuid>("server_id")?;
let initial_runtime =
sagactx.lookup::<InstanceRuntimeState>("initial_runtime")?;
sagactx.lookup::<InstanceHardware>("initial_runtime")?;
let sa = osagactx
.sled_client(&sled_uuid)
.await
Expand Down
2 changes: 1 addition & 1 deletion omicron-sled-agent/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ http = "0.2.0"
hyper = "0.14"
ipnet = "2.3"
omicron-common = { path = "../omicron-common" }
propolis-client = { git = "https://github.com/oxidecomputer/propolis", rev = "3d58a6398" }
propolis-client = { git = "https://github.com/oxidecomputer/propolis", rev = "bc0661e" }
schemars = { version = "0.8", features = [ "chrono", "uuid" ] }
serde = { version = "1.0", features = [ "derive" ] }
serde_json = "1.0"
Expand Down
7 changes: 6 additions & 1 deletion omicron-sled-agent/src/bin/sled-agent.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ use dropshot::ConfigLogging;
use dropshot::ConfigLoggingLevel;
use omicron_common::cmd::fatal;
use omicron_common::cmd::CmdError;
use omicron_sled_agent::common::vlan::VlanID;
use omicron_sled_agent::config::Config;
use omicron_sled_agent::server::{run_openapi, run_server};
use std::net::SocketAddr;
Expand All @@ -29,6 +30,9 @@ enum Args {

#[structopt(name = "NEXUS_IP:PORT", parse(try_from_str))]
nexus_addr: SocketAddr,

#[structopt(long = "vlan")]
vlan: Option<VlanID>,
},
}

Expand All @@ -46,7 +50,7 @@ async fn do_run() -> Result<(), CmdError> {

match args {
Args::OpenApi => run_openapi().map_err(CmdError::Failure),
Args::Run { uuid, sled_agent_addr, nexus_addr } => {
Args::Run { uuid, sled_agent_addr, nexus_addr, vlan } => {
let config = Config {
id: uuid,
nexus_address: nexus_addr,
Expand All @@ -57,6 +61,7 @@ async fn do_run() -> Result<(), CmdError> {
log: ConfigLogging::StderrTerminal {
level: ConfigLoggingLevel::Info,
},
vlan,
};
run_server(&config).await.map_err(CmdError::Failure)
}
Expand Down
1 change: 1 addition & 0 deletions omicron-sled-agent/src/common/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@

pub mod disk;
pub mod instance;
pub mod vlan;
41 changes: 41 additions & 0 deletions omicron-sled-agent/src/common/vlan.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
//! VLAN ID wrapper.

use omicron_common::api::external::Error;
use std::fmt;
use std::str::FromStr;

/// The maximum VLAN value (inclusive), as specified by IEEE 802.1Q.
pub const VLAN_MAX: u16 = 4094;

/// Wrapper around a VLAN ID, ensuring it is valid.
#[derive(Debug, Clone, Copy)]
pub struct VlanID(u16);

impl VlanID {
/// Creates a new VLAN ID, returning an error if it is out of range.
pub fn new(id: u16) -> Result<Self, Error> {
if VLAN_MAX < id {
return Err(Error::InvalidValue {
label: id.to_string(),
message: "Invalid VLAN value".to_string(),
});
}
Ok(Self(id))
}
}

impl fmt::Display for VlanID {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.0.to_string())
}
}

impl FromStr for VlanID {
type Err = Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
Self::new(s.parse().map_err(|e| Error::InvalidValue {
label: s.to_string(),
message: format!("{}", e),
})?)
}
}
7 changes: 4 additions & 3 deletions omicron-sled-agent/src/config.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
//! Interfaces for working with sled agent configuration

use crate::common::vlan::VlanID;
use dropshot::ConfigDropshot;
use dropshot::ConfigLogging;
use serde::Deserialize;
use serde::Serialize;
use std::net::SocketAddr;
use uuid::Uuid;

/// Configuration for a sled agent
#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
#[derive(Clone, Debug)]
pub struct Config {
/// Unique id for the sled
pub id: Uuid,
Expand All @@ -18,4 +17,6 @@ pub struct Config {
pub dropshot: ConfigDropshot,
/// Configuration for the sled agent debug log
pub log: ConfigLogging,
/// Optional VLAN ID to be used for tagging guest VNICs.
pub vlan: Option<VlanID>,
}
8 changes: 2 additions & 6 deletions omicron-sled-agent/src/http_entrypoints.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,12 +54,8 @@ async fn instance_put(
let instance_id = path_params.into_inner().instance_id;
let body_args = body.into_inner();
Ok(HttpResponseOk(
sa.instance_ensure(
instance_id,
body_args.initial_runtime.clone(),
body_args.target.clone(),
)
.await?,
sa.instance_ensure(instance_id, body_args.initial, body_args.target)
.await?,
))
}

Expand Down
49 changes: 42 additions & 7 deletions omicron-sled-agent/src/illumos/dladm.rs
Original file line number Diff line number Diff line change
@@ -1,23 +1,28 @@
//! Utilities for poking at data links.

use crate::common::vlan::VlanID;
use crate::illumos::{execute, PFEXEC};
use omicron_common::api::external::Error;
use omicron_common::api::external::MacAddr;

pub const VNIC_PREFIX: &str = "vnic_propolis";
pub const DLADM: &str = "/usr/sbin/dladm";

const DLADM: &str = "/usr/sbin/dladm";
/// The name of a physical datalink.
#[derive(Debug, Clone)]
pub struct PhysicalLink(pub String);

/// Wraps commands for interacting with data links.
pub struct Dladm {}

#[cfg_attr(test, mockall::automock, allow(dead_code))]
impl Dladm {
/// Returns the name of the first observed physical data link.
pub fn find_physical() -> Result<String, Error> {
pub fn find_physical() -> Result<PhysicalLink, Error> {
let mut command = std::process::Command::new(PFEXEC);
let cmd = command.args(&[DLADM, "show-phys", "-p", "-o", "LINK"]);
let output = execute(cmd)?;
Ok(String::from_utf8(output.stdout)
let name = String::from_utf8(output.stdout)
.map_err(|e| Error::InternalError {
message: format!("Cannot parse dladm output as UTF-8: {}", e),
})?
Expand All @@ -30,14 +35,44 @@ impl Dladm {
.ok_or_else(|| Error::InternalError {
message: "No physical devices found".to_string(),
})?
.to_string())
.to_string();
Ok(PhysicalLink(name))
}

/// Creates a new VNIC atop a physical device.
pub fn create_vnic(physical: &str, vnic_name: &str) -> Result<(), Error> {
///
/// * `physical`: The physical link on top of which a device will be
/// created.
/// * `vnic_name`: Exact name of the VNIC to be created.
/// * `mac`: An optional unicast MAC address for the newly created NIC.
/// * `vlan`: An optional VLAN ID for VLAN tagging.
pub fn create_vnic(
physical: &PhysicalLink,
vnic_name: &str,
mac: Option<MacAddr>,
vlan: Option<VlanID>,
) -> Result<(), Error> {
let mut command = std::process::Command::new(PFEXEC);
let cmd =
command.args(&[DLADM, "create-vnic", "-l", physical, vnic_name]);
let mut args = vec![
DLADM.to_string(),
"create-vnic".to_string(),
"-t".to_string(),
"-l".to_string(),
physical.0.to_string(),
];

if let Some(mac) = mac {
args.push("-m".to_string());
args.push(mac.0.to_string());
}

if let Some(vlan) = vlan {
args.push("-v".to_string());
args.push(vlan.to_string());
}

args.push(vnic_name.to_string());
let cmd = command.args(&args);
execute(cmd)?;
Ok(())
}
Expand Down
Loading