Skip to content

Commit 4b5c6d3

Browse files
merge main into multicast-e2e
2 parents 8d4156a + 48dd241 commit 4b5c6d3

71 files changed

Lines changed: 1847 additions & 351 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

Cargo.lock

Lines changed: 3 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

clients/sled-agent-client/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -270,6 +270,7 @@ impl From<omicron_common::api::external::VpcFirewallRuleProtocol>
270270
Tcp => Self::Tcp,
271271
Udp => Self::Udp,
272272
Icmp(v) => Self::Icmp(v),
273+
Icmp6(v) => Self::Icmp6(v),
273274
}
274275
}
275276
}

common/src/api/external/mod.rs

Lines changed: 64 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -1877,16 +1877,35 @@ pub enum VpcFirewallRuleProtocol {
18771877
Tcp,
18781878
Udp,
18791879
Icmp(Option<VpcFirewallIcmpFilter>),
1880-
// TODO: IPv6 not supported by instances.
1881-
// Icmpv6(Option<VpcFirewallIcmpFilter>),
1880+
Icmp6(Option<VpcFirewallIcmpFilter>),
18821881
// TODO: OPTE does not yet permit further L4 protocols. (opte#609)
18831882
// Other(u16),
18841883
}
18851884

1886-
impl FromStr for VpcFirewallRuleProtocol {
1887-
type Err = Error;
1885+
impl VpcFirewallRuleProtocol {
1886+
/// Returns a string representation of this protocol filter suitable for
1887+
/// use as an API string or in the database.
1888+
///
1889+
/// This is the inverse of `from_api_string`.
1890+
pub fn to_api_string(&self) -> String {
1891+
match self {
1892+
VpcFirewallRuleProtocol::Tcp => "tcp".to_string(),
1893+
VpcFirewallRuleProtocol::Udp => "udp".to_string(),
1894+
VpcFirewallRuleProtocol::Icmp(None) => "icmp".to_string(),
1895+
VpcFirewallRuleProtocol::Icmp(Some(v)) => {
1896+
format!("icmp:{}", v.to_api_string())
1897+
}
1898+
VpcFirewallRuleProtocol::Icmp6(None) => "icmp6".to_string(),
1899+
VpcFirewallRuleProtocol::Icmp6(Some(v)) => {
1900+
format!("icmp6:{}", v.to_api_string())
1901+
}
1902+
}
1903+
}
18881904

1889-
fn from_str(proto: &str) -> Result<Self, Self::Err> {
1905+
/// Parses a protocol filter from the API string format.
1906+
///
1907+
/// This is the inverse of `to_api_string`.
1908+
pub fn from_api_string(proto: &str) -> Result<Self, Error> {
18901909
let (ty_str, content_str) = match proto.split_once(':') {
18911910
None => (proto, None),
18921911
Some((lhs, rhs)) => (lhs, Some(rhs)),
@@ -1898,9 +1917,15 @@ impl FromStr for VpcFirewallRuleProtocol {
18981917
(lhs, None) if lhs.eq_ignore_ascii_case("icmp") => {
18991918
Ok(Self::Icmp(None))
19001919
}
1901-
(lhs, Some(rhs)) if lhs.eq_ignore_ascii_case("icmp") => {
1902-
Ok(Self::Icmp(Some(rhs.parse()?)))
1920+
(lhs, Some(rhs)) if lhs.eq_ignore_ascii_case("icmp") => Ok(
1921+
Self::Icmp(Some(VpcFirewallIcmpFilter::from_api_string(rhs)?)),
1922+
),
1923+
(lhs, None) if lhs.eq_ignore_ascii_case("icmp6") => {
1924+
Ok(Self::Icmp6(None))
19031925
}
1926+
(lhs, Some(rhs)) if lhs.eq_ignore_ascii_case("icmp6") => Ok(
1927+
Self::Icmp6(Some(VpcFirewallIcmpFilter::from_api_string(rhs)?)),
1928+
),
19041929
(lhs, None) => Err(Error::invalid_value(
19051930
"vpc_firewall_rule_protocol",
19061931
format!("unrecognized protocol: {lhs}"),
@@ -1915,45 +1940,28 @@ impl FromStr for VpcFirewallRuleProtocol {
19151940
}
19161941
}
19171942

1918-
impl TryFrom<String> for VpcFirewallRuleProtocol {
1919-
type Error = <VpcFirewallRuleProtocol as FromStr>::Err;
1920-
1921-
fn try_from(proto: String) -> Result<Self, Self::Error> {
1922-
proto.parse()
1923-
}
1924-
}
1925-
1926-
impl Display for VpcFirewallRuleProtocol {
1927-
fn fmt(&self, f: &mut Formatter<'_>) -> FormatResult {
1928-
match self {
1929-
VpcFirewallRuleProtocol::Tcp => write!(f, "tcp"),
1930-
VpcFirewallRuleProtocol::Udp => write!(f, "udp"),
1931-
VpcFirewallRuleProtocol::Icmp(None) => write!(f, "icmp"),
1932-
VpcFirewallRuleProtocol::Icmp(Some(v)) => write!(f, "icmp:{v}"),
1933-
}
1934-
}
1935-
}
1936-
19371943
#[derive(Clone, Copy, Debug, PartialEq, Deserialize, Serialize, JsonSchema)]
19381944
pub struct VpcFirewallIcmpFilter {
19391945
pub icmp_type: u8,
19401946
pub code: Option<IcmpParamRange>,
19411947
}
19421948

1943-
impl Display for VpcFirewallIcmpFilter {
1944-
fn fmt(&self, f: &mut Formatter<'_>) -> FormatResult {
1945-
write!(f, "{}", self.icmp_type)?;
1946-
if let Some(code) = self.code {
1947-
write!(f, ",{code}")?;
1949+
impl VpcFirewallIcmpFilter {
1950+
/// Returns a string representation of this ICMP filter suitable for use
1951+
/// as part of an API string or in the database.
1952+
///
1953+
/// This is the inverse of `from_api_string`.
1954+
pub fn to_api_string(&self) -> String {
1955+
match self.code {
1956+
None => self.icmp_type.to_string(),
1957+
Some(code) => format!("{},{code}", self.icmp_type),
19481958
}
1949-
Ok(())
19501959
}
1951-
}
1952-
1953-
impl FromStr for VpcFirewallIcmpFilter {
1954-
type Err = Error;
19551960

1956-
fn from_str(filter: &str) -> Result<Self, Self::Err> {
1961+
/// Parses an ICMP filter from the API string format.
1962+
///
1963+
/// This is the inverse of `to_api_string`.
1964+
pub fn from_api_string(filter: &str) -> Result<Self, Error> {
19571965
let (ty_str, code_str) = match filter.split_once(',') {
19581966
None => (filter, None),
19591967
Some((lhs, rhs)) => (lhs, Some(rhs)),
@@ -3927,72 +3935,78 @@ mod test {
39273935
}
39283936

39293937
#[test]
3930-
fn test_firewall_rule_proto_filter_parse() {
3931-
assert_eq!(VpcFirewallRuleProtocol::Tcp, "tcp".parse().unwrap());
3932-
assert_eq!(VpcFirewallRuleProtocol::Udp, "udp".parse().unwrap());
3938+
fn test_firewall_rule_proto_filter_from_api_string() {
3939+
assert_eq!(
3940+
VpcFirewallRuleProtocol::Tcp,
3941+
VpcFirewallRuleProtocol::from_api_string("tcp").unwrap()
3942+
);
3943+
assert_eq!(
3944+
VpcFirewallRuleProtocol::Udp,
3945+
VpcFirewallRuleProtocol::from_api_string("udp").unwrap()
3946+
);
39333947

39343948
assert_eq!(
39353949
VpcFirewallRuleProtocol::Icmp(None),
3936-
"icmp".parse().unwrap()
3950+
VpcFirewallRuleProtocol::from_api_string("icmp").unwrap()
39373951
);
39383952
assert_eq!(
39393953
VpcFirewallRuleProtocol::Icmp(Some(VpcFirewallIcmpFilter {
39403954
icmp_type: 4,
39413955
code: None
39423956
})),
3943-
"icmp:4".parse().unwrap()
3957+
VpcFirewallRuleProtocol::from_api_string("icmp:4").unwrap()
39443958
);
39453959
assert_eq!(
39463960
VpcFirewallRuleProtocol::Icmp(Some(VpcFirewallIcmpFilter {
39473961
icmp_type: 60,
39483962
code: Some(0.into())
39493963
})),
3950-
"icmp:60,0".parse().unwrap()
3964+
VpcFirewallRuleProtocol::from_api_string("icmp:60,0").unwrap()
39513965
);
39523966
assert_eq!(
39533967
VpcFirewallRuleProtocol::Icmp(Some(VpcFirewallIcmpFilter {
39543968
icmp_type: 60,
39553969
code: Some((0..=10).try_into().unwrap())
39563970
})),
3957-
"icmp:60,0-10".parse().unwrap()
3971+
VpcFirewallRuleProtocol::from_api_string("icmp:60,0-10").unwrap()
39583972
);
39593973
assert_eq!(
3960-
"icmp:".parse::<VpcFirewallRuleProtocol>(),
3974+
VpcFirewallRuleProtocol::from_api_string("icmp:"),
39613975
Err(Error::invalid_value(
39623976
"icmp_type",
39633977
"\"\" unparsable for type: cannot parse integer from empty string"
39643978
))
39653979
);
39663980
assert_eq!(
3967-
"icmp:20-30".parse::<VpcFirewallRuleProtocol>(),
3981+
VpcFirewallRuleProtocol::from_api_string("icmp:20-30"),
39683982
Err(Error::invalid_value(
39693983
"icmp_type",
39703984
"\"20-30\" unparsable for type: invalid digit found in string"
39713985
))
39723986
);
39733987
assert_eq!(
3974-
"icmp:10,".parse::<VpcFirewallRuleProtocol>(),
3988+
VpcFirewallRuleProtocol::from_api_string("icmp:10,"),
39753989
Err(Error::invalid_value(
39763990
"code",
39773991
"\"\" unparsable for type: cannot parse integer from empty string"
39783992
))
39793993
);
39803994
assert_eq!(
3981-
"icmp:257,".parse::<VpcFirewallRuleProtocol>(),
3995+
VpcFirewallRuleProtocol::from_api_string("icmp:257,"),
39823996
Err(Error::invalid_value(
39833997
"icmp_type",
39843998
"\"257\" unparsable for type: number too large to fit in target type"
39853999
))
39864000
);
39874001
assert_eq!(
3988-
"icmp:0,1000-1001".parse::<VpcFirewallRuleProtocol>(),
4002+
VpcFirewallRuleProtocol::from_api_string("icmp:0,1000-1001"),
39894003
Err(Error::invalid_value(
39904004
"code",
39914005
"\"1000\" unparsable for type: number too large to fit in target type"
39924006
))
39934007
);
39944008
assert_eq!(
3995-
"icmp:0,30-".parse::<VpcFirewallRuleProtocol>(),
4009+
VpcFirewallRuleProtocol::from_api_string("icmp:0,30-"),
39964010
Err(Error::invalid_value("code", "range has no end value"))
39974011
);
39984012
}

dev-tools/omdb/src/bin/omdb/db.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,7 @@ use nexus_db_model::VolumeResourceUsage;
112112
use nexus_db_model::VpcSubnet;
113113
use nexus_db_model::Zpool;
114114
use nexus_db_model::to_db_typed_uuid;
115+
use nexus_db_queries::authz;
115116
use nexus_db_queries::context::OpContext;
116117
use nexus_db_queries::db;
117118
use nexus_db_queries::db::DataStore;
@@ -8188,8 +8189,9 @@ async fn cmd_db_trust_quorum_list_configs(
81888189
}
81898190

81908191
let limit = fetch_opts.fetch_limit;
8192+
let authz_tq = authz::TrustQuorumConfig::for_rack_id(args.rack_id);
81918193
let configs = datastore
8192-
.tq_list_config(opctx, args.rack_id, &first_page::<i64>(limit))
8194+
.tq_list_config(opctx, authz_tq, &first_page::<i64>(limit))
81938195
.await
81948196
.context("listing trust quorum configurations")?;
81958197

illumos-utils/src/opte/firewall_rules.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,14 @@ impl FromVpcFirewallRule for ResolvedVpcFirewallRule {
110110
}
111111
}))
112112
}
113+
VpcFirewallRuleProtocol::Icmp6(v) => {
114+
ProtoFilter::Icmpv6(v.map(|v| {
115+
oxide_vpc::api::IcmpFilter {
116+
ty: v.icmp_type,
117+
codes: v.code.map(Into::into),
118+
}
119+
}))
120+
}
113121
})
114122
.collect(),
115123
_ => vec![ProtoFilter::Any],

illumos-utils/src/zfs.rs

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1129,6 +1129,40 @@ pub struct DatasetVolumeDeleteArgs<'a> {
11291129
pub raw: bool,
11301130
}
11311131

1132+
/// Error returned by [`Zfs::remove_reservation`].
1133+
#[derive(thiserror::Error, Debug)]
1134+
#[error("Failed to remove reservation from '{name}': {err}")]
1135+
pub struct RemoveReservationError {
1136+
name: String,
1137+
#[source]
1138+
err: RemoveReservationErrorInner,
1139+
}
1140+
1141+
impl RemoveReservationError {
1142+
pub fn get_value(name: String, err: GetValueError) -> Self {
1143+
RemoveReservationError {
1144+
name,
1145+
err: RemoveReservationErrorInner::GetValue(err),
1146+
}
1147+
}
1148+
1149+
pub fn set_value(name: String, err: SetValueError) -> Self {
1150+
RemoveReservationError {
1151+
name,
1152+
err: RemoveReservationErrorInner::SetValue(err),
1153+
}
1154+
}
1155+
}
1156+
1157+
#[derive(thiserror::Error, Debug)]
1158+
pub enum RemoveReservationErrorInner {
1159+
#[error(transparent)]
1160+
GetValue(#[from] GetValueError),
1161+
1162+
#[error(transparent)]
1163+
SetValue(#[from] SetValueError),
1164+
}
1165+
11321166
impl Zfs {
11331167
/// Lists all datasets within a pool or existing dataset.
11341168
///
@@ -2128,6 +2162,23 @@ impl Zfs {
21282162
}
21292163
}
21302164
}
2165+
2166+
/// Remove a dataset's reservation, if set
2167+
pub async fn remove_reservation(
2168+
name: &str,
2169+
) -> Result<(), RemoveReservationError> {
2170+
let value = Zfs::get_value(name, "reservation").await.map_err(|e| {
2171+
RemoveReservationError::get_value(name.to_string(), e)
2172+
})?;
2173+
2174+
if value != "none" {
2175+
Zfs::set_value(name, "reservation", "none").await.map_err(|e| {
2176+
RemoveReservationError::set_value(name.to_string(), e)
2177+
})?;
2178+
}
2179+
2180+
Ok(())
2181+
}
21312182
}
21322183

21332184
/// A read-only snapshot of a ZFS filesystem.

0 commit comments

Comments
 (0)