Skip to content

Commit

Permalink
[network metrics] instance network interface schema
Browse files Browse the repository at this point in the history
This is related to the ongoing work in [plumbing instance/guest
metrics](oxidecomputer/propolis#742) through to an
oximeter producer in propolis.

Includes:
- Rewriting the comment for `trait Target` in Oximeter to not being prepped for deprecation,
  as it does come in handy for custom needs
- The main differentiating field among instance network information here is `interface_id`, which is
  exposed to propolis via the client's `NetworkInterfaceRequest`. We currently match the `nic.slot` to the `port.slot` to pass which of the
  requested nics `interface_id` uuids are part of the request.
  • Loading branch information
zeeshanlakhani committed Aug 25, 2024
1 parent 5afa0de commit 90dd028
Show file tree
Hide file tree
Showing 5 changed files with 108 additions and 15 deletions.
3 changes: 3 additions & 0 deletions illumos-utils/src/opte/illumos.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,9 @@ pub enum Error {

#[error("Can't attach new ephemeral IP {0}, currently have {1}")]
ImplicitEphemeralIpDetach(IpAddr, IpAddr),

#[error("No matching NIC found for port {0} at slot {1}.")]
NoNicforPort(String, u32),
}

/// Delete all xde devices on the system.
Expand Down
2 changes: 1 addition & 1 deletion oximeter/instruments/src/kstat/link.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ use oximeter::Sample;

oximeter::use_timeseries!("sled-data-link.toml");

// Helper function to extract the same kstat metrics from all link targets.
/// Helper function to extract the same kstat metrics from all link targets.
fn extract_link_kstats<T>(
target: &T,
named_data: &Named,
Expand Down
70 changes: 70 additions & 0 deletions oximeter/oximeter/schema/instance-network-interface.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
format_version = 1

[target]
name = "instance_network_interface"
description = "A network interface attached to a virtual machine instance"
authz_scope = "project"
versions = [
{ version = 1, fields = [ "interface_id", "instance_id", "project_id", "silo_id" ] },
]

[fields.interface_id]
type = "uuid"
description = "The ID of the network interface"

[fields.instance_id]
type = "uuid"
description = "The ID of the virtual machine instance this network interface is attached to"

[fields.project_id]
type = "uuid"
description = "The ID of the project containing the virtual machine instance this network interface is attached to"

[fields.silo_id]
type = "uuid"
description = "The ID of the silo containing the virtual machine instance this network interface is attached to"

[[metrics]]
name = "bytes_sent"
description = "Total number of bytes sent on the network interface"
units = "bytes"
datum_type = "cumulative_u64"
versions = [
{ added_in = 1, fields = [] }
]

[[metrics]]
name = "bytes_received"
description = "Total number of bytes received on the network interface"
units = "bytes"
datum_type = "cumulative_u64"
versions = [
{ added_in = 1, fields = [] }
]

[[metrics]]
name = "packets_sent"
description = "Total number of packets sent on the network interface"
units = "count"
datum_type = "cumulative_u64"
versions = [
{ added_in = 1, fields = [] }
]

[[metrics]]
name = "packets_received"
description = "Total number of packets received on the network interface"
units = "count"
datum_type = "cumulative_u64"
versions = [
{ added_in = 1, fields = [] }
]

[[metrics]]
name = "packets_dropped"
description = "Number of packets dropped on the RX queue of the network interface"
units = "count"
datum_type = "cumulative_u64"
versions = [
{ added_in = 1, fields = [] }
]
23 changes: 13 additions & 10 deletions oximeter/types/src/traits.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,14 @@ use std::num::NonZeroU8;
use std::ops::Add;
use std::ops::AddAssign;

/// The `Target` trait identifies a source of metric data by a sequence of fields.
///
/// A target is a single source of metric data, identified by a sequence of named and typed field
/// values. Users can write a single struct definition and derive this trait. The methods here
/// provide some introspection into the struct, listing its fields and their values. The struct
/// definition can be thought of as a schema, and an instance of that struct as identifying an
/// The `Target` trait identifies a source of metric data by a sequence of
/// fields.
///
/// A target is a single source of metric data, identified by a sequence of
/// named and typed field values. Users can write a single struct definition and
/// derive this trait. The methods here provide some introspection into the
/// struct, listing its fields and their values. The struct definition can be
/// thought of as a schema, and an instance of that struct as identifying an
/// individual target.
///
/// Target fields may have one of a set of supported types:
Expand Down Expand Up @@ -85,10 +87,11 @@ use std::ops::AddAssign;
/// }
/// ```
///
/// **Important:** Deriving this trait is deprecated, and will be removed in the
/// future. Instead, define your timeseries schema through the TOML format
/// described in [the crate documentation](crate), and use the code
/// generated by the `use_timeseries` macro.
/// **Important:** Typically, you should define your timeseries schema through
/// the TOML format described in [the crate documentation](crate), and use the
/// code generated by the `use_timeseries` macro. However, if you need to handle
/// custom sampling logic and must define custom field definitions not published
/// as part of the target schema, you can implement this trait.
pub trait Target {
/// Return the name of the target, which is the snake_case form of the struct's name.
fn name(&self) -> &'static str;
Expand Down
25 changes: 21 additions & 4 deletions sled-agent/src/instance.rs
Original file line number Diff line number Diff line change
Expand Up @@ -676,11 +676,28 @@ impl InstanceRunner {
) -> Result<(), Error> {
let nics = running_zone
.opte_ports()
.map(|port| propolis_client::types::NetworkInterfaceRequest {
name: port.name().to_string(),
slot: propolis_client::types::Slot(port.slot()),
.map(|port| {
self.requested_nics
.iter()
// We expect to match NIC slots to OPTE port slots.
// Error out if we can't find a NIC for a port.
.position(|nic| nic.slot == port.slot())
.ok_or(Error::Opte(
illumos_utils::opte::Error::NoNicforPort(
port.name().into(),
port.slot().into(),
),
))
.map(|pos| {
let nic = &self.requested_nics[pos];
propolis_client::types::NetworkInterfaceRequest {
interface_id: nic.id,
name: port.name().to_string(),
slot: propolis_client::types::Slot(port.slot()),
}
})
})
.collect();
.collect::<Result<Vec<_>, _>>()?;

let migrate = match migrate {
Some(params) => {
Expand Down

0 comments on commit 90dd028

Please sign in to comment.